more typing work
This commit is contained in:
parent
8803eeb890
commit
fc0adfd965
39
kitty/cli.py
39
kitty/cli.py
@ -7,8 +7,8 @@ import re
|
|||||||
import sys
|
import sys
|
||||||
from collections import deque
|
from collections import deque
|
||||||
from typing import (
|
from typing import (
|
||||||
Any, Callable, Dict, FrozenSet, Iterator, List, Optional, Sequence, Tuple,
|
TYPE_CHECKING, Any, Callable, Dict, FrozenSet, Iterable, Iterator, List,
|
||||||
Type, TypeVar, Union, cast
|
Match, Optional, Sequence, Set, Tuple, Type, TypeVar, Union, cast
|
||||||
)
|
)
|
||||||
|
|
||||||
from .cli_stub import CLIOptions
|
from .cli_stub import CLIOptions
|
||||||
@ -30,6 +30,8 @@ try:
|
|||||||
except ImportError:
|
except ImportError:
|
||||||
OptionDict = Dict[str, Any] # type: ignore
|
OptionDict = Dict[str, Any] # type: ignore
|
||||||
|
|
||||||
|
if TYPE_CHECKING:
|
||||||
|
from .config import BadLine, KeyAction, KeySpec, SequenceMap # noqa
|
||||||
|
|
||||||
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
|
||||||
@ -197,9 +199,9 @@ def parse_option_spec(spec: Optional[str] = None) -> Tuple[OptionSpecSeq, Option
|
|||||||
def prettify(text: str) -> str:
|
def prettify(text: str) -> str:
|
||||||
role_map = globals()
|
role_map = globals()
|
||||||
|
|
||||||
def sub(m):
|
def sub(m: Match) -> str:
|
||||||
role, text = m.group(1, 2)
|
role, text = m.group(1, 2)
|
||||||
return role_map[role](text)
|
return str(role_map[role](text))
|
||||||
|
|
||||||
text = re.sub(r':([a-z]+):`([^`]+)`', sub, text)
|
text = re.sub(r':([a-z]+):`([^`]+)`', sub, text)
|
||||||
return text
|
return text
|
||||||
@ -280,7 +282,7 @@ class PrintHelpForSeq:
|
|||||||
blocks: List[str] = []
|
blocks: List[str] = []
|
||||||
a = blocks.append
|
a = blocks.append
|
||||||
|
|
||||||
def wa(text, indent=0, leading_indent=None):
|
def wa(text: str, indent: int = 0, leading_indent: Optional[int] = None) -> None:
|
||||||
if leading_indent is None:
|
if leading_indent is None:
|
||||||
leading_indent = indent
|
leading_indent = indent
|
||||||
j = '\n' + (' ' * indent)
|
j = '\n' + (' ' * indent)
|
||||||
@ -698,7 +700,7 @@ def options_for_completion() -> OptionSpecSeq:
|
|||||||
def option_spec_as_rst(
|
def option_spec_as_rst(
|
||||||
ospec: Callable[[], str] = options_spec,
|
ospec: Callable[[], str] = options_spec,
|
||||||
usage: Optional[str] = None, message: Optional[str] = None, appname: Optional[str] = None,
|
usage: Optional[str] = None, message: Optional[str] = None, appname: Optional[str] = None,
|
||||||
heading_char='-'
|
heading_char: str = '-'
|
||||||
) -> str:
|
) -> str:
|
||||||
options = parse_option_spec(ospec())
|
options = parse_option_spec(ospec())
|
||||||
seq, disabled = options
|
seq, disabled = options
|
||||||
@ -728,9 +730,10 @@ def parse_args(
|
|||||||
|
|
||||||
|
|
||||||
SYSTEM_CONF = '/etc/xdg/kitty/kitty.conf'
|
SYSTEM_CONF = '/etc/xdg/kitty/kitty.conf'
|
||||||
|
ShortcutMap = Dict[Tuple['KeySpec', ...], 'KeyAction']
|
||||||
|
|
||||||
|
|
||||||
def print_shortcut(key_sequence, action):
|
def print_shortcut(key_sequence: Iterable['KeySpec'], action: 'KeyAction') -> None:
|
||||||
if not getattr(print_shortcut, 'maps', None):
|
if not getattr(print_shortcut, 'maps', None):
|
||||||
from kitty.keys import defines
|
from kitty.keys import defines
|
||||||
v = vars(defines)
|
v = vars(defines)
|
||||||
@ -740,9 +743,9 @@ def print_shortcut(key_sequence, action):
|
|||||||
setattr(print_shortcut, 'maps', (mmap, krmap))
|
setattr(print_shortcut, 'maps', (mmap, krmap))
|
||||||
mmap, krmap = getattr(print_shortcut, 'maps')
|
mmap, krmap = getattr(print_shortcut, 'maps')
|
||||||
keys = []
|
keys = []
|
||||||
for key in key_sequence:
|
for key_spec in key_sequence:
|
||||||
names = []
|
names = []
|
||||||
mods, is_native, key = key
|
mods, is_native, key = key_spec
|
||||||
for name, val in mmap.items():
|
for name, val in mmap.items():
|
||||||
if mods & val:
|
if mods & val:
|
||||||
names.append(name)
|
names.append(name)
|
||||||
@ -758,7 +761,7 @@ def print_shortcut(key_sequence, action):
|
|||||||
print('\t', ' > '.join(keys), action)
|
print('\t', ' > '.join(keys), action)
|
||||||
|
|
||||||
|
|
||||||
def print_shortcut_changes(defns, text, changes):
|
def print_shortcut_changes(defns: ShortcutMap, text: str, changes: Set[Tuple['KeySpec', ...]]) -> None:
|
||||||
if changes:
|
if changes:
|
||||||
print(title(text))
|
print(title(text))
|
||||||
|
|
||||||
@ -766,7 +769,7 @@ def print_shortcut_changes(defns, text, changes):
|
|||||||
print_shortcut(k, defns[k])
|
print_shortcut(k, defns[k])
|
||||||
|
|
||||||
|
|
||||||
def compare_keymaps(final, initial):
|
def compare_keymaps(final: ShortcutMap, initial: ShortcutMap) -> None:
|
||||||
added = set(final) - set(initial)
|
added = set(final) - set(initial)
|
||||||
removed = set(initial) - set(final)
|
removed = set(initial) - set(final)
|
||||||
changed = {k for k in set(final) & set(initial) if final[k] != initial[k]}
|
changed = {k for k in set(final) & set(initial) if final[k] != initial[k]}
|
||||||
@ -775,11 +778,11 @@ def compare_keymaps(final, initial):
|
|||||||
print_shortcut_changes(final, 'Changed shortcuts:', changed)
|
print_shortcut_changes(final, 'Changed shortcuts:', changed)
|
||||||
|
|
||||||
|
|
||||||
def flatten_sequence_map(m):
|
def flatten_sequence_map(m: 'SequenceMap') -> ShortcutMap:
|
||||||
ans = {}
|
ans: Dict[Tuple['KeySpec', ...], 'KeyAction'] = {}
|
||||||
for k, rest_map in m.items():
|
for key_spec, rest_map in m.items():
|
||||||
for r, action in rest_map.items():
|
for r, action in rest_map.items():
|
||||||
ans[(k,) + (r)] = action
|
ans[(key_spec,) + (r)] = action
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
|
||||||
@ -797,15 +800,15 @@ def compare_opts(opts: OptionsStub) -> None:
|
|||||||
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: ShortcutMap = {(k,): v for k, v in final_.items()}
|
||||||
initial = {(k,): v for k, v in initial_.items()}
|
initial: ShortcutMap = {(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)
|
||||||
compare_keymaps(final, initial)
|
compare_keymaps(final, initial)
|
||||||
|
|
||||||
|
|
||||||
def create_opts(args: CLIOptions, debug_config=False, accumulate_bad_lines=None) -> OptionsStub:
|
def create_opts(args: CLIOptions, debug_config: bool = False, accumulate_bad_lines: Optional[List['BadLine']] = None) -> OptionsStub:
|
||||||
from .config import load_config
|
from .config import load_config
|
||||||
config = tuple(resolve_config(SYSTEM_CONF, defconf, args.config))
|
config = tuple(resolve_config(SYSTEM_CONF, defconf, args.config))
|
||||||
if debug_config:
|
if debug_config:
|
||||||
|
|||||||
195
kitty/config.py
195
kitty/config.py
@ -6,12 +6,11 @@ import json
|
|||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import sys
|
import sys
|
||||||
from collections import namedtuple
|
|
||||||
from contextlib import contextmanager, suppress
|
from contextlib import contextmanager, suppress
|
||||||
from functools import partial
|
from functools import partial
|
||||||
from typing import (
|
from typing import (
|
||||||
Any, Callable, Dict, Iterable, List, Optional,
|
Any, Callable, Dict, Generator, Iterable, List, Match, NamedTuple,
|
||||||
Sequence, Set, Tuple, Type
|
Optional, Sequence, Set, Tuple, Type, Union
|
||||||
)
|
)
|
||||||
|
|
||||||
from . import fast_data_types as defines
|
from . import fast_data_types as defines
|
||||||
@ -26,7 +25,6 @@ from .key_names import get_key_name_lookup, key_name_aliases
|
|||||||
from .options_stub import Options as OptionsStub
|
from .options_stub import Options as OptionsStub
|
||||||
from .utils import log_error
|
from .utils import log_error
|
||||||
|
|
||||||
|
|
||||||
KeySpec = Tuple[int, bool, int]
|
KeySpec = Tuple[int, bool, int]
|
||||||
KeyMap = Dict[KeySpec, 'KeyAction']
|
KeyMap = Dict[KeySpec, 'KeyAction']
|
||||||
KeySequence = Tuple[KeySpec, ...]
|
KeySequence = Tuple[KeySpec, ...]
|
||||||
@ -59,8 +57,13 @@ def parse_shortcut(sc: str) -> Tuple[int, bool, Optional[int]]:
|
|||||||
return mods, is_native, key
|
return mods, is_native, key
|
||||||
|
|
||||||
|
|
||||||
KeyAction = namedtuple('KeyAction', 'func args')
|
class KeyAction(NamedTuple):
|
||||||
|
func: str
|
||||||
|
args: Sequence[str]
|
||||||
|
|
||||||
|
|
||||||
func_with_args, args_funcs = key_func()
|
func_with_args, args_funcs = key_func()
|
||||||
|
FuncArgsType = Tuple[str, Sequence[Any]]
|
||||||
|
|
||||||
|
|
||||||
@func_with_args(
|
@func_with_args(
|
||||||
@ -68,12 +71,12 @@ func_with_args, args_funcs = key_func()
|
|||||||
'new_window_with_cwd', 'new_tab_with_cwd', 'new_os_window_with_cwd',
|
'new_window_with_cwd', 'new_tab_with_cwd', 'new_os_window_with_cwd',
|
||||||
'launch'
|
'launch'
|
||||||
)
|
)
|
||||||
def shlex_parse(func, rest):
|
def shlex_parse(func: str, rest: str) -> FuncArgsType:
|
||||||
return func, to_cmdline(rest)
|
return func, to_cmdline(rest)
|
||||||
|
|
||||||
|
|
||||||
@func_with_args('combine')
|
@func_with_args('combine')
|
||||||
def combine_parse(func, rest):
|
def combine_parse(func: str, rest: str) -> FuncArgsType:
|
||||||
sep, rest = rest.split(maxsplit=1)
|
sep, rest = rest.split(maxsplit=1)
|
||||||
parts = re.split(r'\s*' + re.escape(sep) + r'\s*', rest)
|
parts = re.split(r'\s*' + re.escape(sep) + r'\s*', rest)
|
||||||
args = tuple(map(parse_key_action, filter(None, parts)))
|
args = tuple(map(parse_key_action, filter(None, parts)))
|
||||||
@ -81,19 +84,21 @@ def combine_parse(func, rest):
|
|||||||
|
|
||||||
|
|
||||||
@func_with_args('send_text')
|
@func_with_args('send_text')
|
||||||
def send_text_parse(func, rest):
|
def send_text_parse(func: str, rest: str) -> FuncArgsType:
|
||||||
args = rest.split(maxsplit=1)
|
args = rest.split(maxsplit=1)
|
||||||
if len(args) > 0:
|
mode = ''
|
||||||
|
data = b''
|
||||||
|
if len(args) > 1:
|
||||||
|
mode = args[0]
|
||||||
try:
|
try:
|
||||||
args[1] = parse_send_text_bytes(args[1])
|
data = parse_send_text_bytes(args[1])
|
||||||
except Exception:
|
except Exception:
|
||||||
log_error('Ignoring invalid send_text string: ' + args[1])
|
log_error('Ignoring invalid send_text string: ' + args[1])
|
||||||
args[1] = ''
|
return func, [mode, data]
|
||||||
return func, args
|
|
||||||
|
|
||||||
|
|
||||||
@func_with_args('run_kitten', 'run_simple_kitten', 'kitten')
|
@func_with_args('run_kitten', 'run_simple_kitten', 'kitten')
|
||||||
def kitten_parse(func, rest):
|
def kitten_parse(func: str, rest: str) -> FuncArgsType:
|
||||||
if func == 'kitten':
|
if func == 'kitten':
|
||||||
args = rest.split(maxsplit=1)
|
args = rest.split(maxsplit=1)
|
||||||
else:
|
else:
|
||||||
@ -103,13 +108,13 @@ def kitten_parse(func, rest):
|
|||||||
|
|
||||||
|
|
||||||
@func_with_args('goto_tab')
|
@func_with_args('goto_tab')
|
||||||
def goto_tab_parse(func, rest):
|
def goto_tab_parse(func: str, rest: str) -> FuncArgsType:
|
||||||
args = (max(0, int(rest)), )
|
args = (max(0, int(rest)), )
|
||||||
return func, args
|
return func, args
|
||||||
|
|
||||||
|
|
||||||
@func_with_args('detach_window')
|
@func_with_args('detach_window')
|
||||||
def detach_window_parse(func, rest):
|
def detach_window_parse(func: str, rest: str) -> FuncArgsType:
|
||||||
if rest not in ('new', 'new-tab', 'ask'):
|
if rest not in ('new', 'new-tab', 'ask'):
|
||||||
log_error('Ignoring invalid detach_window argument: {}'.format(rest))
|
log_error('Ignoring invalid detach_window argument: {}'.format(rest))
|
||||||
rest = 'new'
|
rest = 'new'
|
||||||
@ -117,7 +122,7 @@ def detach_window_parse(func, rest):
|
|||||||
|
|
||||||
|
|
||||||
@func_with_args('detach_tab')
|
@func_with_args('detach_tab')
|
||||||
def detach_tab_parse(func, rest):
|
def detach_tab_parse(func: str, rest: str) -> FuncArgsType:
|
||||||
if rest not in ('new', 'ask'):
|
if rest not in ('new', 'ask'):
|
||||||
log_error('Ignoring invalid detach_tab argument: {}'.format(rest))
|
log_error('Ignoring invalid detach_tab argument: {}'.format(rest))
|
||||||
rest = 'new'
|
rest = 'new'
|
||||||
@ -125,12 +130,12 @@ def detach_tab_parse(func, rest):
|
|||||||
|
|
||||||
|
|
||||||
@func_with_args('set_background_opacity', 'goto_layout', 'kitty_shell')
|
@func_with_args('set_background_opacity', 'goto_layout', 'kitty_shell')
|
||||||
def simple_parse(func, rest):
|
def simple_parse(func: str, rest: str) -> FuncArgsType:
|
||||||
return func, [rest]
|
return func, [rest]
|
||||||
|
|
||||||
|
|
||||||
@func_with_args('set_font_size')
|
@func_with_args('set_font_size')
|
||||||
def float_parse(func, rest):
|
def float_parse(func: str, rest: str) -> FuncArgsType:
|
||||||
return func, (float(rest),)
|
return func, (float(rest),)
|
||||||
|
|
||||||
|
|
||||||
@ -150,28 +155,28 @@ def parse_change_font_size(func: str, rest: str) -> Tuple[str, Tuple[bool, Optio
|
|||||||
|
|
||||||
|
|
||||||
@func_with_args('clear_terminal')
|
@func_with_args('clear_terminal')
|
||||||
def clear_terminal(func, rest):
|
def clear_terminal(func: str, rest: str) -> FuncArgsType:
|
||||||
vals = rest.strip().split(maxsplit=1)
|
vals = rest.strip().split(maxsplit=1)
|
||||||
if len(vals) != 2:
|
if len(vals) != 2:
|
||||||
log_error('clear_terminal needs two arguments, using defaults')
|
log_error('clear_terminal needs two arguments, using defaults')
|
||||||
args = ['reset', 'active']
|
args: List[Union[str, bool]] = ['reset', 'active']
|
||||||
else:
|
else:
|
||||||
args = [vals[0].lower(), vals[1].lower() == 'active']
|
args = [vals[0].lower(), vals[1].lower() == 'active']
|
||||||
return func, args
|
return func, args
|
||||||
|
|
||||||
|
|
||||||
@func_with_args('copy_to_buffer')
|
@func_with_args('copy_to_buffer')
|
||||||
def copy_to_buffer(func, rest):
|
def copy_to_buffer(func: str, rest: str) -> FuncArgsType:
|
||||||
return func, [rest]
|
return func, [rest]
|
||||||
|
|
||||||
|
|
||||||
@func_with_args('paste_from_buffer')
|
@func_with_args('paste_from_buffer')
|
||||||
def paste_from_buffer(func, rest):
|
def paste_from_buffer(func: str, rest: str) -> FuncArgsType:
|
||||||
return func, [rest]
|
return func, [rest]
|
||||||
|
|
||||||
|
|
||||||
@func_with_args('neighboring_window')
|
@func_with_args('neighboring_window')
|
||||||
def neighboring_window(func, rest):
|
def neighboring_window(func: str, rest: str) -> FuncArgsType:
|
||||||
rest = rest.lower()
|
rest = rest.lower()
|
||||||
rest = {'up': 'top', 'down': 'bottom'}.get(rest, rest)
|
rest = {'up': 'top', 'down': 'bottom'}.get(rest, rest)
|
||||||
if rest not in ('left', 'right', 'top', 'bottom'):
|
if rest not in ('left', 'right', 'top', 'bottom'):
|
||||||
@ -181,7 +186,7 @@ def neighboring_window(func, rest):
|
|||||||
|
|
||||||
|
|
||||||
@func_with_args('resize_window')
|
@func_with_args('resize_window')
|
||||||
def resize_window(func, rest):
|
def resize_window(func: str, rest: str) -> FuncArgsType:
|
||||||
vals = rest.strip().split(maxsplit=1)
|
vals = rest.strip().split(maxsplit=1)
|
||||||
if len(vals) > 2:
|
if len(vals) > 2:
|
||||||
log_error('resize_window needs one or two arguments, using defaults')
|
log_error('resize_window needs one or two arguments, using defaults')
|
||||||
@ -202,39 +207,40 @@ def resize_window(func, rest):
|
|||||||
|
|
||||||
|
|
||||||
@func_with_args('move_window')
|
@func_with_args('move_window')
|
||||||
def move_window(func, rest):
|
def move_window(func: str, rest: str) -> FuncArgsType:
|
||||||
rest = rest.lower()
|
rest = rest.lower()
|
||||||
rest = {'up': 'top', 'down': 'bottom'}.get(rest, rest)
|
rest = {'up': 'top', 'down': 'bottom'}.get(rest, rest)
|
||||||
|
prest: Union[int, str] = rest
|
||||||
try:
|
try:
|
||||||
rest = int(rest)
|
prest = int(prest)
|
||||||
except Exception:
|
except Exception:
|
||||||
if rest not in ('left', 'right', 'top', 'bottom'):
|
if prest not in ('left', 'right', 'top', 'bottom'):
|
||||||
log_error('Invalid move_window specification: {}'.format(rest))
|
log_error('Invalid move_window specification: {}'.format(rest))
|
||||||
rest = 0
|
prest = 0
|
||||||
return func, [rest]
|
return func, [prest]
|
||||||
|
|
||||||
|
|
||||||
@func_with_args('pipe')
|
@func_with_args('pipe')
|
||||||
def pipe(func, rest):
|
def pipe(func: str, rest: str) -> FuncArgsType:
|
||||||
import shlex
|
import shlex
|
||||||
rest = shlex.split(rest)
|
r = shlex.split(rest)
|
||||||
if len(rest) < 3:
|
if len(r) < 3:
|
||||||
log_error('Too few arguments to pipe function')
|
log_error('Too few arguments to pipe function')
|
||||||
rest = ['none', 'none', 'true']
|
r = ['none', 'none', 'true']
|
||||||
return func, rest
|
return func, r
|
||||||
|
|
||||||
|
|
||||||
@func_with_args('set_colors')
|
@func_with_args('set_colors')
|
||||||
def set_colors(func, rest):
|
def set_colors(func: str, rest: str) -> FuncArgsType:
|
||||||
import shlex
|
import shlex
|
||||||
rest = shlex.split(rest)
|
r = shlex.split(rest)
|
||||||
if len(rest) < 1:
|
if len(r) < 1:
|
||||||
log_error('Too few arguments to set_colors function')
|
log_error('Too few arguments to set_colors function')
|
||||||
return func, rest
|
return func, r
|
||||||
|
|
||||||
|
|
||||||
@func_with_args('nth_window')
|
@func_with_args('nth_window')
|
||||||
def nth_window(func, rest):
|
def nth_window(func: str, rest: str) -> FuncArgsType:
|
||||||
try:
|
try:
|
||||||
num = int(rest)
|
num = int(rest)
|
||||||
except Exception:
|
except Exception:
|
||||||
@ -244,7 +250,7 @@ def nth_window(func, rest):
|
|||||||
|
|
||||||
|
|
||||||
@func_with_args('disable_ligatures_in')
|
@func_with_args('disable_ligatures_in')
|
||||||
def disable_ligatures_in(func, rest):
|
def disable_ligatures_in(func: str, rest: str) -> FuncArgsType:
|
||||||
parts = rest.split(maxsplit=1)
|
parts = rest.split(maxsplit=1)
|
||||||
if len(parts) == 1:
|
if len(parts) == 1:
|
||||||
where, strategy = 'active', parts[0]
|
where, strategy = 'active', parts[0]
|
||||||
@ -258,14 +264,14 @@ def disable_ligatures_in(func, rest):
|
|||||||
|
|
||||||
|
|
||||||
@func_with_args('layout_action')
|
@func_with_args('layout_action')
|
||||||
def layout_action(func, rest):
|
def layout_action(func: str, rest: str) -> FuncArgsType:
|
||||||
parts = rest.split(maxsplit=1)
|
parts = rest.split(maxsplit=1)
|
||||||
if not parts:
|
if not parts:
|
||||||
raise ValueError('layout_action must have at least one argument')
|
raise ValueError('layout_action must have at least one argument')
|
||||||
return func, [parts[0], tuple(parts[1:])]
|
return func, [parts[0], tuple(parts[1:])]
|
||||||
|
|
||||||
|
|
||||||
def parse_marker_spec(ftype, parts):
|
def parse_marker_spec(ftype: str, parts: Sequence[str]) -> Tuple[str, Union[str, Tuple[Tuple[int, str], ...]], int]:
|
||||||
flags = re.UNICODE
|
flags = re.UNICODE
|
||||||
if ftype in ('text', 'itext', 'regex', 'iregex'):
|
if ftype in ('text', 'itext', 'regex', 'iregex'):
|
||||||
if ftype.startswith('i'):
|
if ftype.startswith('i'):
|
||||||
@ -278,12 +284,12 @@ def parse_marker_spec(ftype, parts):
|
|||||||
color = max(1, min(int(parts[i]), 3))
|
color = max(1, min(int(parts[i]), 3))
|
||||||
except Exception:
|
except Exception:
|
||||||
raise ValueError('color {} in marker specification is not an integer'.format(parts[i]))
|
raise ValueError('color {} in marker specification is not an integer'.format(parts[i]))
|
||||||
spec = parts[i + 1]
|
sspec = parts[i + 1]
|
||||||
if 'regex' not in ftype:
|
if 'regex' not in ftype:
|
||||||
spec = re.escape(spec)
|
sspec = re.escape(sspec)
|
||||||
ans.append((color, spec))
|
ans.append((color, sspec))
|
||||||
ftype = 'regex'
|
ftype = 'regex'
|
||||||
spec = tuple(ans)
|
spec: Union[str, Tuple[Tuple[int, str], ...]] = tuple(ans)
|
||||||
elif ftype == 'function':
|
elif ftype == 'function':
|
||||||
spec = ' '.join(parts)
|
spec = ' '.join(parts)
|
||||||
else:
|
else:
|
||||||
@ -292,7 +298,7 @@ def parse_marker_spec(ftype, parts):
|
|||||||
|
|
||||||
|
|
||||||
@func_with_args('toggle_marker')
|
@func_with_args('toggle_marker')
|
||||||
def toggle_marker(func, rest):
|
def toggle_marker(func: str, rest: str) -> FuncArgsType:
|
||||||
parts = rest.split(maxsplit=1)
|
parts = rest.split(maxsplit=1)
|
||||||
if len(parts) != 2:
|
if len(parts) != 2:
|
||||||
raise ValueError('{} if not a valid marker specification'.format(rest))
|
raise ValueError('{} if not a valid marker specification'.format(rest))
|
||||||
@ -302,7 +308,7 @@ def toggle_marker(func, rest):
|
|||||||
|
|
||||||
|
|
||||||
@func_with_args('scroll_to_mark')
|
@func_with_args('scroll_to_mark')
|
||||||
def scroll_to_mark(func, rest):
|
def scroll_to_mark(func: str, rest: str) -> FuncArgsType:
|
||||||
parts = rest.split()
|
parts = rest.split()
|
||||||
if not parts or not rest:
|
if not parts or not rest:
|
||||||
return func, [True, 0]
|
return func, [True, 0]
|
||||||
@ -340,17 +346,17 @@ sequence_sep = '>'
|
|||||||
|
|
||||||
class KeyDefinition:
|
class KeyDefinition:
|
||||||
|
|
||||||
def __init__(self, is_sequence, action, mods, is_native, key, rest=()):
|
def __init__(self, is_sequence: bool, action: KeyAction, mods: int, is_native: bool, key: int, rest: Tuple[KeySpec, ...] = ()):
|
||||||
self.is_sequence = is_sequence
|
self.is_sequence = is_sequence
|
||||||
self.action = action
|
self.action = action
|
||||||
self.trigger = mods, is_native, key
|
self.trigger = mods, is_native, key
|
||||||
self.rest = rest
|
self.rest = rest
|
||||||
|
|
||||||
def resolve(self, kitty_mod):
|
def resolve(self, kitty_mod: int) -> None:
|
||||||
self.trigger = defines.resolve_key_mods(kitty_mod, self.trigger[0]), self.trigger[1], self.trigger[2]
|
self.trigger = defines.resolve_key_mods(kitty_mod, self.trigger[0]), self.trigger[1], self.trigger[2]
|
||||||
self.rest = tuple((defines.resolve_key_mods(kitty_mod, mods), is_native, key) for mods, is_native, key in self.rest)
|
self.rest = tuple((defines.resolve_key_mods(kitty_mod, mods), is_native, key) for mods, is_native, key in self.rest)
|
||||||
|
|
||||||
def resolve_kitten_aliases(self, aliases: Dict[str, Sequence[str]]):
|
def resolve_kitten_aliases(self, aliases: Dict[str, Sequence[str]]) -> None:
|
||||||
if not self.action.args:
|
if not self.action.args:
|
||||||
return
|
return
|
||||||
kitten = self.action.args[0]
|
kitten = self.action.args[0]
|
||||||
@ -366,7 +372,7 @@ class KeyDefinition:
|
|||||||
self.action = self.action._replace(args=[kitten + (' ' + rest).rstrip()])
|
self.action = self.action._replace(args=[kitten + (' ' + rest).rstrip()])
|
||||||
|
|
||||||
|
|
||||||
def parse_key(val, key_definitions):
|
def parse_key(val: str, key_definitions: List[KeyDefinition]) -> None:
|
||||||
parts = val.split(maxsplit=1)
|
parts = val.split(maxsplit=1)
|
||||||
if len(parts) != 2:
|
if len(parts) != 2:
|
||||||
return
|
return
|
||||||
@ -413,14 +419,15 @@ def parse_key(val, key_definitions):
|
|||||||
if trigger is not None:
|
if trigger is not None:
|
||||||
key_definitions.append(KeyDefinition(True, paction, trigger[0], trigger[1], trigger[2], rest))
|
key_definitions.append(KeyDefinition(True, paction, trigger[0], trigger[1], trigger[2], rest))
|
||||||
else:
|
else:
|
||||||
|
assert key is not None
|
||||||
key_definitions.append(KeyDefinition(False, paction, mods, is_native, key))
|
key_definitions.append(KeyDefinition(False, paction, mods, is_native, key))
|
||||||
|
|
||||||
|
|
||||||
def parse_symbol_map(val):
|
def parse_symbol_map(val: str) -> Dict[Tuple[int, int], str]:
|
||||||
parts = val.split()
|
parts = val.split()
|
||||||
symbol_map = {}
|
symbol_map: Dict[Tuple[int, int], str] = {}
|
||||||
|
|
||||||
def abort():
|
def abort() -> Dict[Tuple[int, int], str]:
|
||||||
log_error('Symbol map: {} is invalid, ignoring'.format(
|
log_error('Symbol map: {} is invalid, ignoring'.format(
|
||||||
val))
|
val))
|
||||||
return {}
|
return {}
|
||||||
@ -429,17 +436,16 @@ def parse_symbol_map(val):
|
|||||||
return abort()
|
return abort()
|
||||||
family = ' '.join(parts[1:])
|
family = ' '.join(parts[1:])
|
||||||
|
|
||||||
def to_chr(x):
|
def to_chr(x: str) -> int:
|
||||||
if not x.startswith('U+'):
|
if not x.startswith('U+'):
|
||||||
raise ValueError()
|
raise ValueError()
|
||||||
x = int(x[2:], 16)
|
return int(x[2:], 16)
|
||||||
return x
|
|
||||||
|
|
||||||
for x in parts[0].split(','):
|
for x in parts[0].split(','):
|
||||||
a, b = x.partition('-')[::2]
|
a_, b_ = x.partition('-')[::2]
|
||||||
b = b or a
|
b_ = b_ or a_
|
||||||
try:
|
try:
|
||||||
a, b = map(to_chr, (a, b))
|
a, b = map(to_chr, (a_, b_))
|
||||||
except Exception:
|
except Exception:
|
||||||
return abort()
|
return abort()
|
||||||
if b < a or max(a, b) > sys.maxunicode or min(a, b) < 1:
|
if b < a or max(a, b) > sys.maxunicode or min(a, b) < 1:
|
||||||
@ -448,24 +454,23 @@ def parse_symbol_map(val):
|
|||||||
return symbol_map
|
return symbol_map
|
||||||
|
|
||||||
|
|
||||||
def parse_send_text_bytes(text):
|
def parse_send_text_bytes(text: str) -> bytes:
|
||||||
return python_string(text).encode('utf-8')
|
return python_string(text).encode('utf-8')
|
||||||
|
|
||||||
|
|
||||||
def parse_send_text(val, key_definitions):
|
def parse_send_text(val: str, key_definitions: List[KeyDefinition]) -> None:
|
||||||
parts = val.split(' ')
|
parts = val.split(' ')
|
||||||
|
|
||||||
def abort(msg):
|
def abort(msg: str) -> None:
|
||||||
log_error('Send text: {} is invalid ({}), ignoring'.format(
|
log_error('Send text: {} is invalid ({}), ignoring'.format(
|
||||||
val, msg))
|
val, msg))
|
||||||
return {}
|
|
||||||
|
|
||||||
if len(parts) < 3:
|
if len(parts) < 3:
|
||||||
return abort('Incomplete')
|
return abort('Incomplete')
|
||||||
mode, sc = parts[:2]
|
mode, sc = parts[:2]
|
||||||
text = ' '.join(parts[2:])
|
text = ' '.join(parts[2:])
|
||||||
key_str = '{} send_text {} {}'.format(sc, mode, text)
|
key_str = '{} send_text {} {}'.format(sc, mode, text)
|
||||||
return parse_key(key_str, key_definitions)
|
parse_key(key_str, key_definitions)
|
||||||
|
|
||||||
|
|
||||||
SpecialHandlerFunc = Callable[[str, str, Dict[str, Any]], None]
|
SpecialHandlerFunc = Callable[[str, str, Dict[str, Any]], None]
|
||||||
@ -486,25 +491,25 @@ def deprecated_handler(*names: str) -> Callable[[SpecialHandlerFunc], SpecialHan
|
|||||||
|
|
||||||
|
|
||||||
@special_handler
|
@special_handler
|
||||||
def handle_map(key, val, ans):
|
def handle_map(key: str, val: str, ans: Dict[str, Any]) -> None:
|
||||||
parse_key(val, ans['key_definitions'])
|
parse_key(val, ans['key_definitions'])
|
||||||
|
|
||||||
|
|
||||||
@special_handler
|
@special_handler
|
||||||
def handle_symbol_map(key, val, ans):
|
def handle_symbol_map(key: str, val: str, ans: Dict[str, Any]) -> None:
|
||||||
ans['symbol_map'].update(parse_symbol_map(val))
|
ans['symbol_map'].update(parse_symbol_map(val))
|
||||||
|
|
||||||
|
|
||||||
class FontFeature(str):
|
class FontFeature(str):
|
||||||
|
|
||||||
def __new__(cls, name: str, parsed: bytes):
|
def __new__(cls, name: str, parsed: bytes) -> 'FontFeature':
|
||||||
ans = str.__new__(cls, name) # type: ignore
|
ans: FontFeature = str.__new__(cls, name) # type: ignore
|
||||||
ans.parsed = parsed
|
ans.parsed = parsed # type: ignore
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
|
||||||
@special_handler
|
@special_handler
|
||||||
def handle_font_features(key, val, ans):
|
def handle_font_features(key: str, val: str, ans: Dict[str, Any]) -> None:
|
||||||
if val != 'none':
|
if val != 'none':
|
||||||
parts = val.split()
|
parts = val.split()
|
||||||
if len(parts) < 2:
|
if len(parts) < 2:
|
||||||
@ -523,26 +528,26 @@ def handle_font_features(key, val, ans):
|
|||||||
|
|
||||||
|
|
||||||
@special_handler
|
@special_handler
|
||||||
def handle_kitten_alias(key, val, ans):
|
def handle_kitten_alias(key: str, val: str, ans: Dict[str, Any]) -> None:
|
||||||
parts = val.split(maxsplit=2)
|
parts = val.split(maxsplit=2)
|
||||||
if len(parts) >= 2:
|
if len(parts) >= 2:
|
||||||
ans['kitten_aliases'][parts[0]] = parts[1:]
|
ans['kitten_aliases'][parts[0]] = parts[1:]
|
||||||
|
|
||||||
|
|
||||||
@special_handler
|
@special_handler
|
||||||
def handle_send_text(key, val, ans):
|
def handle_send_text(key: str, val: str, ans: Dict[str, Any]) -> None:
|
||||||
# For legacy compatibility
|
# For legacy compatibility
|
||||||
parse_send_text(val, ans['key_definitions'])
|
parse_send_text(val, ans['key_definitions'])
|
||||||
|
|
||||||
|
|
||||||
@special_handler
|
@special_handler
|
||||||
def handle_clear_all_shortcuts(key, val, ans):
|
def handle_clear_all_shortcuts(key: str, val: str, ans: Dict[str, Any]) -> None:
|
||||||
if to_bool(val):
|
if to_bool(val):
|
||||||
ans['key_definitions'] = [None]
|
ans['key_definitions'] = [None]
|
||||||
|
|
||||||
|
|
||||||
@deprecated_handler('x11_hide_window_decorations', 'macos_hide_titlebar')
|
@deprecated_handler('x11_hide_window_decorations', 'macos_hide_titlebar')
|
||||||
def handle_deprecated_hide_window_decorations_aliases(key, val, ans):
|
def handle_deprecated_hide_window_decorations_aliases(key: str, val: str, ans: Dict[str, Any]) -> None:
|
||||||
if not hasattr(handle_deprecated_hide_window_decorations_aliases, key):
|
if not hasattr(handle_deprecated_hide_window_decorations_aliases, key):
|
||||||
setattr(handle_deprecated_hide_window_decorations_aliases, 'key', True)
|
setattr(handle_deprecated_hide_window_decorations_aliases, 'key', True)
|
||||||
log_error('The option {} is deprecated. Use hide_window_decorations instead.'.format(key))
|
log_error('The option {} is deprecated. Use hide_window_decorations instead.'.format(key))
|
||||||
@ -552,7 +557,7 @@ def handle_deprecated_hide_window_decorations_aliases(key, val, ans):
|
|||||||
|
|
||||||
|
|
||||||
@deprecated_handler('macos_show_window_title_in_menubar')
|
@deprecated_handler('macos_show_window_title_in_menubar')
|
||||||
def handle_deprecated_macos_show_window_title_in_menubar_alias(key, val, ans):
|
def handle_deprecated_macos_show_window_title_in_menubar_alias(key: str, val: str, ans: Dict[str, Any]) -> None:
|
||||||
if not hasattr(handle_deprecated_macos_show_window_title_in_menubar_alias, key):
|
if not hasattr(handle_deprecated_macos_show_window_title_in_menubar_alias, key):
|
||||||
setattr(handle_deprecated_macos_show_window_title_in_menubar_alias, 'key', True)
|
setattr(handle_deprecated_macos_show_window_title_in_menubar_alias, 'key', True)
|
||||||
log_error('The option {} is deprecated. Use macos_show_window_title_in menubar instead.'.format(key))
|
log_error('The option {} is deprecated. Use macos_show_window_title_in menubar instead.'.format(key))
|
||||||
@ -570,9 +575,9 @@ def handle_deprecated_macos_show_window_title_in_menubar_alias(key, val, ans):
|
|||||||
ans['macos_show_window_title_in'] = macos_show_window_title_in
|
ans['macos_show_window_title_in'] = macos_show_window_title_in
|
||||||
|
|
||||||
|
|
||||||
def expandvars(val, env):
|
def expandvars(val: str, env: Dict[str, str]) -> str:
|
||||||
|
|
||||||
def sub(m):
|
def sub(m: Match) -> str:
|
||||||
key = m.group(1)
|
key = m.group(1)
|
||||||
result = env.get(key)
|
result = env.get(key)
|
||||||
if result is None:
|
if result is None:
|
||||||
@ -585,25 +590,25 @@ def expandvars(val, env):
|
|||||||
|
|
||||||
|
|
||||||
@special_handler
|
@special_handler
|
||||||
def handle_env(key, val, ans):
|
def handle_env(key: str, val: str, ans: Dict[str, Any]) -> None:
|
||||||
key, val = val.partition('=')[::2]
|
key, val = val.partition('=')[::2]
|
||||||
key, val = key.strip(), val.strip()
|
key, val = key.strip(), val.strip()
|
||||||
ans['env'][key] = expandvars(val, ans['env'])
|
ans['env'][key] = expandvars(val, ans['env'])
|
||||||
|
|
||||||
|
|
||||||
def special_handling(key, val, ans):
|
def special_handling(key: str, val: str, ans: Dict[str, Any]) -> bool:
|
||||||
func = special_handlers.get(key)
|
func = special_handlers.get(key)
|
||||||
if func is not None:
|
if func is not None:
|
||||||
func(key, val, ans)
|
func(key, val, ans)
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
|
||||||
def option_names_for_completion():
|
def option_names_for_completion() -> Generator[str, None, None]:
|
||||||
yield from defaults
|
yield from defaults
|
||||||
yield from special_handlers
|
yield from special_handlers
|
||||||
|
|
||||||
|
|
||||||
def parse_config(lines: Iterable[str], check_keys=True, accumulate_bad_lines: Optional[List[BadLine]] = None):
|
def parse_config(lines: Iterable[str], check_keys: bool = True, accumulate_bad_lines: Optional[List[BadLine]] = None) -> Dict[str, Any]:
|
||||||
ans: Dict[str, Any] = {
|
ans: Dict[str, Any] = {
|
||||||
'symbol_map': {}, 'keymap': {}, 'sequence_map': {}, 'key_definitions': [],
|
'symbol_map': {}, 'keymap': {}, 'sequence_map': {}, 'key_definitions': [],
|
||||||
'env': {}, 'kitten_aliases': {}, 'font_features': {}
|
'env': {}, 'kitten_aliases': {}, 'font_features': {}
|
||||||
@ -624,7 +629,7 @@ def parse_config(lines: Iterable[str], check_keys=True, accumulate_bad_lines: Op
|
|||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
|
||||||
def parse_defaults(lines, check_keys=False):
|
def parse_defaults(lines: Iterable[str], check_keys: bool = False) -> Dict[str, Any]:
|
||||||
ans = parse_config(lines, check_keys)
|
ans = parse_config(lines, check_keys)
|
||||||
return ans
|
return ans
|
||||||
|
|
||||||
@ -652,18 +657,18 @@ def merge_configs(defaults: Dict, vals: Dict) -> Dict:
|
|||||||
return ans
|
return ans
|
||||||
|
|
||||||
|
|
||||||
def build_ansi_color_table(opts=defaults):
|
def build_ansi_color_table(opts: OptionsStub = defaults) -> List[int]:
|
||||||
|
|
||||||
def as_int(x):
|
def as_int(x: Tuple[int, int, int]) -> int:
|
||||||
return (x[0] << 16) | (x[1] << 8) | x[2]
|
return (x[0] << 16) | (x[1] << 8) | x[2]
|
||||||
|
|
||||||
def col(i):
|
def col(i: int) -> int:
|
||||||
return as_int(getattr(opts, 'color{}'.format(i)))
|
return as_int(getattr(opts, 'color{}'.format(i)))
|
||||||
|
|
||||||
return list(map(col, range(256)))
|
return list(map(col, range(256)))
|
||||||
|
|
||||||
|
|
||||||
def atomic_save(data, path):
|
def atomic_save(data: bytes, path: str) -> None:
|
||||||
import tempfile
|
import tempfile
|
||||||
fd, p = tempfile.mkstemp(dir=os.path.dirname(path), suffix='.tmp')
|
fd, p = tempfile.mkstemp(dir=os.path.dirname(path), suffix='.tmp')
|
||||||
try:
|
try:
|
||||||
@ -681,7 +686,7 @@ def atomic_save(data, path):
|
|||||||
|
|
||||||
|
|
||||||
@contextmanager
|
@contextmanager
|
||||||
def cached_values_for(name):
|
def cached_values_for(name: str) -> Generator[Dict, None, None]:
|
||||||
cached_path = os.path.join(cache_dir(), name + '.json')
|
cached_path = os.path.join(cache_dir(), name + '.json')
|
||||||
cached_values: Dict = {}
|
cached_values: Dict = {}
|
||||||
try:
|
try:
|
||||||
@ -703,14 +708,14 @@ def cached_values_for(name):
|
|||||||
err))
|
err))
|
||||||
|
|
||||||
|
|
||||||
def initial_window_size_func(opts, cached_values):
|
def initial_window_size_func(opts: OptionsStub, cached_values: Dict) -> Callable[[int, int, float, float, float, float], Tuple[int, int]]:
|
||||||
|
|
||||||
if 'window-size' in cached_values and opts.remember_window_size:
|
if 'window-size' in cached_values and opts.remember_window_size:
|
||||||
ws = cached_values['window-size']
|
ws = cached_values['window-size']
|
||||||
try:
|
try:
|
||||||
w, h = map(int, ws)
|
w, h = map(int, ws)
|
||||||
|
|
||||||
def initial_window_size(*a):
|
def initial_window_size(*a: Any) -> Tuple[int, int]:
|
||||||
return w, h
|
return w, h
|
||||||
return initial_window_size
|
return initial_window_size
|
||||||
except Exception:
|
except Exception:
|
||||||
@ -719,7 +724,7 @@ def initial_window_size_func(opts, cached_values):
|
|||||||
w, w_unit = opts.initial_window_width
|
w, w_unit = opts.initial_window_width
|
||||||
h, h_unit = opts.initial_window_height
|
h, h_unit = opts.initial_window_height
|
||||||
|
|
||||||
def get_window_size(cell_width, cell_height, dpi_x, dpi_y, xscale, yscale):
|
def get_window_size(cell_width: int, cell_height: int, dpi_x: float, dpi_y: float, xscale: float, yscale: float) -> Tuple[int, int]:
|
||||||
if not is_macos:
|
if not is_macos:
|
||||||
# scaling is not needed on Wayland, but is needed on macOS. Not
|
# scaling is not needed on Wayland, but is needed on macOS. Not
|
||||||
# sure about X11.
|
# sure about X11.
|
||||||
@ -732,12 +737,12 @@ def initial_window_size_func(opts, cached_values):
|
|||||||
height = cell_height * h / yscale + (dpi_y / 72) * (opts.window_margin_width + opts.window_padding_width) + 1
|
height = cell_height * h / yscale + (dpi_y / 72) * (opts.window_margin_width + opts.window_padding_width) + 1
|
||||||
else:
|
else:
|
||||||
height = h
|
height = h
|
||||||
return width, height
|
return int(width), int(height)
|
||||||
|
|
||||||
return get_window_size
|
return get_window_size
|
||||||
|
|
||||||
|
|
||||||
def commented_out_default_config():
|
def commented_out_default_config() -> str:
|
||||||
ans = []
|
ans = []
|
||||||
for line in as_conf_file(all_options.values()):
|
for line in as_conf_file(all_options.values()):
|
||||||
if line and line[0] != '#':
|
if line and line[0] != '#':
|
||||||
@ -746,7 +751,7 @@ def commented_out_default_config():
|
|||||||
return '\n'.join(ans)
|
return '\n'.join(ans)
|
||||||
|
|
||||||
|
|
||||||
def prepare_config_file_for_editing():
|
def prepare_config_file_for_editing() -> str:
|
||||||
if not os.path.exists(defconf):
|
if not os.path.exists(defconf):
|
||||||
d = os.path.dirname(defconf)
|
d = os.path.dirname(defconf)
|
||||||
with suppress(FileExistsError):
|
with suppress(FileExistsError):
|
||||||
|
|||||||
@ -27,7 +27,7 @@ warn_unused_configs = True
|
|||||||
check_untyped_defs = True
|
check_untyped_defs = True
|
||||||
# disallow_untyped_defs = True
|
# disallow_untyped_defs = True
|
||||||
|
|
||||||
[mypy-kitty.rc.*,kitty.conf.*,kitty.fonts.*,kitty.launch,kitty.child]
|
[mypy-kitty.rc.*,kitty.conf.*,kitty.fonts.*,kitty.launch,kitty.child,kitty.cli,kitty.config]
|
||||||
disallow_untyped_defs = True
|
disallow_untyped_defs = True
|
||||||
|
|
||||||
[mypy-conf]
|
[mypy-conf]
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user