Add a non-interactive mode to the themes kitten
This commit is contained in:
parent
e1ed9aca10
commit
d916ecc4f3
@ -47,3 +47,5 @@ go to `kitty-themes <https://github.com/kovidgoyal/kitty-themes>`_ and open a pu
|
|||||||
asking to add your contributions to the repository. Use the file
|
asking to add your contributions to the repository. Use the file
|
||||||
`template.conf <https://github.com/kovidgoyal/kitty-themes/raw/master/template.conf>`_ as
|
`template.conf <https://github.com/kovidgoyal/kitty-themes/raw/master/template.conf>`_ as
|
||||||
a template when creating your theme.
|
a template when creating your theme.
|
||||||
|
|
||||||
|
.. include:: ../generated/cli-kitten-themes.rst
|
||||||
|
|||||||
@ -75,11 +75,17 @@ def set_comment_in_zip_file(path: str, data: str) -> None:
|
|||||||
zf.comment = data.encode('utf-8')
|
zf.comment = data.encode('utf-8')
|
||||||
|
|
||||||
|
|
||||||
|
class NoCacheFound(ValueError):
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def fetch_themes(
|
def fetch_themes(
|
||||||
name: str = 'kitty-themes',
|
name: str = 'kitty-themes',
|
||||||
url: str = 'https://codeload.github.com/kovidgoyal/kitty-themes/zip/master'
|
url: str = 'https://codeload.github.com/kovidgoyal/kitty-themes/zip/master',
|
||||||
|
cache_age: float = 1,
|
||||||
) -> str:
|
) -> str:
|
||||||
now = datetime.datetime.now(datetime.timezone.utc)
|
now = datetime.datetime.now(datetime.timezone.utc)
|
||||||
|
cache_age_delta = datetime.timedelta(days=cache_age)
|
||||||
|
|
||||||
class Metadata:
|
class Metadata:
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
@ -95,8 +101,11 @@ def fetch_themes(
|
|||||||
q = json.loads(zf.comment)
|
q = json.loads(zf.comment)
|
||||||
m.etag = str(q.get('etag') or '')
|
m.etag = str(q.get('etag') or '')
|
||||||
m.timestamp = datetime.datetime.fromisoformat(q['timestamp'])
|
m.timestamp = datetime.datetime.fromisoformat(q['timestamp'])
|
||||||
if (now - m.timestamp).days < 1:
|
if cache_age < 0 or (now - m.timestamp) < cache_age_delta:
|
||||||
return dest_path
|
return dest_path
|
||||||
|
if cache_age < 0:
|
||||||
|
raise NoCacheFound('No local themes cache found and negative cache age specified, aborting')
|
||||||
|
|
||||||
rq = Request(url)
|
rq = Request(url)
|
||||||
m.timestamp = now
|
m.timestamp = now
|
||||||
if m.etag:
|
if m.etag:
|
||||||
@ -348,9 +357,9 @@ class Themes:
|
|||||||
self.index_map = tuple(self.themes)
|
self.index_map = tuple(self.themes)
|
||||||
|
|
||||||
|
|
||||||
def load_themes() -> Themes:
|
def load_themes(cache_age: float = 1.) -> Themes:
|
||||||
ans = Themes()
|
ans = Themes()
|
||||||
ans.load_from_zip(fetch_themes())
|
ans.load_from_zip(fetch_themes(cache_age=cache_age))
|
||||||
ans.load_from_dir(os.path.join(config_dir, 'themes'))
|
ans.load_from_dir(os.path.join(config_dir, 'themes'))
|
||||||
ans.index_map = tuple(ans.themes)
|
ans.index_map = tuple(ans.themes)
|
||||||
return ans
|
return ans
|
||||||
|
|||||||
@ -7,11 +7,13 @@ import re
|
|||||||
import sys
|
import sys
|
||||||
import traceback
|
import traceback
|
||||||
from enum import Enum, auto
|
from enum import Enum, auto
|
||||||
|
from gettext import gettext as _
|
||||||
from typing import (
|
from typing import (
|
||||||
Any, Callable, Dict, Iterable, Iterator, List, Optional, Tuple, Union
|
Any, Callable, Dict, Iterable, Iterator, List, Optional, Tuple, Union
|
||||||
)
|
)
|
||||||
|
|
||||||
from kitty.cli import create_default_opts
|
from kitty.cli import create_default_opts, parse_args
|
||||||
|
from kitty.cli_stub import ThemesCLIOptions
|
||||||
from kitty.config import cached_values_for
|
from kitty.config import cached_values_for
|
||||||
from kitty.constants import config_dir
|
from kitty.constants import config_dir
|
||||||
from kitty.fast_data_types import truncate_point_for_length, wcswidth
|
from kitty.fast_data_types import truncate_point_for_length, wcswidth
|
||||||
@ -23,7 +25,7 @@ from ..tui.handler import Handler
|
|||||||
from ..tui.line_edit import LineEdit
|
from ..tui.line_edit import LineEdit
|
||||||
from ..tui.loop import Loop
|
from ..tui.loop import Loop
|
||||||
from ..tui.operations import color_code, styled
|
from ..tui.operations import color_code, styled
|
||||||
from .collection import MARK_AFTER, Theme, Themes, load_themes
|
from .collection import MARK_AFTER, NoCacheFound, Theme, Themes, load_themes
|
||||||
|
|
||||||
separator = '║'
|
separator = '║'
|
||||||
|
|
||||||
@ -133,8 +135,9 @@ class ThemesList:
|
|||||||
|
|
||||||
class ThemesHandler(Handler):
|
class ThemesHandler(Handler):
|
||||||
|
|
||||||
def __init__(self, cached_values: Dict[str, Any]) -> None:
|
def __init__(self, cached_values: Dict[str, Any], cli_opts: ThemesCLIOptions) -> None:
|
||||||
self.cached_values = cached_values
|
self.cached_values = cached_values
|
||||||
|
self.cli_opts = cli_opts
|
||||||
self.state = State.fetching
|
self.state = State.fetching
|
||||||
self.report_traceback_on_exit: Optional[str] = None
|
self.report_traceback_on_exit: Optional[str] = None
|
||||||
self.filter_map: Dict[str, Callable[[Theme], bool]] = {
|
self.filter_map: Dict[str, Callable[[Theme], bool]] = {
|
||||||
@ -221,7 +224,7 @@ class ThemesHandler(Handler):
|
|||||||
|
|
||||||
def fetch() -> None:
|
def fetch() -> None:
|
||||||
try:
|
try:
|
||||||
themes: Union[Themes, str] = load_themes()
|
themes: Union[Themes, str] = load_themes(self.cli_opts.cache_age)
|
||||||
except Exception:
|
except Exception:
|
||||||
themes = format_traceback('Failed to download themes')
|
themes = format_traceback('Failed to download themes')
|
||||||
self.asyncio_loop.call_soon_threadsafe(fetching_done, themes)
|
self.asyncio_loop.call_soon_threadsafe(fetching_done, themes)
|
||||||
@ -502,10 +505,56 @@ class ThemesHandler(Handler):
|
|||||||
self.quit_loop(1)
|
self.quit_loop(1)
|
||||||
|
|
||||||
|
|
||||||
|
help_text = (
|
||||||
|
'Change the kitty theme. If no theme name is supplied, run interactively, otherwise'
|
||||||
|
' change the current theme to the specified theme name.'
|
||||||
|
)
|
||||||
|
usage = '[theme name to switch to]'
|
||||||
|
OPTIONS = '''
|
||||||
|
--cache-age
|
||||||
|
type=float
|
||||||
|
default=1
|
||||||
|
Check for new themes only after the specified number of days. A value of
|
||||||
|
zero will always check for new themes. A negative value will never check
|
||||||
|
for new themes, instead raising an error if a local copy of the themes
|
||||||
|
is not available.
|
||||||
|
|
||||||
|
|
||||||
|
'''.format
|
||||||
|
|
||||||
|
|
||||||
|
def parse_themes_args(args: List[str]) -> Tuple[ThemesCLIOptions, List[str]]:
|
||||||
|
return parse_args(args, OPTIONS, usage, help_text, 'kitty +kitten themes', result_class=ThemesCLIOptions)
|
||||||
|
|
||||||
|
|
||||||
|
def non_interactive(cli_opts: ThemesCLIOptions, theme_name: str) -> None:
|
||||||
|
try:
|
||||||
|
themes = load_themes(cli_opts.cache_age)
|
||||||
|
except NoCacheFound as e:
|
||||||
|
raise SystemExit(str(e))
|
||||||
|
try:
|
||||||
|
theme = themes[theme_name]
|
||||||
|
except KeyError:
|
||||||
|
raise SystemExit(f'No theme named: {theme_name}')
|
||||||
|
theme.save_in_conf(config_dir)
|
||||||
|
|
||||||
|
|
||||||
def main(args: List[str]) -> None:
|
def main(args: List[str]) -> None:
|
||||||
|
try:
|
||||||
|
cli_opts, items = parse_themes_args(args[1:])
|
||||||
|
except SystemExit as e:
|
||||||
|
if e.code != 0:
|
||||||
|
print(e.args[0], file=sys.stderr)
|
||||||
|
input(_('Press Enter to quit'))
|
||||||
|
return None
|
||||||
|
if len(items) > 1:
|
||||||
|
raise SystemExit('At most one theme name must be specified')
|
||||||
|
if len(items) == 1:
|
||||||
|
return non_interactive(cli_opts, items[0])
|
||||||
|
|
||||||
loop = Loop()
|
loop = Loop()
|
||||||
with cached_values_for('themes-kitten') as cached_values:
|
with cached_values_for('themes-kitten') as cached_values:
|
||||||
handler = ThemesHandler(cached_values)
|
handler = ThemesHandler(cached_values, cli_opts)
|
||||||
loop.loop(handler)
|
loop.loop(handler)
|
||||||
if loop.return_code != 0:
|
if loop.return_code != 0:
|
||||||
if handler.report_traceback_on_exit:
|
if handler.report_traceback_on_exit:
|
||||||
@ -521,3 +570,8 @@ def main(args: List[str]) -> None:
|
|||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main(sys.argv)
|
main(sys.argv)
|
||||||
|
elif __name__ == '__doc__':
|
||||||
|
cd = sys.cli_docs # type: ignore
|
||||||
|
cd['usage'] = usage
|
||||||
|
cd['options'] = OPTIONS
|
||||||
|
cd['help_text'] = help_text
|
||||||
|
|||||||
@ -14,6 +14,7 @@ LaunchCLIOptions = AskCLIOptions = ClipboardCLIOptions = DiffCLIOptions = CLIOpt
|
|||||||
HintsCLIOptions = IcatCLIOptions = PanelCLIOptions = ResizeCLIOptions = CLIOptions
|
HintsCLIOptions = IcatCLIOptions = PanelCLIOptions = ResizeCLIOptions = CLIOptions
|
||||||
ErrorCLIOptions = UnicodeCLIOptions = RCOptions = RemoteFileCLIOptions = CLIOptions
|
ErrorCLIOptions = UnicodeCLIOptions = RCOptions = RemoteFileCLIOptions = CLIOptions
|
||||||
QueryTerminalCLIOptions = BroadcastCLIOptions = ShowKeyCLIOptions = CLIOptions
|
QueryTerminalCLIOptions = BroadcastCLIOptions = ShowKeyCLIOptions = CLIOptions
|
||||||
|
ThemesCLIOptions = CLIOptions
|
||||||
|
|
||||||
|
|
||||||
def generate_stub() -> None:
|
def generate_stub() -> None:
|
||||||
@ -72,6 +73,9 @@ def generate_stub() -> None:
|
|||||||
from kittens.unicode_input.main import OPTIONS
|
from kittens.unicode_input.main import OPTIONS
|
||||||
do(OPTIONS(), 'UnicodeCLIOptions')
|
do(OPTIONS(), 'UnicodeCLIOptions')
|
||||||
|
|
||||||
|
from kittens.themes.main import OPTIONS
|
||||||
|
do(OPTIONS(), 'ThemesCLIOptions')
|
||||||
|
|
||||||
from kitty.rc.base import all_command_names, command_for_name
|
from kitty.rc.base import all_command_names, command_for_name
|
||||||
for cmd_name in all_command_names():
|
for cmd_name in all_command_names():
|
||||||
cmd = command_for_name(cmd_name)
|
cmd = command_for_name(cmd_name)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user