Add type checking for the various CLI options objects
This commit is contained in:
@@ -7,6 +7,7 @@ from contextlib import suppress
|
|||||||
|
|
||||||
from kitty.cli import parse_args
|
from kitty.cli import parse_args
|
||||||
from kitty.constants import cache_dir
|
from kitty.constants import cache_dir
|
||||||
|
from kitty.cli_stub import AskCLIOptions
|
||||||
|
|
||||||
from ..tui.operations import alternate_screen, styled
|
from ..tui.operations import alternate_screen, styled
|
||||||
from ..tui.handler import result_handler
|
from ..tui.handler import result_handler
|
||||||
@@ -89,7 +90,7 @@ def main(args):
|
|||||||
from kitty.shell import init_readline
|
from kitty.shell import init_readline
|
||||||
msg = 'Ask the user for input'
|
msg = 'Ask the user for input'
|
||||||
try:
|
try:
|
||||||
args, items = parse_args(args[1:], option_text, '', msg, 'kitty ask')
|
args, items = parse_args(args[1:], option_text, '', msg, 'kitty ask', result_class=AskCLIOptions)
|
||||||
except SystemExit as e:
|
except SystemExit as e:
|
||||||
if e.code != 0:
|
if e.code != 0:
|
||||||
print(e.args[0])
|
print(e.args[0])
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
from kitty.cli import parse_args
|
from kitty.cli import parse_args
|
||||||
|
from kitty.cli_stub import ClipboardCLIOptions
|
||||||
|
|
||||||
from ..tui.handler import Handler
|
from ..tui.handler import Handler
|
||||||
from ..tui.loop import Loop
|
from ..tui.loop import Loop
|
||||||
@@ -81,7 +82,7 @@ usage = ''
|
|||||||
|
|
||||||
|
|
||||||
def main(args):
|
def main(args):
|
||||||
args, items = parse_args(args[1:], OPTIONS, usage, help_text, 'kitty +kitten clipboard')
|
args, items = parse_args(args[1:], OPTIONS, usage, help_text, 'kitty +kitten clipboard', result_class=ClipboardCLIOptions)
|
||||||
if items:
|
if items:
|
||||||
raise SystemExit('Unrecognized extra command line arguments')
|
raise SystemExit('Unrecognized extra command line arguments')
|
||||||
data = None
|
data = None
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ from kitty.conf.definition import config_lines
|
|||||||
from kitty.constants import config_dir
|
from kitty.constants import config_dir
|
||||||
from kitty.rgb import color_as_sgr
|
from kitty.rgb import color_as_sgr
|
||||||
|
|
||||||
from .config_data import type_map, all_options
|
from .config_data import type_convert, all_options
|
||||||
|
|
||||||
defaults = None
|
defaults = None
|
||||||
|
|
||||||
@@ -89,7 +89,7 @@ def parse_config(lines, check_keys=True):
|
|||||||
parse_config_base(
|
parse_config_base(
|
||||||
lines,
|
lines,
|
||||||
defaults,
|
defaults,
|
||||||
type_map,
|
type_convert,
|
||||||
special_handling,
|
special_handling,
|
||||||
ans,
|
ans,
|
||||||
check_keys=check_keys
|
check_keys=check_keys
|
||||||
|
|||||||
@@ -3,10 +3,10 @@
|
|||||||
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPL v3 Copyright: 2018, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
|
||||||
from functools import partial
|
|
||||||
# Utils {{{
|
# Utils {{{
|
||||||
|
from functools import partial
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
from typing import Dict, Union
|
from typing import Any, Dict, Union
|
||||||
|
|
||||||
from kitty.conf.definition import Option, Shortcut, option_func
|
from kitty.conf.definition import Option, Shortcut, option_func
|
||||||
from kitty.conf.utils import positive_int, python_string, to_color
|
from kitty.conf.utils import positive_int, python_string, to_color
|
||||||
@@ -120,4 +120,9 @@ k('prev_match', '<', 'scroll_to prev-match', _('Scroll to previous search match'
|
|||||||
k('search_forward_simple', 'f', 'start_search substring forward', _('Search forward (no regex)'))
|
k('search_forward_simple', 'f', 'start_search substring forward', _('Search forward (no regex)'))
|
||||||
k('search_backward_simple', 'b', 'start_search substring backward', _('Search backward (no regex)'))
|
k('search_backward_simple', 'b', 'start_search substring backward', _('Search backward (no regex)'))
|
||||||
|
|
||||||
type_map = {o.name: o.option_type for o in all_options.values() if isinstance(o, Option)}
|
|
||||||
|
def type_convert(name: str, val: Any) -> Any:
|
||||||
|
o = all_options.get(name)
|
||||||
|
if isinstance(o, Option):
|
||||||
|
val = o.option_type(val)
|
||||||
|
return val
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ from functools import partial
|
|||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
|
|
||||||
from kitty.cli import CONFIG_HELP, parse_args
|
from kitty.cli import CONFIG_HELP, parse_args
|
||||||
|
from kitty.cli_stub import DiffCLIOptions
|
||||||
from kitty.constants import appname
|
from kitty.constants import appname
|
||||||
from kitty.fast_data_types import wcswidth
|
from kitty.fast_data_types import wcswidth
|
||||||
from kitty.key_encoding import RELEASE, enter_key, key_defs as K
|
from kitty.key_encoding import RELEASE, enter_key, key_defs as K
|
||||||
@@ -542,7 +543,7 @@ def get_remote_file(path):
|
|||||||
|
|
||||||
def main(args):
|
def main(args):
|
||||||
warnings.showwarning = showwarning
|
warnings.showwarning = showwarning
|
||||||
args, items = parse_args(args[1:], OPTIONS, usage, help_text, 'kitty +kitten diff')
|
args, items = parse_args(args[1:], OPTIONS, usage, help_text, 'kitty +kitten diff', result_class=DiffCLIOptions)
|
||||||
if len(items) != 2:
|
if len(items) != 2:
|
||||||
raise SystemExit('You must specify exactly two files/directories to compare')
|
raise SystemExit('You must specify exactly two files/directories to compare')
|
||||||
left, right = items
|
left, right = items
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ from gettext import gettext as _
|
|||||||
from itertools import repeat
|
from itertools import repeat
|
||||||
|
|
||||||
from kitty.cli import parse_args
|
from kitty.cli import parse_args
|
||||||
|
from kitty.cli_stub import HintsCLIOptions
|
||||||
from kitty.fast_data_types import set_clipboard_string
|
from kitty.fast_data_types import set_clipboard_string
|
||||||
from kitty.key_encoding import key_defs as K, backspace_key, enter_key
|
from kitty.key_encoding import key_defs as K, backspace_key, enter_key
|
||||||
from kitty.utils import screen_size_function
|
from kitty.utils import screen_size_function
|
||||||
@@ -508,7 +509,7 @@ usage = ''
|
|||||||
|
|
||||||
|
|
||||||
def parse_hints_args(args):
|
def parse_hints_args(args):
|
||||||
return parse_args(args, OPTIONS, usage, help_text, 'kitty +kitten hints')
|
return parse_args(args, OPTIONS, usage, help_text, 'kitty +kitten hints', result_class=HintsCLIOptions)
|
||||||
|
|
||||||
|
|
||||||
def main(args):
|
def main(args):
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ from math import ceil
|
|||||||
from tempfile import NamedTemporaryFile
|
from tempfile import NamedTemporaryFile
|
||||||
|
|
||||||
from kitty.cli import parse_args
|
from kitty.cli import parse_args
|
||||||
|
from kitty.cli_stub import IcatCLIOptions
|
||||||
from kitty.constants import appname
|
from kitty.constants import appname
|
||||||
from kitty.utils import TTYIO, fit_image, screen_size_function
|
from kitty.utils import TTYIO, fit_image, screen_size_function
|
||||||
|
|
||||||
@@ -343,7 +344,7 @@ def process_single_item(item, args, url_pat=None, maybe_dir=True):
|
|||||||
|
|
||||||
|
|
||||||
def main(args=sys.argv):
|
def main(args=sys.argv):
|
||||||
args, items = parse_args(args[1:], options_spec, usage, help_text, '{} +kitten icat'.format(appname))
|
args, items = parse_args(args[1:], options_spec, usage, help_text, '{} +kitten icat'.format(appname), result_class=IcatCLIOptions)
|
||||||
|
|
||||||
if args.print_window_size:
|
if args.print_window_size:
|
||||||
screen_size_function.ans = None
|
screen_size_function.ans = None
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import subprocess
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
from kitty.cli import parse_args
|
from kitty.cli import parse_args
|
||||||
|
from kitty.cli_stub import PanelCLIOptions
|
||||||
from kitty.constants import is_macos
|
from kitty.constants import is_macos
|
||||||
|
|
||||||
OPTIONS = r'''
|
OPTIONS = r'''
|
||||||
@@ -48,7 +49,7 @@ usage = 'program-to-run'
|
|||||||
|
|
||||||
|
|
||||||
def parse_panel_args(args):
|
def parse_panel_args(args):
|
||||||
return parse_args(args, OPTIONS, usage, help_text, 'kitty +kitten panel')
|
return parse_args(args, OPTIONS, usage, help_text, 'kitty +kitten panel', result_class=PanelCLIOptions)
|
||||||
|
|
||||||
|
|
||||||
def call_xprop(*cmd, silent=False):
|
def call_xprop(*cmd, silent=False):
|
||||||
|
|||||||
@@ -6,6 +6,7 @@
|
|||||||
import sys
|
import sys
|
||||||
|
|
||||||
from kitty.cli import parse_args
|
from kitty.cli import parse_args
|
||||||
|
from kitty.cli_stub import ResizeCLIOptions
|
||||||
from kitty.cmds import cmap, parse_subcommand_cli
|
from kitty.cmds import cmap, parse_subcommand_cli
|
||||||
from kitty.constants import version
|
from kitty.constants import version
|
||||||
from kitty.key_encoding import CTRL, RELEASE, key_defs as K
|
from kitty.key_encoding import CTRL, RELEASE, key_defs as K
|
||||||
@@ -119,7 +120,7 @@ The base vertical increment.
|
|||||||
def main(args):
|
def main(args):
|
||||||
msg = 'Resize the current window'
|
msg = 'Resize the current window'
|
||||||
try:
|
try:
|
||||||
args, items = parse_args(args[1:], OPTIONS, '', msg, 'resize_window')
|
args, items = parse_args(args[1:], OPTIONS, '', msg, 'resize_window', result_class=ResizeCLIOptions)
|
||||||
except SystemExit as e:
|
except SystemExit as e:
|
||||||
if e.code != 0:
|
if e.code != 0:
|
||||||
print(e.args[0], file=sys.stderr)
|
print(e.args[0], file=sys.stderr)
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import sys
|
|||||||
from contextlib import suppress
|
from contextlib import suppress
|
||||||
|
|
||||||
from kitty.cli import parse_args
|
from kitty.cli import parse_args
|
||||||
|
from kitty.cli_stub import ErrorCLIOptions
|
||||||
|
|
||||||
from ..tui.operations import styled
|
from ..tui.operations import styled
|
||||||
|
|
||||||
@@ -19,7 +20,7 @@ The title for the error message.
|
|||||||
|
|
||||||
def real_main(args):
|
def real_main(args):
|
||||||
msg = 'Show an error message'
|
msg = 'Show an error message'
|
||||||
args, items = parse_args(args[1:], OPTIONS, '', msg, 'hints')
|
args, items = parse_args(args[1:], OPTIONS, '', msg, 'hints', result_class=ErrorCLIOptions)
|
||||||
error_message = sys.stdin.buffer.read().decode('utf-8')
|
error_message = sys.stdin.buffer.read().decode('utf-8')
|
||||||
sys.stdin = open(os.ctermid())
|
sys.stdin = open(os.ctermid())
|
||||||
print(styled(args.title, fg_intense=True, fg='red', bold=True))
|
print(styled(args.title, fg_intense=True, fg='red', bold=True))
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ from functools import lru_cache
|
|||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
|
|
||||||
from kitty.cli import parse_args
|
from kitty.cli import parse_args
|
||||||
|
from kitty.cli_stub import UnicodeCLIOptions
|
||||||
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 is_emoji_presentation_base, wcswidth
|
from kitty.fast_data_types import is_emoji_presentation_base, wcswidth
|
||||||
@@ -530,7 +531,7 @@ default form specified in the unicode standard for the symbol is used.
|
|||||||
|
|
||||||
|
|
||||||
def parse_unicode_input_args(args):
|
def parse_unicode_input_args(args):
|
||||||
return parse_args(args, OPTIONS, usage, help_text, 'kitty +kitten unicode_input')
|
return parse_args(args, OPTIONS, usage, help_text, 'kitty +kitten unicode_input', result_class=UnicodeCLIOptions)
|
||||||
|
|
||||||
|
|
||||||
def main(args):
|
def main(args):
|
||||||
|
|||||||
@@ -304,8 +304,9 @@ class Boss:
|
|||||||
else:
|
else:
|
||||||
msg = json.loads(msg)
|
msg = json.loads(msg)
|
||||||
if isinstance(msg, dict) and msg.get('cmd') == 'new_instance':
|
if isinstance(msg, dict) and msg.get('cmd') == 'new_instance':
|
||||||
|
from .cli_stub import CLIOptions
|
||||||
startup_id = msg.get('startup_id')
|
startup_id = msg.get('startup_id')
|
||||||
args, rest = parse_args(msg['args'][1:])
|
args, rest = parse_args(msg['args'][1:], result_class=CLIOptions)
|
||||||
args.args = rest
|
args.args = rest
|
||||||
opts = create_opts(args)
|
opts = create_opts(args)
|
||||||
if not os.path.isabs(args.directory):
|
if not os.path.isabs(args.directory):
|
||||||
|
|||||||
235
kitty/cli.py
235
kitty/cli.py
@@ -6,9 +6,30 @@ import os
|
|||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
from collections import deque
|
from collections import deque
|
||||||
|
from typing import (
|
||||||
|
Any, Callable, Dict, FrozenSet, Iterator, List, Optional, Tuple, Type,
|
||||||
|
TypeVar, Union, cast
|
||||||
|
)
|
||||||
|
|
||||||
from .conf.utils import resolve_config
|
from .conf.utils import resolve_config
|
||||||
from .constants import appname, defconf, is_macos, is_wayland, str_version
|
from .constants import appname, defconf, is_macos, is_wayland, str_version
|
||||||
|
from .cli_stub import CLIOptions
|
||||||
|
from .options_stub import Options as OptionsStub
|
||||||
|
|
||||||
|
try:
|
||||||
|
from typing import TypedDict
|
||||||
|
|
||||||
|
class OptionDict(TypedDict):
|
||||||
|
dest: str
|
||||||
|
aliases: FrozenSet[str]
|
||||||
|
help: str
|
||||||
|
choices: FrozenSet[str]
|
||||||
|
type: str
|
||||||
|
default: Optional[str]
|
||||||
|
condition: bool
|
||||||
|
except ImportError:
|
||||||
|
OptionDict = Dict[str, Any] # type: ignore
|
||||||
|
|
||||||
|
|
||||||
CONFIG_HELP = '''\
|
CONFIG_HELP = '''\
|
||||||
Specify a path to the configuration file(s) to use. All configuration files are
|
Specify a path to the configuration file(s) to use. All configuration files are
|
||||||
@@ -34,49 +55,49 @@ defaults for all users.
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
def surround(x, start, end):
|
def surround(x: str, start: int, end: int) -> str:
|
||||||
if sys.stdout.isatty():
|
if sys.stdout.isatty():
|
||||||
x = '\033[{}m{}\033[{}m'.format(start, x, end)
|
x = '\033[{}m{}\033[{}m'.format(start, x, end)
|
||||||
return x
|
return x
|
||||||
|
|
||||||
|
|
||||||
def emph(x):
|
def emph(x: str) -> str:
|
||||||
return surround(x, 91, 39)
|
return surround(x, 91, 39)
|
||||||
|
|
||||||
|
|
||||||
def cyan(x):
|
def cyan(x: str) -> str:
|
||||||
return surround(x, 96, 39)
|
return surround(x, 96, 39)
|
||||||
|
|
||||||
|
|
||||||
def green(x):
|
def green(x: str) -> str:
|
||||||
return surround(x, 32, 39)
|
return surround(x, 32, 39)
|
||||||
|
|
||||||
|
|
||||||
def blue(x):
|
def blue(x: str) -> str:
|
||||||
return surround(x, 34, 39)
|
return surround(x, 34, 39)
|
||||||
|
|
||||||
|
|
||||||
def yellow(x):
|
def yellow(x: str) -> str:
|
||||||
return surround(x, 93, 39)
|
return surround(x, 93, 39)
|
||||||
|
|
||||||
|
|
||||||
def italic(x):
|
def italic(x: str) -> str:
|
||||||
return surround(x, 3, 23)
|
return surround(x, 3, 23)
|
||||||
|
|
||||||
|
|
||||||
def bold(x):
|
def bold(x: str) -> str:
|
||||||
return surround(x, 1, 22)
|
return surround(x, 1, 22)
|
||||||
|
|
||||||
|
|
||||||
def title(x):
|
def title(x: str) -> str:
|
||||||
return blue(bold(x))
|
return blue(bold(x))
|
||||||
|
|
||||||
|
|
||||||
def opt(text):
|
def opt(text: str) -> str:
|
||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
def option(x):
|
def option(x: str) -> str:
|
||||||
idx = x.find('-')
|
idx = x.find('-')
|
||||||
if idx > -1:
|
if idx > -1:
|
||||||
x = x[idx:]
|
x = x[idx:]
|
||||||
@@ -84,33 +105,39 @@ def option(x):
|
|||||||
return ' '.join(parts)
|
return ' '.join(parts)
|
||||||
|
|
||||||
|
|
||||||
def code(x):
|
def code(x: str) -> str:
|
||||||
return x
|
return x
|
||||||
|
|
||||||
|
|
||||||
def kbd(x):
|
def kbd(x: str) -> str:
|
||||||
return x
|
return x
|
||||||
|
|
||||||
|
|
||||||
def env(x):
|
def env(x: str) -> str:
|
||||||
return italic(x)
|
return italic(x)
|
||||||
|
|
||||||
|
|
||||||
def file(x):
|
def file(x: str) -> str:
|
||||||
return italic(x)
|
return italic(x)
|
||||||
|
|
||||||
|
|
||||||
def parse_option_spec(spec=None):
|
OptionSpecSeq = List[Union[str, OptionDict]]
|
||||||
|
|
||||||
|
|
||||||
|
def parse_option_spec(spec: Optional[str] = None) -> Tuple[OptionSpecSeq, OptionSpecSeq]:
|
||||||
if spec is None:
|
if spec is None:
|
||||||
spec = options_spec()
|
spec = options_spec()
|
||||||
NORMAL, METADATA, HELP = 'NORMAL', 'METADATA', 'HELP'
|
NORMAL, METADATA, HELP = 'NORMAL', 'METADATA', 'HELP'
|
||||||
state = NORMAL
|
state = NORMAL
|
||||||
lines = spec.splitlines()
|
lines = spec.splitlines()
|
||||||
prev_line = ''
|
prev_line = ''
|
||||||
seq = []
|
seq: OptionSpecSeq = []
|
||||||
disabled = []
|
disabled: OptionSpecSeq = []
|
||||||
mpat = re.compile('([a-z]+)=(.+)')
|
mpat = re.compile('([a-z]+)=(.+)')
|
||||||
current_cmd = None
|
current_cmd: OptionDict = {
|
||||||
|
'dest': '', 'aliases': frozenset(), 'help': '', 'choices': frozenset(), 'type': '', 'condition': False, 'default': None
|
||||||
|
}
|
||||||
|
empty_cmd = current_cmd
|
||||||
|
|
||||||
for line in lines:
|
for line in lines:
|
||||||
line = line.rstrip()
|
line = line.rstrip()
|
||||||
@@ -122,7 +149,10 @@ def parse_option_spec(spec=None):
|
|||||||
continue
|
continue
|
||||||
if line.startswith('--'):
|
if line.startswith('--'):
|
||||||
parts = line.split(' ')
|
parts = line.split(' ')
|
||||||
current_cmd = {'dest': parts[0][2:].replace('-', '_'), 'aliases': frozenset(parts), 'help': ''}
|
current_cmd = {
|
||||||
|
'dest': parts[0][2:].replace('-', '_'), 'aliases': frozenset(parts), 'help': '', 'choices': frozenset(), 'type': '',
|
||||||
|
'default': None, 'condition': True
|
||||||
|
}
|
||||||
state = METADATA
|
state = METADATA
|
||||||
continue
|
continue
|
||||||
raise ValueError('Invalid option spec, unexpected line: {}'.format(line))
|
raise ValueError('Invalid option spec, unexpected line: {}'.format(line))
|
||||||
@@ -133,11 +163,17 @@ def parse_option_spec(spec=None):
|
|||||||
current_cmd['help'] += line
|
current_cmd['help'] += line
|
||||||
else:
|
else:
|
||||||
k, v = m.group(1), m.group(2)
|
k, v = m.group(1), m.group(2)
|
||||||
if k == 'condition':
|
|
||||||
v = eval(v)
|
|
||||||
current_cmd[k] = v
|
|
||||||
if k == 'choices':
|
if k == 'choices':
|
||||||
current_cmd['choices'] = {x.strip() for x in current_cmd['choices'].split(',')}
|
current_cmd['choices'] = frozenset(x.strip() for x in v.split(','))
|
||||||
|
else:
|
||||||
|
if k == 'default':
|
||||||
|
current_cmd['default'] = v
|
||||||
|
elif k == 'type':
|
||||||
|
current_cmd['type'] = v
|
||||||
|
elif k == 'dest':
|
||||||
|
current_cmd['dest'] = v
|
||||||
|
elif k == 'condition':
|
||||||
|
current_cmd['condition'] = bool(eval(v))
|
||||||
elif state is HELP:
|
elif state is HELP:
|
||||||
if line:
|
if line:
|
||||||
spc = '' if current_cmd['help'].endswith('\n') else ' '
|
spc = '' if current_cmd['help'].endswith('\n') else ' '
|
||||||
@@ -148,15 +184,15 @@ def parse_option_spec(spec=None):
|
|||||||
else:
|
else:
|
||||||
state = NORMAL
|
state = NORMAL
|
||||||
(seq if current_cmd.get('condition', True) else disabled).append(current_cmd)
|
(seq if current_cmd.get('condition', True) else disabled).append(current_cmd)
|
||||||
current_cmd = None
|
current_cmd = empty_cmd
|
||||||
prev_line = line
|
prev_line = line
|
||||||
if current_cmd is not None:
|
if current_cmd is not empty_cmd:
|
||||||
(seq if current_cmd.get('condition', True) else disabled).append(current_cmd)
|
(seq if current_cmd.get('condition', True) else disabled).append(current_cmd)
|
||||||
|
|
||||||
return seq, disabled
|
return seq, disabled
|
||||||
|
|
||||||
|
|
||||||
def prettify(text):
|
def prettify(text: str) -> str:
|
||||||
role_map = globals()
|
role_map = globals()
|
||||||
|
|
||||||
def sub(m):
|
def sub(m):
|
||||||
@@ -167,11 +203,11 @@ def prettify(text):
|
|||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
def prettify_rst(text):
|
def prettify_rst(text: str) -> str:
|
||||||
return re.sub(r':([a-z]+):`([^`]+)`(=[^\s.]+)', r':\1:`\2`:code:`\3`', text)
|
return re.sub(r':([a-z]+):`([^`]+)`(=[^\s.]+)', r':\1:`\2`:code:`\3`', text)
|
||||||
|
|
||||||
|
|
||||||
def version(add_rev=False):
|
def version(add_rev: bool = False) -> str:
|
||||||
rev = ''
|
rev = ''
|
||||||
from . import fast_data_types
|
from . import fast_data_types
|
||||||
if add_rev and hasattr(fast_data_types, 'KITTY_VCS_REV'):
|
if add_rev and hasattr(fast_data_types, 'KITTY_VCS_REV'):
|
||||||
@@ -179,7 +215,7 @@ def version(add_rev=False):
|
|||||||
return '{} {}{} created by {}'.format(italic(appname), green(str_version), rev, title('Kovid Goyal'))
|
return '{} {}{} created by {}'.format(italic(appname), green(str_version), rev, title('Kovid Goyal'))
|
||||||
|
|
||||||
|
|
||||||
def wrap(text, limit=80):
|
def wrap(text: str, limit: int = 80) -> Iterator[str]:
|
||||||
NORMAL, IN_FORMAT = 'NORMAL', 'IN_FORMAT'
|
NORMAL, IN_FORMAT = 'NORMAL', 'IN_FORMAT'
|
||||||
state = NORMAL
|
state = NORMAL
|
||||||
last_space_at = None
|
last_space_at = None
|
||||||
@@ -203,7 +239,7 @@ def wrap(text, limit=80):
|
|||||||
last_space_at = None
|
last_space_at = None
|
||||||
chars_in_line = i - breaks[-1]
|
chars_in_line = i - breaks[-1]
|
||||||
|
|
||||||
lines = []
|
lines: List[str] = []
|
||||||
for b in reversed(breaks):
|
for b in reversed(breaks):
|
||||||
lines.append(text[b:].lstrip())
|
lines.append(text[b:].lstrip())
|
||||||
text = text[:b]
|
text = text[:b]
|
||||||
@@ -212,8 +248,8 @@ def wrap(text, limit=80):
|
|||||||
return reversed(lines)
|
return reversed(lines)
|
||||||
|
|
||||||
|
|
||||||
def get_defaults_from_seq(seq):
|
def get_defaults_from_seq(seq: OptionSpecSeq) -> Dict[str, Any]:
|
||||||
ans = {}
|
ans: Dict[str, Any] = {}
|
||||||
for opt in seq:
|
for opt in seq:
|
||||||
if not isinstance(opt, str):
|
if not isinstance(opt, str):
|
||||||
ans[opt['dest']] = defval_for_opt(opt)
|
ans[opt['dest']] = defval_for_opt(opt)
|
||||||
@@ -232,21 +268,21 @@ class PrintHelpForSeq:
|
|||||||
|
|
||||||
allow_pager = True
|
allow_pager = True
|
||||||
|
|
||||||
def __call__(self, seq, usage, message, appname):
|
def __call__(self, seq: OptionSpecSeq, usage: Optional[str], message: Optional[str], appname: str) -> None:
|
||||||
from kitty.utils import screen_size_function
|
from kitty.utils import screen_size_function
|
||||||
screen_size = screen_size_function()
|
screen_size = screen_size_function()
|
||||||
try:
|
try:
|
||||||
linesz = min(screen_size().cols, 76)
|
linesz = min(screen_size().cols, 76)
|
||||||
except OSError:
|
except OSError:
|
||||||
linesz = 76
|
linesz = 76
|
||||||
blocks = []
|
blocks: List[str] = []
|
||||||
a = blocks.append
|
a = blocks.append
|
||||||
|
|
||||||
def wa(text, indent=0, leading_indent=None):
|
def wa(text, indent=0, leading_indent=None):
|
||||||
if leading_indent is None:
|
if leading_indent is None:
|
||||||
leading_indent = indent
|
leading_indent = indent
|
||||||
j = '\n' + (' ' * indent)
|
j = '\n' + (' ' * indent)
|
||||||
lines = []
|
lines: List[str] = []
|
||||||
for l in text.splitlines():
|
for l in text.splitlines():
|
||||||
if l:
|
if l:
|
||||||
lines.extend(wrap(l, limit=linesz - indent))
|
lines.extend(wrap(l, limit=linesz - indent))
|
||||||
@@ -279,7 +315,7 @@ class PrintHelpForSeq:
|
|||||||
wa(prettify(t.strip()), indent=4)
|
wa(prettify(t.strip()), indent=4)
|
||||||
if defval is not None:
|
if defval is not None:
|
||||||
wa('Default: {}'.format(defval), indent=4)
|
wa('Default: {}'.format(defval), indent=4)
|
||||||
if 'choices' in opt:
|
if opt.get('choices'):
|
||||||
wa('Choices: {}'.format(', '.join(opt['choices'])), indent=4)
|
wa('Choices: {}'.format(', '.join(opt['choices'])), indent=4)
|
||||||
a('')
|
a('')
|
||||||
|
|
||||||
@@ -299,9 +335,9 @@ class PrintHelpForSeq:
|
|||||||
print_help_for_seq = PrintHelpForSeq()
|
print_help_for_seq = PrintHelpForSeq()
|
||||||
|
|
||||||
|
|
||||||
def seq_as_rst(seq, usage, message, appname, heading_char='-'):
|
def seq_as_rst(seq: OptionSpecSeq, usage: Optional[str], message: Optional[str], appname: Optional[str], heading_char: str = '-') -> str:
|
||||||
import textwrap
|
import textwrap
|
||||||
blocks = []
|
blocks: List[str] = []
|
||||||
a = blocks.append
|
a = blocks.append
|
||||||
|
|
||||||
usage = '[program-to-run ...]' if usage is None else usage
|
usage = '[program-to-run ...]' if usage is None else usage
|
||||||
@@ -338,7 +374,7 @@ def seq_as_rst(seq, usage, message, appname, heading_char='-'):
|
|||||||
a(textwrap.indent(prettify_rst(t), ' ' * 4))
|
a(textwrap.indent(prettify_rst(t), ' ' * 4))
|
||||||
if defval is not None:
|
if defval is not None:
|
||||||
a(textwrap.indent('Default: :code:`{}`'.format(defval), ' ' * 4))
|
a(textwrap.indent('Default: :code:`{}`'.format(defval), ' ' * 4))
|
||||||
if 'choices' in opt:
|
if opt.get('choices'):
|
||||||
a(textwrap.indent('Choices: :code:`{}`'.format(', '.join(sorted(opt['choices']))), ' ' * 4))
|
a(textwrap.indent('Choices: :code:`{}`'.format(', '.join(sorted(opt['choices']))), ' ' * 4))
|
||||||
a('')
|
a('')
|
||||||
|
|
||||||
@@ -346,8 +382,30 @@ def seq_as_rst(seq, usage, message, appname, heading_char='-'):
|
|||||||
return text
|
return text
|
||||||
|
|
||||||
|
|
||||||
def defval_for_opt(opt):
|
def as_type_stub(seq: OptionSpecSeq, disabled: OptionSpecSeq, class_name: str) -> str:
|
||||||
dv = opt.get('default')
|
from itertools import chain
|
||||||
|
ans: List[str] = ['class {}:'.format(class_name)]
|
||||||
|
for opt in chain(seq, disabled):
|
||||||
|
if isinstance(opt, str):
|
||||||
|
continue
|
||||||
|
name = opt['dest']
|
||||||
|
otype = opt['type'] or 'str'
|
||||||
|
if otype in ('str', 'int', 'float'):
|
||||||
|
t = otype
|
||||||
|
elif otype == 'list':
|
||||||
|
t = 'typing.Sequence[str]'
|
||||||
|
elif otype in ('choice', 'choices'):
|
||||||
|
t = 'str'
|
||||||
|
elif otype.startswith('bool-'):
|
||||||
|
t = 'bool'
|
||||||
|
else:
|
||||||
|
raise ValueError('Unknown CLI option type: {}'.format(otype))
|
||||||
|
ans.append(' {}: {}'.format(name, t))
|
||||||
|
return '\n'.join(ans) + '\n\n\n'
|
||||||
|
|
||||||
|
|
||||||
|
def defval_for_opt(opt: OptionDict) -> Any:
|
||||||
|
dv: Any = opt.get('default')
|
||||||
typ = opt.get('type', '')
|
typ = opt.get('type', '')
|
||||||
if typ.startswith('bool-'):
|
if typ.startswith('bool-'):
|
||||||
if dv is None:
|
if dv is None:
|
||||||
@@ -363,11 +421,11 @@ def defval_for_opt(opt):
|
|||||||
|
|
||||||
class Options:
|
class Options:
|
||||||
|
|
||||||
def __init__(self, seq, usage, message, appname):
|
def __init__(self, seq: OptionSpecSeq, usage: Optional[str], message: Optional[str], appname: Optional[str]):
|
||||||
self.alias_map = {}
|
self.alias_map = {}
|
||||||
self.seq = seq
|
self.seq = seq
|
||||||
self.names_map = {}
|
self.names_map: Dict[str, OptionDict] = {}
|
||||||
self.values_map = {}
|
self.values_map: Dict[str, Any] = {}
|
||||||
self.usage, self.message, self.appname = usage, message, appname
|
self.usage, self.message, self.appname = usage, message, appname
|
||||||
for opt in seq:
|
for opt in seq:
|
||||||
if isinstance(opt, str):
|
if isinstance(opt, str):
|
||||||
@@ -378,13 +436,13 @@ class Options:
|
|||||||
self.names_map[name] = opt
|
self.names_map[name] = opt
|
||||||
self.values_map[name] = defval_for_opt(opt)
|
self.values_map[name] = defval_for_opt(opt)
|
||||||
|
|
||||||
def opt_for_alias(self, alias):
|
def opt_for_alias(self, alias: str) -> OptionDict:
|
||||||
opt = self.alias_map.get(alias)
|
opt = self.alias_map.get(alias)
|
||||||
if opt is None:
|
if opt is None:
|
||||||
raise SystemExit('Unknown option: {}'.format(emph(alias)))
|
raise SystemExit('Unknown option: {}'.format(emph(alias)))
|
||||||
return opt
|
return opt
|
||||||
|
|
||||||
def needs_arg(self, alias):
|
def needs_arg(self, alias: str) -> bool:
|
||||||
if alias in ('-h', '--help'):
|
if alias in ('-h', '--help'):
|
||||||
print_help_for_seq(self.seq, self.usage, self.message, self.appname or appname)
|
print_help_for_seq(self.seq, self.usage, self.message, self.appname or appname)
|
||||||
raise SystemExit(0)
|
raise SystemExit(0)
|
||||||
@@ -395,7 +453,7 @@ class Options:
|
|||||||
typ = opt.get('type', '')
|
typ = opt.get('type', '')
|
||||||
return not typ.startswith('bool-')
|
return not typ.startswith('bool-')
|
||||||
|
|
||||||
def process_arg(self, alias, val=None):
|
def process_arg(self, alias: str, val: Any = None) -> None:
|
||||||
opt = self.opt_for_alias(alias)
|
opt = self.opt_for_alias(alias)
|
||||||
typ = opt.get('type', '')
|
typ = opt.get('type', '')
|
||||||
name = opt['dest']
|
name = opt['dest']
|
||||||
@@ -424,23 +482,15 @@ class Options:
|
|||||||
self.values_map[name] = val
|
self.values_map[name] = val
|
||||||
|
|
||||||
|
|
||||||
class Namespace:
|
def parse_cmdline(oc: Options, disabled: OptionSpecSeq, ans: Any, args: Optional[List[str]] = None) -> List[str]:
|
||||||
|
|
||||||
def __init__(self, kwargs):
|
|
||||||
for name, val in kwargs.items():
|
|
||||||
setattr(self, name, val)
|
|
||||||
|
|
||||||
|
|
||||||
def parse_cmdline(oc, disabled, args=None):
|
|
||||||
NORMAL, EXPECTING_ARG = 'NORMAL', 'EXPECTING_ARG'
|
NORMAL, EXPECTING_ARG = 'NORMAL', 'EXPECTING_ARG'
|
||||||
state = NORMAL
|
state = NORMAL
|
||||||
if args is None:
|
dargs = deque(sys.argv[1:] if args is None else args)
|
||||||
args = sys.argv[1:]
|
leftover_args: List[str] = []
|
||||||
args = deque(args)
|
|
||||||
current_option = None
|
current_option = None
|
||||||
|
|
||||||
while args:
|
while dargs:
|
||||||
arg = args.popleft()
|
arg = dargs.popleft()
|
||||||
if state is NORMAL:
|
if state is NORMAL:
|
||||||
if arg.startswith('-'):
|
if arg.startswith('-'):
|
||||||
if arg == '--':
|
if arg == '--':
|
||||||
@@ -458,21 +508,23 @@ def parse_cmdline(oc, disabled, args=None):
|
|||||||
continue
|
continue
|
||||||
oc.process_arg(parts[0], parts[1])
|
oc.process_arg(parts[0], parts[1])
|
||||||
else:
|
else:
|
||||||
args = [arg] + list(args)
|
leftover_args = [arg] + list(dargs)
|
||||||
break
|
break
|
||||||
else:
|
elif current_option is not None:
|
||||||
oc.process_arg(current_option, arg)
|
oc.process_arg(current_option, arg)
|
||||||
current_option, state = None, NORMAL
|
current_option, state = None, NORMAL
|
||||||
if state is EXPECTING_ARG:
|
if state is EXPECTING_ARG:
|
||||||
raise SystemExit('An argument is required for the option: {}'.format(emph(arg)))
|
raise SystemExit('An argument is required for the option: {}'.format(emph(arg)))
|
||||||
|
|
||||||
ans = Namespace(oc.values_map)
|
for key, val in oc.values_map.items():
|
||||||
|
setattr(ans, key, val)
|
||||||
for opt in disabled:
|
for opt in disabled:
|
||||||
setattr(ans, opt['dest'], defval_for_opt(opt))
|
if not isinstance(opt, str):
|
||||||
return ans, list(args)
|
setattr(ans, opt['dest'], defval_for_opt(opt))
|
||||||
|
return leftover_args
|
||||||
|
|
||||||
|
|
||||||
def options_spec():
|
def options_spec() -> str:
|
||||||
if not hasattr(options_spec, 'ans'):
|
if not hasattr(options_spec, 'ans'):
|
||||||
OPTIONS = '''
|
OPTIONS = '''
|
||||||
--class
|
--class
|
||||||
@@ -618,31 +670,49 @@ Print out information about the system and kitty configuration.
|
|||||||
type=bool-set
|
type=bool-set
|
||||||
!
|
!
|
||||||
'''
|
'''
|
||||||
options_spec.ans = OPTIONS.format(
|
setattr(options_spec, 'ans', OPTIONS.format(
|
||||||
appname=appname, config_help=CONFIG_HELP.format(appname=appname, conf_name=appname)
|
appname=appname, config_help=CONFIG_HELP.format(appname=appname, conf_name=appname)
|
||||||
|
|
||||||
)
|
))
|
||||||
return options_spec.ans
|
return getattr(options_spec, 'ans')
|
||||||
|
|
||||||
|
|
||||||
def options_for_completion():
|
def options_for_completion() -> OptionSpecSeq:
|
||||||
raw = '--help -h\ntype=bool-set\nShow help for {appname} command line options\n\n{raw}'.format(
|
raw = '--help -h\ntype=bool-set\nShow help for {appname} command line options\n\n{raw}'.format(
|
||||||
appname=appname, raw=options_spec())
|
appname=appname, raw=options_spec())
|
||||||
return parse_option_spec(raw)[0]
|
return parse_option_spec(raw)[0]
|
||||||
|
|
||||||
|
|
||||||
def option_spec_as_rst(ospec=options_spec, usage=None, message=None, appname=None, heading_char='-'):
|
def option_spec_as_rst(
|
||||||
|
ospec: Callable[[], str] = options_spec,
|
||||||
|
usage: Optional[str] = None, message: Optional[str] = None, appname: Optional[str] = None,
|
||||||
|
heading_char='-'
|
||||||
|
) -> str:
|
||||||
options = parse_option_spec(ospec())
|
options = parse_option_spec(ospec())
|
||||||
seq, disabled = options
|
seq, disabled = options
|
||||||
oc = Options(seq, usage, message, appname)
|
oc = Options(seq, usage, message, appname)
|
||||||
return seq_as_rst(oc.seq, oc.usage, oc.message, oc.appname, heading_char=heading_char)
|
return seq_as_rst(oc.seq, oc.usage, oc.message, oc.appname, heading_char=heading_char)
|
||||||
|
|
||||||
|
|
||||||
def parse_args(args=None, ospec=options_spec, usage=None, message=None, appname=None):
|
T = TypeVar('T')
|
||||||
|
|
||||||
|
|
||||||
|
def parse_args(
|
||||||
|
args: Optional[List[str]] = None,
|
||||||
|
ospec: Callable[[], str] = options_spec,
|
||||||
|
usage: Optional[str] = None,
|
||||||
|
message: Optional[str] = None,
|
||||||
|
appname: Optional[str] = None,
|
||||||
|
result_class: Type[T] = None,
|
||||||
|
) -> Tuple[T, List[str]]:
|
||||||
options = parse_option_spec(ospec())
|
options = parse_option_spec(ospec())
|
||||||
seq, disabled = options
|
seq, disabled = options
|
||||||
oc = Options(seq, usage, message, appname)
|
oc = Options(seq, usage, message, appname)
|
||||||
return parse_cmdline(oc, disabled, args=args)
|
if result_class is not None:
|
||||||
|
ans = result_class()
|
||||||
|
else:
|
||||||
|
ans = cast(T, CLIOptions())
|
||||||
|
return ans, parse_cmdline(oc, disabled, ans, args=args)
|
||||||
|
|
||||||
|
|
||||||
SYSTEM_CONF = '/etc/xdg/kitty/kitty.conf'
|
SYSTEM_CONF = '/etc/xdg/kitty/kitty.conf'
|
||||||
@@ -655,8 +725,8 @@ def print_shortcut(key_sequence, action):
|
|||||||
mmap = {m[len('GLFW_MOD_'):].lower(): x for m, x in v.items() if m.startswith('GLFW_MOD_')}
|
mmap = {m[len('GLFW_MOD_'):].lower(): x for m, x in v.items() if m.startswith('GLFW_MOD_')}
|
||||||
kmap = {k[len('GLFW_KEY_'):].lower(): x for k, x in v.items() if k.startswith('GLFW_KEY_')}
|
kmap = {k[len('GLFW_KEY_'):].lower(): x for k, x in v.items() if k.startswith('GLFW_KEY_')}
|
||||||
krmap = {v: k for k, v in kmap.items()}
|
krmap = {v: k for k, v in kmap.items()}
|
||||||
print_shortcut.maps = mmap, krmap
|
setattr(print_shortcut, 'maps', (mmap, krmap))
|
||||||
mmap, krmap = print_shortcut.maps
|
mmap, krmap = getattr(print_shortcut, 'maps')
|
||||||
keys = []
|
keys = []
|
||||||
for key in key_sequence:
|
for key in key_sequence:
|
||||||
names = []
|
names = []
|
||||||
@@ -667,7 +737,8 @@ def print_shortcut(key_sequence, action):
|
|||||||
if key:
|
if key:
|
||||||
if is_native:
|
if is_native:
|
||||||
from .fast_data_types import GLFW_KEY_UNKNOWN, glfw_get_key_name
|
from .fast_data_types import GLFW_KEY_UNKNOWN, glfw_get_key_name
|
||||||
names.append(glfw_get_key_name(GLFW_KEY_UNKNOWN, key))
|
kn = glfw_get_key_name(GLFW_KEY_UNKNOWN, key) or 'Unknown key'
|
||||||
|
names.append(kn)
|
||||||
else:
|
else:
|
||||||
names.append(krmap[key])
|
names.append(krmap[key])
|
||||||
keys.append('+'.join(names))
|
keys.append('+'.join(names))
|
||||||
@@ -700,12 +771,12 @@ def flatten_sequence_map(m):
|
|||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
|
||||||
def compare_opts(opts):
|
def compare_opts(opts: OptionsStub) -> None:
|
||||||
from .config import defaults, load_config
|
from .config import defaults, load_config
|
||||||
print('\nConfig options different from defaults:')
|
print('\nConfig options different from defaults:')
|
||||||
default_opts = load_config()
|
default_opts = load_config()
|
||||||
changed_opts = [
|
changed_opts = [
|
||||||
f for f in sorted(defaults._fields)
|
f for f in sorted(defaults._fields) # type: ignore
|
||||||
if f not in ('key_definitions', 'keymap', 'sequence_map') and getattr(opts, f) != getattr(defaults, f)
|
if f not in ('key_definitions', 'keymap', 'sequence_map') and getattr(opts, f) != getattr(defaults, f)
|
||||||
]
|
]
|
||||||
field_len = max(map(len, changed_opts)) if changed_opts else 20
|
field_len = max(map(len, changed_opts)) if changed_opts else 20
|
||||||
@@ -713,9 +784,9 @@ def compare_opts(opts):
|
|||||||
for f in changed_opts:
|
for f in changed_opts:
|
||||||
print(title(fmt.format(f)), getattr(opts, f))
|
print(title(fmt.format(f)), getattr(opts, f))
|
||||||
|
|
||||||
final, initial = opts.keymap, default_opts.keymap
|
final_, initial_ = opts.keymap, default_opts.keymap
|
||||||
final = {(k,): v for k, v in final.items()}
|
final = {(k,): v for k, v in final_.items()}
|
||||||
initial = {(k,): v for k, v in initial.items()}
|
initial = {(k,): v for k, v in initial_.items()}
|
||||||
final_s, initial_s = map(flatten_sequence_map, (opts.sequence_map, default_opts.sequence_map))
|
final_s, initial_s = map(flatten_sequence_map, (opts.sequence_map, default_opts.sequence_map))
|
||||||
final.update(final_s)
|
final.update(final_s)
|
||||||
initial.update(initial_s)
|
initial.update(initial_s)
|
||||||
|
|||||||
64
kitty/cli_stub.py
Normal file
64
kitty/cli_stub.py
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
#!/usr/bin/env python
|
||||||
|
# vim:fileencoding=utf-8
|
||||||
|
# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
|
|
||||||
|
class CLIOptions:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
LaunchCLIOptions = AskCLIOptions = ClipboardCLIOptions = DiffCLIOptions = CLIOptions
|
||||||
|
HintsCLIOptions = IcatCLIOptions = PanelCLIOptions = ResizeCLIOptions = CLIOptions
|
||||||
|
ErrorCLIOptions = UnicodeCLIOptions = CLIOptions
|
||||||
|
|
||||||
|
|
||||||
|
def generate_stub() -> None:
|
||||||
|
from .cli import parse_option_spec, as_type_stub
|
||||||
|
from .conf.definition import save_type_stub
|
||||||
|
text = 'import typing\n\n\n'
|
||||||
|
|
||||||
|
def do(otext=None, cls: str = 'CLIOptions'):
|
||||||
|
nonlocal text
|
||||||
|
text += as_type_stub(*parse_option_spec(otext), class_name=cls)
|
||||||
|
|
||||||
|
do()
|
||||||
|
|
||||||
|
from .launch import options_spec
|
||||||
|
do(options_spec(), 'LaunchCLIOptions')
|
||||||
|
|
||||||
|
from kittens.ask.main import option_text
|
||||||
|
do(option_text(), 'AskCLIOptions')
|
||||||
|
|
||||||
|
from kittens.clipboard.main import OPTIONS
|
||||||
|
do(OPTIONS(), 'ClipboardCLIOptions')
|
||||||
|
|
||||||
|
from kittens.diff.main import OPTIONS
|
||||||
|
do(OPTIONS(), 'DiffCLIOptions')
|
||||||
|
|
||||||
|
from kittens.hints.main import OPTIONS
|
||||||
|
do(OPTIONS(), 'HintsCLIOptions')
|
||||||
|
|
||||||
|
from kittens.icat.main import OPTIONS
|
||||||
|
do(OPTIONS, 'IcatCLIOptions')
|
||||||
|
|
||||||
|
from kittens.panel.main import OPTIONS
|
||||||
|
do(OPTIONS(), 'PanelCLIOptions')
|
||||||
|
|
||||||
|
from kittens.resize_window.main import OPTIONS
|
||||||
|
do(OPTIONS(), 'ResizeCLIOptions')
|
||||||
|
|
||||||
|
from kittens.show_error.main import OPTIONS
|
||||||
|
do(OPTIONS(), 'ErrorCLIOptions')
|
||||||
|
|
||||||
|
from kittens.unicode_input.main import OPTIONS
|
||||||
|
do(OPTIONS(), 'UnicodeCLIOptions')
|
||||||
|
|
||||||
|
save_type_stub(text, __file__)
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
import subprocess
|
||||||
|
subprocess.Popen([
|
||||||
|
'kitty', '+runpy',
|
||||||
|
'from kitty.cli_stub import generate_stub; generate_stub()'
|
||||||
|
])
|
||||||
118
kitty/cli_stub.pyi
Normal file
118
kitty/cli_stub.pyi
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
# Update this file by running: python kitty/cli_stub.py
|
||||||
|
import typing
|
||||||
|
|
||||||
|
|
||||||
|
class CLIOptions:
|
||||||
|
cls: str
|
||||||
|
name: str
|
||||||
|
title: str
|
||||||
|
config: typing.Sequence[str]
|
||||||
|
override: typing.Sequence[str]
|
||||||
|
directory: str
|
||||||
|
detach: bool
|
||||||
|
session: str
|
||||||
|
hold: bool
|
||||||
|
single_instance: bool
|
||||||
|
instance_group: str
|
||||||
|
wait_for_single_instance_window_close: bool
|
||||||
|
listen_on: str
|
||||||
|
start_as: str
|
||||||
|
version: bool
|
||||||
|
dump_commands: bool
|
||||||
|
replay_commands: str
|
||||||
|
dump_bytes: str
|
||||||
|
debug_gl: bool
|
||||||
|
debug_keyboard: bool
|
||||||
|
debug_font_fallback: bool
|
||||||
|
debug_config: bool
|
||||||
|
execute: bool
|
||||||
|
|
||||||
|
|
||||||
|
class LaunchCLIOptions:
|
||||||
|
window_title: str
|
||||||
|
tab_title: str
|
||||||
|
type: str
|
||||||
|
keep_focus: bool
|
||||||
|
cwd: str
|
||||||
|
env: typing.Sequence[str]
|
||||||
|
copy_colors: bool
|
||||||
|
copy_cmdline: bool
|
||||||
|
copy_env: bool
|
||||||
|
location: str
|
||||||
|
allow_remote_control: bool
|
||||||
|
stdin_source: str
|
||||||
|
stdin_add_formatting: bool
|
||||||
|
stdin_add_line_wrap_markers: bool
|
||||||
|
marker: str
|
||||||
|
|
||||||
|
|
||||||
|
class AskCLIOptions:
|
||||||
|
type: str
|
||||||
|
message: str
|
||||||
|
name: str
|
||||||
|
|
||||||
|
|
||||||
|
class ClipboardCLIOptions:
|
||||||
|
get_clipboard: bool
|
||||||
|
use_primary: bool
|
||||||
|
wait_for_completion: bool
|
||||||
|
|
||||||
|
|
||||||
|
class DiffCLIOptions:
|
||||||
|
context: int
|
||||||
|
config: typing.Sequence[str]
|
||||||
|
override: typing.Sequence[str]
|
||||||
|
|
||||||
|
|
||||||
|
class HintsCLIOptions:
|
||||||
|
program: typing.Sequence[str]
|
||||||
|
type: str
|
||||||
|
regex: str
|
||||||
|
linenum_action: str
|
||||||
|
url_prefixes: str
|
||||||
|
word_characters: str
|
||||||
|
minimum_match_length: int
|
||||||
|
multiple: bool
|
||||||
|
multiple_joiner: str
|
||||||
|
add_trailing_space: str
|
||||||
|
hints_offset: int
|
||||||
|
alphabet: str
|
||||||
|
ascending: bool
|
||||||
|
customize_processing: str
|
||||||
|
|
||||||
|
|
||||||
|
class IcatCLIOptions:
|
||||||
|
align: str
|
||||||
|
place: str
|
||||||
|
scale_up: bool
|
||||||
|
clear: bool
|
||||||
|
transfer_mode: str
|
||||||
|
detect_support: bool
|
||||||
|
detection_timeout: float
|
||||||
|
print_window_size: bool
|
||||||
|
stdin: str
|
||||||
|
silent: bool
|
||||||
|
z_index: str
|
||||||
|
|
||||||
|
|
||||||
|
class PanelCLIOptions:
|
||||||
|
lines: int
|
||||||
|
columns: int
|
||||||
|
edge: str
|
||||||
|
config: typing.Sequence[str]
|
||||||
|
override: typing.Sequence[str]
|
||||||
|
|
||||||
|
|
||||||
|
class ResizeCLIOptions:
|
||||||
|
horizontal_increment: int
|
||||||
|
vertical_increment: int
|
||||||
|
|
||||||
|
|
||||||
|
class ErrorCLIOptions:
|
||||||
|
title: str
|
||||||
|
|
||||||
|
|
||||||
|
class UnicodeCLIOptions:
|
||||||
|
emoji_variation: str
|
||||||
|
|
||||||
|
|
||||||
@@ -9,7 +9,7 @@ from contextlib import suppress
|
|||||||
from typing import Any, BinaryIO, Callable, Dict, List, Optional
|
from typing import Any, BinaryIO, Callable, Dict, List, Optional
|
||||||
|
|
||||||
from .cli import (
|
from .cli import (
|
||||||
Namespace, get_defaults_from_seq, parse_args, parse_option_spec
|
get_defaults_from_seq, parse_args, parse_option_spec
|
||||||
)
|
)
|
||||||
from .config import parse_config, parse_send_text_bytes
|
from .config import parse_config, parse_send_text_bytes
|
||||||
from .constants import appname
|
from .constants import appname
|
||||||
@@ -42,7 +42,7 @@ class UnknownLayout(ValueError):
|
|||||||
hide_traceback = True
|
hide_traceback = True
|
||||||
|
|
||||||
|
|
||||||
CommandFunction = Callable[[Namespace, Namespace, List[str]], Optional[Dict[str, Any]]]
|
CommandFunction = Callable[[Any, Any, List[str]], Optional[Dict[str, Any]]]
|
||||||
cmap: Dict[str, CommandFunction] = {}
|
cmap: Dict[str, CommandFunction] = {}
|
||||||
|
|
||||||
|
|
||||||
@@ -906,13 +906,16 @@ def cmd_launch(global_opts, opts, args):
|
|||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
|
||||||
|
class GlobalOpts:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def launch(boss, window, payload):
|
def launch(boss, window, payload):
|
||||||
pg = cmd_launch.payload_get
|
pg = cmd_launch.payload_get
|
||||||
default_opts = parse_launch_args()[0]
|
default_opts = parse_launch_args()[0]
|
||||||
opts = {}
|
opts = GlobalOpts()
|
||||||
for key, default_value in default_opts.__dict__.items():
|
for key, default_value in default_opts.__dict__.items():
|
||||||
opts[key] = payload.get(key, default_value)
|
setattr(opts, key, payload.get(key, default_value))
|
||||||
opts = Namespace(opts)
|
|
||||||
match = pg(payload, 'match')
|
match = pg(payload, 'match')
|
||||||
if match:
|
if match:
|
||||||
tabs = tuple(boss.match_tabs(match))
|
tabs = tuple(boss.match_tabs(match))
|
||||||
@@ -1444,8 +1447,12 @@ def cli_params_for(func):
|
|||||||
return (func.options_spec or '\n').format, func.argspec, func.desc, '{} @ {}'.format(appname, func.name)
|
return (func.options_spec or '\n').format, func.argspec, func.desc, '{} @ {}'.format(appname, func.name)
|
||||||
|
|
||||||
|
|
||||||
|
class SubCommandOptions:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def parse_subcommand_cli(func, args):
|
def parse_subcommand_cli(func, args):
|
||||||
opts, items = parse_args(args[1:], *cli_params_for(func))
|
opts, items = parse_args(args[1:], *cli_params_for(func), result_class=SubCommandOptions)
|
||||||
if func.args_count is not None and func.args_count != len(items):
|
if func.args_count is not None and func.args_count != len(items):
|
||||||
if func.args_count == 0:
|
if func.args_count == 0:
|
||||||
raise SystemExit('Unknown extra argument(s) supplied to {}'.format(func.name))
|
raise SystemExit('Unknown extra argument(s) supplied to {}'.format(func.name))
|
||||||
@@ -1455,4 +1462,4 @@ def parse_subcommand_cli(func, args):
|
|||||||
|
|
||||||
def display_subcommand_help(func):
|
def display_subcommand_help(func):
|
||||||
with suppress(SystemExit):
|
with suppress(SystemExit):
|
||||||
parse_args(['--help'], (func.options_spec or '\n').format, func.argspec, func.desc, func.name)
|
parse_args(['--help'], (func.options_spec or '\n').format, func.argspec, func.desc, func.name, result_class=SubCommandOptions)
|
||||||
|
|||||||
@@ -4,7 +4,9 @@
|
|||||||
|
|
||||||
import re
|
import re
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from typing import Any, Dict, List, Set, Tuple, Union, get_type_hints, Optional
|
from typing import (
|
||||||
|
Any, Dict, Iterator, List, Optional, Set, Tuple, Union, get_type_hints
|
||||||
|
)
|
||||||
|
|
||||||
from .utils import to_bool
|
from .utils import to_bool
|
||||||
|
|
||||||
@@ -26,7 +28,7 @@ class Option:
|
|||||||
|
|
||||||
__slots__ = 'name', 'group', 'long_text', 'option_type', 'defval_as_string', 'add_to_default', 'add_to_docs', 'line'
|
__slots__ = 'name', 'group', 'long_text', 'option_type', 'defval_as_string', 'add_to_default', 'add_to_docs', 'line'
|
||||||
|
|
||||||
def __init__(self, name, group, defval, option_type, long_text, add_to_default, add_to_docs):
|
def __init__(self, name: str, group: str, defval: str, option_type: Any, long_text: str, add_to_default: bool, add_to_docs: bool):
|
||||||
self.name, self.group = name, group
|
self.name, self.group = name, group
|
||||||
self.long_text, self.option_type = long_text.strip(), option_type
|
self.long_text, self.option_type = long_text.strip(), option_type
|
||||||
self.defval_as_string = defval
|
self.defval_as_string = defval
|
||||||
@@ -158,8 +160,8 @@ def remove_markup(text):
|
|||||||
return re.sub(r':([a-zA-Z0-9]+):`(.+?)`', sub, text, flags=re.DOTALL)
|
return re.sub(r':([a-zA-Z0-9]+):`(.+?)`', sub, text, flags=re.DOTALL)
|
||||||
|
|
||||||
|
|
||||||
def iter_blocks(lines):
|
def iter_blocks(lines: Iterator[str]):
|
||||||
current_block = []
|
current_block: List[str] = []
|
||||||
prev_indent = 0
|
prev_indent = 0
|
||||||
for line in lines:
|
for line in lines:
|
||||||
indent_size = len(line) - len(line.lstrip())
|
indent_size = len(line) - len(line.lstrip())
|
||||||
@@ -180,9 +182,10 @@ def wrapped_block(lines):
|
|||||||
wrapper = getattr(wrapped_block, 'wrapper', None)
|
wrapper = getattr(wrapped_block, 'wrapper', None)
|
||||||
if wrapper is None:
|
if wrapper is None:
|
||||||
import textwrap
|
import textwrap
|
||||||
wrapper = wrapped_block.wrapper = textwrap.TextWrapper(
|
wrapper = textwrap.TextWrapper(
|
||||||
initial_indent='#: ', subsequent_indent='#: ', width=70, break_long_words=False
|
initial_indent='#: ', subsequent_indent='#: ', width=70, break_long_words=False
|
||||||
)
|
)
|
||||||
|
setattr(wrapped_block, 'wrapper', wrapper)
|
||||||
for block, indent_size in iter_blocks(lines):
|
for block, indent_size in iter_blocks(lines):
|
||||||
if indent_size > 0:
|
if indent_size > 0:
|
||||||
for line in block:
|
for line in block:
|
||||||
@@ -278,9 +281,9 @@ def as_conf_file(all_options):
|
|||||||
else:
|
else:
|
||||||
count += 1
|
count += 1
|
||||||
else:
|
else:
|
||||||
if start is not None:
|
if start is not None and count is not None:
|
||||||
map_groups.append((start, count))
|
map_groups.append((start, count))
|
||||||
start = None
|
start = count = None
|
||||||
for start, count in map_groups:
|
for start, count in map_groups:
|
||||||
r = range(start, start + count)
|
r = range(start, start + count)
|
||||||
sz = max(len(ans[i].split(' ', 3)[1]) for i in r)
|
sz = max(len(ans[i].split(' ', 3)[1]) for i in r)
|
||||||
@@ -304,8 +307,13 @@ def config_lines(all_options):
|
|||||||
yield sc.line
|
yield sc.line
|
||||||
|
|
||||||
|
|
||||||
def as_type_stub(all_options: Dict[str, Union[Option, List[Shortcut]]], special_types: Optional[Dict[str, str]] = None) -> str:
|
def as_type_stub(
|
||||||
ans = ['import typing\n', '', 'class Options:']
|
all_options: Dict[str, Union[Option, List[Shortcut]]],
|
||||||
|
special_types: Optional[Dict[str, str]] = None,
|
||||||
|
preamble_lines: Union[Tuple[str, ...], List[str], Iterator[str]] = (),
|
||||||
|
extra_fields: Union[Tuple[Tuple[str, str], ...], List[Tuple[str, str]], Iterator[Tuple[str, str]]] = ()
|
||||||
|
) -> str:
|
||||||
|
ans = ['import typing\n'] + list(preamble_lines) + ['', 'class Options:']
|
||||||
imports: Set[Tuple[str, str]] = set()
|
imports: Set[Tuple[str, str]] = set()
|
||||||
overrides = special_types or {}
|
overrides = special_types or {}
|
||||||
for name, val in all_options.items():
|
for name, val in all_options.items():
|
||||||
@@ -316,4 +324,16 @@ def as_type_stub(all_options: Dict[str, Union[Option, List[Shortcut]]], special_
|
|||||||
for mod, name in imports:
|
for mod, name in imports:
|
||||||
ans.insert(0, 'from {} import {}'.format(mod, name))
|
ans.insert(0, 'from {} import {}'.format(mod, name))
|
||||||
ans.insert(0, 'import {}'.format(mod))
|
ans.insert(0, 'import {}'.format(mod))
|
||||||
|
for field_name, type_def in extra_fields:
|
||||||
|
ans.append(' {}: {}'.format(field_name, type_def))
|
||||||
return '\n'.join(ans)
|
return '\n'.join(ans)
|
||||||
|
|
||||||
|
|
||||||
|
def save_type_stub(text: str, fpath: str) -> None:
|
||||||
|
import os
|
||||||
|
with open(fpath + 'i', 'w') as f:
|
||||||
|
print(
|
||||||
|
'# Update this file by running: python {}'.format(os.path.relpath(os.path.abspath(fpath))),
|
||||||
|
file=f
|
||||||
|
)
|
||||||
|
f.write(text)
|
||||||
|
|||||||
@@ -73,7 +73,9 @@ def choices(*choices) -> Callable[[str], str]:
|
|||||||
|
|
||||||
|
|
||||||
def parse_line(
|
def parse_line(
|
||||||
line: str, type_map: Dict[str, Any], special_handling: Callable,
|
line: str,
|
||||||
|
type_convert: Callable[[str, Any], Any],
|
||||||
|
special_handling: Callable,
|
||||||
ans: Dict[str, Any], all_keys: Optional[FrozenSet[str]],
|
ans: Dict[str, Any], all_keys: Optional[FrozenSet[str]],
|
||||||
base_path_for_includes: str
|
base_path_for_includes: str
|
||||||
) -> None:
|
) -> None:
|
||||||
@@ -93,7 +95,7 @@ def parse_line(
|
|||||||
val = os.path.join(base_path_for_includes, val)
|
val = os.path.join(base_path_for_includes, val)
|
||||||
try:
|
try:
|
||||||
with open(val, encoding='utf-8', errors='replace') as include:
|
with open(val, encoding='utf-8', errors='replace') as include:
|
||||||
_parse(include, type_map, special_handling, ans, all_keys)
|
_parse(include, type_convert, special_handling, ans, all_keys)
|
||||||
except FileNotFoundError:
|
except FileNotFoundError:
|
||||||
log_error(
|
log_error(
|
||||||
'Could not find included config file: {}, ignoring'.
|
'Could not find included config file: {}, ignoring'.
|
||||||
@@ -108,15 +110,12 @@ def parse_line(
|
|||||||
if all_keys is not None and key not in all_keys:
|
if all_keys is not None and key not in all_keys:
|
||||||
log_error('Ignoring unknown config key: {}'.format(key))
|
log_error('Ignoring unknown config key: {}'.format(key))
|
||||||
return
|
return
|
||||||
tm = type_map.get(key)
|
ans[key] = type_convert(key, val)
|
||||||
if tm is not None:
|
|
||||||
val = tm(val)
|
|
||||||
ans[key] = val
|
|
||||||
|
|
||||||
|
|
||||||
def _parse(
|
def _parse(
|
||||||
lines: Iterator[str],
|
lines: Iterator[str],
|
||||||
type_map: Dict[str, Any],
|
type_convert: Callable[[str, Any], Any],
|
||||||
special_handling: Callable,
|
special_handling: Callable,
|
||||||
ans: Dict[str, Any],
|
ans: Dict[str, Any],
|
||||||
all_keys: Optional[FrozenSet[str]],
|
all_keys: Optional[FrozenSet[str]],
|
||||||
@@ -131,7 +130,7 @@ def _parse(
|
|||||||
for i, line in enumerate(lines):
|
for i, line in enumerate(lines):
|
||||||
try:
|
try:
|
||||||
parse_line(
|
parse_line(
|
||||||
line, type_map, special_handling, ans, all_keys,
|
line, type_convert, special_handling, ans, all_keys,
|
||||||
base_path_for_includes
|
base_path_for_includes
|
||||||
)
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
@@ -143,7 +142,7 @@ def _parse(
|
|||||||
def parse_config_base(
|
def parse_config_base(
|
||||||
lines: Iterator[str],
|
lines: Iterator[str],
|
||||||
defaults: Any,
|
defaults: Any,
|
||||||
type_map: Dict[str, Any],
|
type_convert: Callable[[str, Any], Any],
|
||||||
special_handling: Callable,
|
special_handling: Callable,
|
||||||
ans: Dict[str, Any],
|
ans: Dict[str, Any],
|
||||||
check_keys=True,
|
check_keys=True,
|
||||||
@@ -151,7 +150,7 @@ def parse_config_base(
|
|||||||
):
|
):
|
||||||
all_keys: Optional[FrozenSet[str]] = defaults._asdict() if check_keys else None
|
all_keys: Optional[FrozenSet[str]] = defaults._asdict() if check_keys else None
|
||||||
_parse(
|
_parse(
|
||||||
lines, type_map, special_handling, ans, all_keys, accumulate_bad_lines
|
lines, type_convert, special_handling, ans, all_keys, accumulate_bad_lines
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -7,9 +7,9 @@ import os
|
|||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
from contextlib import contextmanager
|
from contextlib import contextmanager, suppress
|
||||||
from contextlib import suppress
|
|
||||||
from functools import partial
|
from functools import partial
|
||||||
|
from typing import Type
|
||||||
|
|
||||||
from . import fast_data_types as defines
|
from . import fast_data_types as defines
|
||||||
from .conf.definition import as_conf_file, config_lines
|
from .conf.definition import as_conf_file, config_lines
|
||||||
@@ -17,10 +17,11 @@ from .conf.utils import (
|
|||||||
init_config, key_func, load_config as _load_config, merge_dicts,
|
init_config, key_func, load_config as _load_config, merge_dicts,
|
||||||
parse_config_base, python_string, to_bool, to_cmdline
|
parse_config_base, python_string, to_bool, to_cmdline
|
||||||
)
|
)
|
||||||
from .config_data import all_options, parse_mods, type_map
|
from .config_data import all_options, parse_mods, type_convert
|
||||||
from .constants import cache_dir, defconf, is_macos
|
from .constants import cache_dir, defconf, is_macos
|
||||||
from .utils import log_error
|
|
||||||
from .key_names import get_key_name_lookup, key_name_aliases
|
from .key_names import get_key_name_lookup, key_name_aliases
|
||||||
|
from .options_stub import Options as OptionsStub
|
||||||
|
from .utils import log_error
|
||||||
|
|
||||||
|
|
||||||
def parse_shortcut(sc):
|
def parse_shortcut(sc):
|
||||||
@@ -575,9 +576,6 @@ def special_handling(key, val, ans):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
defaults = None
|
|
||||||
|
|
||||||
|
|
||||||
def option_names_for_completion():
|
def option_names_for_completion():
|
||||||
yield from defaults
|
yield from defaults
|
||||||
yield from special_handlers
|
yield from special_handlers
|
||||||
@@ -585,10 +583,15 @@ def option_names_for_completion():
|
|||||||
|
|
||||||
def parse_config(lines, check_keys=True, accumulate_bad_lines=None):
|
def parse_config(lines, check_keys=True, accumulate_bad_lines=None):
|
||||||
ans = {'symbol_map': {}, 'keymap': {}, 'sequence_map': {}, 'key_definitions': [], 'env': {}, 'kitten_aliases': {}, 'font_features': {}}
|
ans = {'symbol_map': {}, 'keymap': {}, 'sequence_map': {}, 'key_definitions': [], 'env': {}, 'kitten_aliases': {}, 'font_features': {}}
|
||||||
|
if check_keys:
|
||||||
|
defs = defaults
|
||||||
|
else:
|
||||||
|
defs = None
|
||||||
|
|
||||||
parse_config_base(
|
parse_config_base(
|
||||||
lines,
|
lines,
|
||||||
defaults,
|
defs,
|
||||||
type_map,
|
type_convert,
|
||||||
special_handling,
|
special_handling,
|
||||||
ans,
|
ans,
|
||||||
check_keys=check_keys,
|
check_keys=check_keys,
|
||||||
@@ -602,7 +605,9 @@ def parse_defaults(lines, check_keys=False):
|
|||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
|
||||||
Options, defaults = init_config(config_lines(all_options), parse_defaults)
|
xc = init_config(config_lines(all_options), parse_defaults)
|
||||||
|
Options: Type[OptionsStub] = xc[0]
|
||||||
|
defaults: OptionsStub = xc[1]
|
||||||
actions = frozenset(all_key_actions) | frozenset(
|
actions = frozenset(all_key_actions) | frozenset(
|
||||||
'run_simple_kitten combine send_text goto_tab goto_layout set_font_size new_tab_with_cwd new_window_with_cwd new_os_window_with_cwd'.
|
'run_simple_kitten combine send_text goto_tab goto_layout set_font_size new_tab_with_cwd new_window_with_cwd new_os_window_with_cwd'.
|
||||||
split()
|
split()
|
||||||
@@ -763,7 +768,7 @@ def finalize_keys(opts):
|
|||||||
opts.sequence_map = sequence_map
|
opts.sequence_map = sequence_map
|
||||||
|
|
||||||
|
|
||||||
def load_config(*paths, overrides=None, accumulate_bad_lines=None):
|
def load_config(*paths, overrides=None, accumulate_bad_lines=None) -> OptionsStub:
|
||||||
parser = parse_config
|
parser = parse_config
|
||||||
if accumulate_bad_lines is not None:
|
if accumulate_bad_lines is not None:
|
||||||
parser = partial(parse_config, accumulate_bad_lines=accumulate_bad_lines)
|
parser = partial(parse_config, accumulate_bad_lines=accumulate_bad_lines)
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
import os
|
import os
|
||||||
from gettext import gettext as _
|
from gettext import gettext as _
|
||||||
from typing import (
|
from typing import (
|
||||||
Dict, FrozenSet, Iterable, List, Optional, Set, Tuple, TypeVar, Union
|
Any, Dict, FrozenSet, Iterable, List, Optional, Set, Tuple, TypeVar, Union
|
||||||
)
|
)
|
||||||
|
|
||||||
from . import fast_data_types as defines
|
from . import fast_data_types as defines
|
||||||
@@ -1432,4 +1432,9 @@ the line (same as pressing the Home key)::
|
|||||||
# }}}
|
# }}}
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
type_map = {o.name: o.option_type for o in all_options.values() if isinstance(o, Option)}
|
|
||||||
|
def type_convert(name: str, val: Any) -> Any:
|
||||||
|
o = all_options.get(name)
|
||||||
|
if isinstance(o, Option):
|
||||||
|
val = o.option_type(val)
|
||||||
|
return val
|
||||||
|
|||||||
@@ -1,9 +1,10 @@
|
|||||||
from typing import Any, Callable, Dict, List, NewType, Optional, Tuple, Union
|
from typing import Any, Callable, Dict, List, NewType, Optional, Tuple, Union
|
||||||
|
|
||||||
from kitty.boss import Boss
|
from kitty.boss import Boss
|
||||||
from kitty.cli import Namespace
|
from kitty.options_stub import Options
|
||||||
|
|
||||||
# Constants {{{
|
# Constants {{{
|
||||||
|
KITTY_VCS_REV: str
|
||||||
ERROR_PREFIX: str
|
ERROR_PREFIX: str
|
||||||
GLSL_VERSION: int
|
GLSL_VERSION: int
|
||||||
GLFW_IBEAM_CURSOR: int
|
GLFW_IBEAM_CURSOR: int
|
||||||
@@ -339,6 +340,10 @@ def redirect_std_streams(devnull: str) -> None:
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def glfw_get_key_name(key: int, native_key: int) -> Optional[str]:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
StartupCtx = NewType('StartupCtx', int)
|
StartupCtx = NewType('StartupCtx', int)
|
||||||
Display = NewType('Display', int)
|
Display = NewType('Display', int)
|
||||||
|
|
||||||
@@ -468,7 +473,7 @@ def update_window_visibility(os_window_id: int, tab_id: int, window_id: int, win
|
|||||||
|
|
||||||
|
|
||||||
def set_options(
|
def set_options(
|
||||||
opts: Namespace,
|
opts: Options,
|
||||||
is_wayland: bool = False,
|
is_wayland: bool = False,
|
||||||
debug_gl: bool = False,
|
debug_gl: bool = False,
|
||||||
debug_font_fallback: bool = False
|
debug_font_fallback: bool = False
|
||||||
|
|||||||
@@ -4,10 +4,11 @@
|
|||||||
|
|
||||||
|
|
||||||
from functools import lru_cache
|
from functools import lru_cache
|
||||||
from typing import Dict
|
from typing import Dict, List, Optional, Sequence, Tuple
|
||||||
|
|
||||||
from .child import Child
|
from .child import Child
|
||||||
from .cli import parse_args
|
from .cli import parse_args
|
||||||
|
from .cli_stub import LaunchCLIOptions
|
||||||
from .fast_data_types import set_clipboard_string
|
from .fast_data_types import set_clipboard_string
|
||||||
from .utils import set_primary_selection
|
from .utils import set_primary_selection
|
||||||
|
|
||||||
@@ -130,16 +131,16 @@ the same as for the :code:`toggle_marker` map action (see :doc:`/marks`).
|
|||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
def parse_launch_args(args=None):
|
def parse_launch_args(args: Optional[Sequence[str]] = None) -> Tuple[LaunchCLIOptions, List[str]]:
|
||||||
args = list(args or ())
|
args = list(args or ())
|
||||||
try:
|
try:
|
||||||
opts, args = parse_args(args=args, ospec=options_spec)
|
opts, args = parse_args(result_class=LaunchCLIOptions, args=args, ospec=options_spec)
|
||||||
except SystemExit as e:
|
except SystemExit as e:
|
||||||
raise ValueError from e
|
raise ValueError from e
|
||||||
return opts, args
|
return opts, args
|
||||||
|
|
||||||
|
|
||||||
def get_env(opts, active_child: Child) -> Dict[str, str]:
|
def get_env(opts: LaunchCLIOptions, active_child: Child) -> Dict[str, str]:
|
||||||
env: Dict[str, str] = {}
|
env: Dict[str, str] = {}
|
||||||
if opts.copy_env and active_child:
|
if opts.copy_env and active_child:
|
||||||
env.update(active_child.foreground_environ)
|
env.update(active_child.foreground_environ)
|
||||||
@@ -150,7 +151,7 @@ def get_env(opts, active_child: Child) -> Dict[str, str]:
|
|||||||
return env
|
return env
|
||||||
|
|
||||||
|
|
||||||
def tab_for_window(boss, opts, target_tab=None):
|
def tab_for_window(boss, opts: LaunchCLIOptions, target_tab=None):
|
||||||
if opts.type == 'tab':
|
if opts.type == 'tab':
|
||||||
tm = boss.active_tab_manager
|
tm = boss.active_tab_manager
|
||||||
tab = tm.new_tab(empty_tab=True, location=opts.location)
|
tab = tm.new_tab(empty_tab=True, location=opts.location)
|
||||||
@@ -168,7 +169,7 @@ def tab_for_window(boss, opts, target_tab=None):
|
|||||||
return tab
|
return tab
|
||||||
|
|
||||||
|
|
||||||
def launch(boss, opts, args, target_tab=None):
|
def launch(boss, opts: LaunchCLIOptions, args: List[str], target_tab=None):
|
||||||
active = boss.active_window_for_cwd
|
active = boss.active_window_for_cwd
|
||||||
active_child = getattr(active, 'child', None)
|
active_child = getattr(active, 'child', None)
|
||||||
env = get_env(opts, active_child)
|
env = get_env(opts, active_child)
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ from .borders import load_borders_program
|
|||||||
from .boss import Boss
|
from .boss import Boss
|
||||||
from .child import set_default_env
|
from .child import set_default_env
|
||||||
from .cli import create_opts, parse_args
|
from .cli import create_opts, parse_args
|
||||||
|
from .cli_stub import CLIOptions
|
||||||
from .config import cached_values_for, initial_window_size_func
|
from .config import cached_values_for, initial_window_size_func
|
||||||
from .constants import (
|
from .constants import (
|
||||||
appname, beam_cursor_data_file, config_dir, glfw_path, is_macos,
|
appname, beam_cursor_data_file, config_dir, glfw_path, is_macos,
|
||||||
@@ -280,7 +281,7 @@ def _main():
|
|||||||
cwd_ok = False
|
cwd_ok = False
|
||||||
if not cwd_ok:
|
if not cwd_ok:
|
||||||
os.chdir(os.path.expanduser('~'))
|
os.chdir(os.path.expanduser('~'))
|
||||||
args, rest = parse_args(args=args)
|
args, rest = parse_args(result_class=CLIOptions, args=args)
|
||||||
args.args = rest
|
args.args = rest
|
||||||
if args.debug_config:
|
if args.debug_config:
|
||||||
create_opts(args, debug_config=True)
|
create_opts(args, debug_config=True)
|
||||||
|
|||||||
@@ -2,8 +2,6 @@
|
|||||||
# vim:fileencoding=utf-8
|
# vim:fileencoding=utf-8
|
||||||
# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
|
# License: GPLv3 Copyright: 2020, Kovid Goyal <kovid at kovidgoyal.net>
|
||||||
|
|
||||||
import os
|
|
||||||
|
|
||||||
|
|
||||||
class Options:
|
class Options:
|
||||||
pass
|
pass
|
||||||
@@ -11,19 +9,23 @@ class Options:
|
|||||||
|
|
||||||
def generate_stub():
|
def generate_stub():
|
||||||
from .config_data import all_options
|
from .config_data import all_options
|
||||||
from .conf.definition import as_type_stub
|
from .conf.definition import as_type_stub, save_type_stub
|
||||||
text = as_type_stub(
|
text = as_type_stub(
|
||||||
all_options,
|
all_options,
|
||||||
special_types={
|
special_types={
|
||||||
'symbol_map': 'typing.Dict[typing.Tuple[int, int], str]'
|
'symbol_map': 'typing.Dict[typing.Tuple[int, int], str]'
|
||||||
}
|
},
|
||||||
)
|
preamble_lines=(
|
||||||
with open(__file__ + 'i', 'w') as f:
|
'from kitty.config import KeyAction',
|
||||||
print(
|
'KeySpec = typing.Tuple[int, bool, int]',
|
||||||
'# Update this file by running: python {}'.format(os.path.relpath(os.path.abspath(__file__))),
|
'KeyMap = typing.Dict[KeySpec, KeyAction]',
|
||||||
file=f
|
),
|
||||||
|
extra_fields=(
|
||||||
|
('keymap', 'KeyMap'),
|
||||||
|
('sequence_map', 'typing.Dict[KeySpec, KeyMap]'),
|
||||||
)
|
)
|
||||||
f.write(text)
|
)
|
||||||
|
save_type_stub(text, __file__)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|||||||
@@ -3,6 +3,9 @@ import kitty.rgb
|
|||||||
from kitty.rgb import Color
|
from kitty.rgb import Color
|
||||||
import typing
|
import typing
|
||||||
|
|
||||||
|
from kitty.config import KeyAction
|
||||||
|
KeySpec = typing.Tuple[int, bool, int]
|
||||||
|
KeyMap = typing.Dict[KeySpec, KeyAction]
|
||||||
|
|
||||||
class Options:
|
class Options:
|
||||||
font_family: str
|
font_family: str
|
||||||
@@ -381,3 +384,5 @@ class Options:
|
|||||||
kitty_mod: int
|
kitty_mod: int
|
||||||
clear_all_shortcuts: bool
|
clear_all_shortcuts: bool
|
||||||
kitten_alias: str
|
kitten_alias: str
|
||||||
|
keymap: KeyMap
|
||||||
|
sequence_map: typing.Dict[KeySpec, KeyMap]
|
||||||
@@ -138,13 +138,17 @@ cli_msg = (
|
|||||||
).format(appname=appname)
|
).format(appname=appname)
|
||||||
|
|
||||||
|
|
||||||
|
class RCOptions:
|
||||||
|
pass
|
||||||
|
|
||||||
|
|
||||||
def parse_rc_args(args):
|
def parse_rc_args(args):
|
||||||
cmds = (' :green:`{}`\n {}'.format(cmap[c].name, cmap[c].short_desc) for c in all_commands)
|
cmds = (' :green:`{}`\n {}'.format(cmap[c].name, cmap[c].short_desc) for c in all_commands)
|
||||||
msg = cli_msg + (
|
msg = cli_msg + (
|
||||||
'\n\n:title:`Commands`:\n{cmds}\n\n'
|
'\n\n:title:`Commands`:\n{cmds}\n\n'
|
||||||
'You can get help for each individual command by using:\n'
|
'You can get help for each individual command by using:\n'
|
||||||
'{appname} @ :italic:`command` -h').format(appname=appname, cmds='\n'.join(cmds))
|
'{appname} @ :italic:`command` -h').format(appname=appname, cmds='\n'.join(cmds))
|
||||||
return parse_args(args[1:], global_options_spec, 'command ...', msg, '{} @'.format(appname))
|
return parse_args(args[1:], global_options_spec, 'command ...', msg, '{} @'.format(appname), result_class=RCOptions)
|
||||||
|
|
||||||
|
|
||||||
def main(args):
|
def main(args):
|
||||||
|
|||||||
Reference in New Issue
Block a user