Refactor single key config parsing to use a special type
This commit is contained in:
parent
31cb68840a
commit
b94d2b27f4
@ -25,7 +25,8 @@ from .config import (
|
||||
)
|
||||
from .config_data import MINIMUM_FONT_SIZE
|
||||
from .constants import (
|
||||
appname, config_dir, is_macos, kitty_exe, supports_primary_selection
|
||||
SingleKey, appname, config_dir, is_macos, kitty_exe,
|
||||
supports_primary_selection
|
||||
)
|
||||
from .fast_data_types import (
|
||||
CLOSE_BEING_CONFIRMED, IMPERATIVE_CLOSE_REQUESTED, NO_CLOSE_REQUESTED,
|
||||
@ -140,7 +141,7 @@ class Boss:
|
||||
opts: Options,
|
||||
args: CLIOptions,
|
||||
cached_values: Dict[str, Any],
|
||||
new_os_window_trigger: Optional[Tuple[int, bool, int]]
|
||||
new_os_window_trigger: Optional[SingleKey]
|
||||
):
|
||||
set_layout_options(opts)
|
||||
self.clipboard_buffers: Dict[str, str] = {}
|
||||
|
||||
12
kitty/cli.py
12
kitty/cli.py
@ -14,9 +14,9 @@ from typing import (
|
||||
from .cli_stub import CLIOptions
|
||||
from .conf.utils import resolve_config
|
||||
from .config import KeyAction
|
||||
from .constants import appname, defconf, is_macos, is_wayland, str_version
|
||||
from .constants import appname, defconf, is_macos, is_wayland, str_version, SingleKey
|
||||
from .options_stub import Options as OptionsStub
|
||||
from .typing import BadLineType, KeySpec, SequenceMap, TypedDict
|
||||
from .typing import BadLineType, SequenceMap, TypedDict
|
||||
|
||||
|
||||
class OptionDict(TypedDict):
|
||||
@ -755,10 +755,10 @@ def parse_args(
|
||||
|
||||
|
||||
SYSTEM_CONF = '/etc/xdg/kitty/kitty.conf'
|
||||
ShortcutMap = Dict[Tuple[KeySpec, ...], KeyAction]
|
||||
ShortcutMap = Dict[Tuple[SingleKey, ...], KeyAction]
|
||||
|
||||
|
||||
def print_shortcut(key_sequence: Iterable[KeySpec], action: KeyAction) -> None:
|
||||
def print_shortcut(key_sequence: Iterable[SingleKey], action: KeyAction) -> None:
|
||||
if not getattr(print_shortcut, 'maps', None):
|
||||
from kitty.keys import defines
|
||||
v = vars(defines)
|
||||
@ -786,7 +786,7 @@ def print_shortcut(key_sequence: Iterable[KeySpec], action: KeyAction) -> None:
|
||||
print('\t', ' > '.join(keys), action)
|
||||
|
||||
|
||||
def print_shortcut_changes(defns: ShortcutMap, text: str, changes: Set[Tuple[KeySpec, ...]]) -> None:
|
||||
def print_shortcut_changes(defns: ShortcutMap, text: str, changes: Set[Tuple[SingleKey, ...]]) -> None:
|
||||
if changes:
|
||||
print(title(text))
|
||||
|
||||
@ -804,7 +804,7 @@ def compare_keymaps(final: ShortcutMap, initial: ShortcutMap) -> None:
|
||||
|
||||
|
||||
def flatten_sequence_map(m: SequenceMap) -> ShortcutMap:
|
||||
ans: Dict[Tuple[KeySpec, ...], KeyAction] = {}
|
||||
ans: Dict[Tuple[SingleKey, ...], KeyAction] = {}
|
||||
for key_spec, rest_map in m.items():
|
||||
for r, action in rest_map.items():
|
||||
ans[(key_spec,) + (r)] = action
|
||||
|
||||
@ -19,44 +19,17 @@ from .conf.utils import (
|
||||
BadLine, init_config, key_func, load_config as _load_config, merge_dicts,
|
||||
parse_config_base, python_string, to_bool, to_cmdline
|
||||
)
|
||||
from .config_data import all_options, parse_mods, type_convert
|
||||
from .constants import cache_dir, defconf, is_macos
|
||||
from .config_data import InvalidMods, all_options, parse_shortcut, type_convert
|
||||
from .constants import SingleKey, cache_dir, defconf, is_macos
|
||||
from .fonts import FontFeature
|
||||
from .key_names import get_key_name_lookup, key_name_aliases
|
||||
from .options_stub import Options as OptionsStub
|
||||
from .typing import TypedDict
|
||||
from .utils import expandvars, log_error
|
||||
|
||||
KeySpec = Tuple[int, bool, int]
|
||||
KeyMap = Dict[KeySpec, 'KeyAction']
|
||||
KeySequence = Tuple[KeySpec, ...]
|
||||
KeyMap = Dict[SingleKey, 'KeyAction']
|
||||
KeySequence = Tuple[SingleKey, ...]
|
||||
SubSequenceMap = Dict[KeySequence, 'KeyAction']
|
||||
SequenceMap = Dict[KeySpec, SubSequenceMap]
|
||||
|
||||
|
||||
class InvalidMods(ValueError):
|
||||
pass
|
||||
|
||||
|
||||
def parse_shortcut(sc: str) -> Tuple[int, bool, Optional[int]]:
|
||||
parts = sc.split('+')
|
||||
mods = 0
|
||||
if len(parts) > 1:
|
||||
mods = parse_mods(parts[:-1], sc) or 0
|
||||
if not mods:
|
||||
raise InvalidMods('Invalid shortcut')
|
||||
q = parts[-1].upper()
|
||||
key: Optional[int] = getattr(defines, 'GLFW_KEY_' + key_name_aliases.get(q, q), None)
|
||||
is_native = False
|
||||
if key is None:
|
||||
q = parts[-1]
|
||||
if q.startswith('0x'):
|
||||
with suppress(Exception):
|
||||
key = int(q, 16)
|
||||
else:
|
||||
key = get_key_name_lookup()(q, False)
|
||||
is_native = key is not None
|
||||
return mods, is_native, key
|
||||
SequenceMap = Dict[SingleKey, SubSequenceMap]
|
||||
|
||||
|
||||
class KeyAction(NamedTuple):
|
||||
@ -371,15 +344,22 @@ sequence_sep = '>'
|
||||
|
||||
class KeyDefinition:
|
||||
|
||||
def __init__(self, is_sequence: bool, action: KeyAction, mods: int, is_native: bool, key: int, rest: Tuple[KeySpec, ...] = ()):
|
||||
def __init__(self, is_sequence: bool, action: KeyAction, mods: int, is_native: bool, key: int, rest: Tuple[SingleKey, ...] = ()):
|
||||
self.is_sequence = is_sequence
|
||||
self.action = action
|
||||
self.trigger = mods, is_native, key
|
||||
self.trigger = SingleKey(mods, is_native, key)
|
||||
self.rest = rest
|
||||
|
||||
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.rest = tuple((defines.resolve_key_mods(kitty_mod, mods), is_native, key) for mods, is_native, key in self.rest)
|
||||
|
||||
def r(k: SingleKey) -> SingleKey:
|
||||
mods = defines.resolve_key_mods(kitty_mod, k.mods)
|
||||
key = k.key
|
||||
is_native = k.is_native
|
||||
return SingleKey(mods, is_native, key)
|
||||
|
||||
self.trigger = r(self.trigger)
|
||||
self.rest = tuple(map(r, self.rest))
|
||||
|
||||
def resolve_kitten_aliases(self, aliases: Dict[str, Sequence[str]]) -> None:
|
||||
if not self.action.args:
|
||||
@ -407,28 +387,28 @@ def parse_key(val: str, key_definitions: List[KeyDefinition]) -> None:
|
||||
return
|
||||
is_sequence = sequence_sep in sc
|
||||
if is_sequence:
|
||||
trigger: Optional[Tuple[int, bool, int]] = None
|
||||
restl: List[Tuple[int, bool, int]] = []
|
||||
trigger: Optional[SingleKey] = None
|
||||
restl: List[SingleKey] = []
|
||||
for part in sc.split(sequence_sep):
|
||||
try:
|
||||
mods, is_native, key = parse_shortcut(part)
|
||||
except InvalidMods:
|
||||
return
|
||||
if key is None:
|
||||
if key is defines.GLFW_KEY_UNKNOWN:
|
||||
if mods is not None:
|
||||
log_error('Shortcut: {} has unknown key, ignoring'.format(sc))
|
||||
return
|
||||
if trigger is None:
|
||||
trigger = mods, is_native, key
|
||||
trigger = SingleKey(mods, is_native, key)
|
||||
else:
|
||||
restl.append((mods, is_native, key))
|
||||
restl.append(SingleKey(mods, is_native, key))
|
||||
rest = tuple(restl)
|
||||
else:
|
||||
try:
|
||||
mods, is_native, key = parse_shortcut(sc)
|
||||
except InvalidMods:
|
||||
return
|
||||
if key is None:
|
||||
if key is defines.GLFW_KEY_UNKNOWN:
|
||||
if mods is not None:
|
||||
log_error('Shortcut: {} has unknown key, ignoring'.format(sc))
|
||||
return
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
|
||||
# Utils {{{
|
||||
import os
|
||||
from contextlib import suppress
|
||||
from gettext import gettext as _
|
||||
from typing import (
|
||||
Any, Callable, Dict, FrozenSet, Iterable, List, Optional, Sequence, Set,
|
||||
@ -16,15 +17,21 @@ from .conf.utils import (
|
||||
choices, positive_float, positive_int, to_bool, to_cmdline as tc, to_color,
|
||||
to_color_or_none, unit_float
|
||||
)
|
||||
from .constants import FloatEdges, config_dir, is_macos
|
||||
from .constants import (
|
||||
FloatEdges, SingleKey, config_dir, is_macos
|
||||
)
|
||||
from .fast_data_types import CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE
|
||||
from .key_names import get_key_name_lookup, key_name_aliases
|
||||
from .layout.interface import all_layouts
|
||||
from .rgb import Color, color_as_int, color_as_sharp, color_from_int
|
||||
from .utils import log_error
|
||||
|
||||
|
||||
class InvalidMods(ValueError):
|
||||
pass
|
||||
|
||||
|
||||
MINIMUM_FONT_SIZE = 4
|
||||
|
||||
|
||||
mod_map = {'CTRL': 'CONTROL', 'CMD': 'SUPER', '⌘': 'SUPER',
|
||||
'⌥': 'ALT', 'OPTION': 'ALT', 'KITTY_MOD': 'KITTY'}
|
||||
|
||||
@ -53,6 +60,27 @@ def to_modifiers(val: str) -> int:
|
||||
return parse_mods(val.split('+'), val) or 0
|
||||
|
||||
|
||||
def parse_shortcut(sc: str) -> SingleKey:
|
||||
parts = sc.split('+')
|
||||
mods = 0
|
||||
if len(parts) > 1:
|
||||
mods = parse_mods(parts[:-1], sc) or 0
|
||||
if not mods:
|
||||
raise InvalidMods('Invalid shortcut')
|
||||
q = parts[-1].upper()
|
||||
key: Optional[int] = getattr(defines, 'GLFW_KEY_' + key_name_aliases.get(q, q), None)
|
||||
is_native = False
|
||||
if key is None:
|
||||
q = parts[-1]
|
||||
if q.startswith('0x'):
|
||||
with suppress(Exception):
|
||||
key = int(q, 16)
|
||||
else:
|
||||
key = get_key_name_lookup()(q, False)
|
||||
is_native = key is not None
|
||||
return SingleKey(mods, is_native, key or defines.GLFW_KEY_UNKNOWN)
|
||||
|
||||
|
||||
T = TypeVar('T')
|
||||
|
||||
|
||||
|
||||
@ -60,6 +60,12 @@ class WindowGeometry(NamedTuple):
|
||||
spaces: Edges = Edges()
|
||||
|
||||
|
||||
class SingleKey(NamedTuple):
|
||||
mods: int = 0
|
||||
is_native: bool = False
|
||||
key: int = -1
|
||||
|
||||
|
||||
@lru_cache(maxsize=2)
|
||||
def kitty_exe() -> str:
|
||||
rpath = sys._xoptions.get('bundle_exe_dir')
|
||||
|
||||
@ -6,7 +6,8 @@ import string
|
||||
from typing import Any, Callable, Dict, Iterable, Optional, Tuple, Union
|
||||
|
||||
from . import fast_data_types as defines
|
||||
from .config import KeyAction, KeyMap, KeySpec, SequenceMap, SubSequenceMap
|
||||
from .constants import SingleKey
|
||||
from .config import KeyAction, KeyMap, SequenceMap, SubSequenceMap
|
||||
from .key_encoding import KEY_MAP
|
||||
from .terminfo import key_as_bytes, modify_key_bytes
|
||||
from .typing import ScreenType, WindowType
|
||||
@ -278,13 +279,13 @@ def interpret_key_event(key: int, native_key: int, mods: int, window: WindowType
|
||||
|
||||
def get_shortcut(keymap: Union[KeyMap, SequenceMap], mods: int, key: int, native_key: int) -> Optional[Union[KeyAction, SubSequenceMap]]:
|
||||
mods &= 0b1111
|
||||
ans = keymap.get((mods, False, key))
|
||||
ans = keymap.get(SingleKey(mods, False, key))
|
||||
if ans is None:
|
||||
ans = keymap.get((mods, True, native_key))
|
||||
ans = keymap.get(SingleKey(mods, True, native_key))
|
||||
return ans
|
||||
|
||||
|
||||
def shortcut_matches(s: KeySpec, mods: int, key: int, native_key: int) -> bool:
|
||||
def shortcut_matches(s: SingleKey, mods: int, key: int, native_key: int) -> bool:
|
||||
mods &= 0b1111
|
||||
q = native_key if s[1] else key
|
||||
return bool(s[0] & 0b1111 == mods & 0b1111 and s[2] == q)
|
||||
|
||||
@ -7,7 +7,7 @@ import os
|
||||
import shutil
|
||||
import sys
|
||||
from contextlib import contextmanager, suppress
|
||||
from typing import Generator, List, Mapping, Optional, Sequence, Tuple
|
||||
from typing import Generator, List, Mapping, Optional, Sequence
|
||||
|
||||
from .borders import load_borders_program
|
||||
from .boss import Boss
|
||||
@ -17,7 +17,7 @@ from .cli_stub import CLIOptions
|
||||
from .conf.utils import BadLine
|
||||
from .config import cached_values_for
|
||||
from .constants import (
|
||||
appname, beam_cursor_data_file, config_dir, glfw_path, is_macos,
|
||||
SingleKey, appname, beam_cursor_data_file, config_dir, glfw_path, is_macos,
|
||||
is_wayland, kitty_exe, logo_data_file, running_in_kitty
|
||||
)
|
||||
from .fast_data_types import (
|
||||
@ -103,7 +103,7 @@ def init_glfw(opts: OptionsStub, debug_keyboard: bool = False) -> str:
|
||||
return glfw_module
|
||||
|
||||
|
||||
def get_new_os_window_trigger(opts: OptionsStub) -> Optional[Tuple[int, bool, int]]:
|
||||
def get_new_os_window_trigger(opts: OptionsStub) -> Optional[SingleKey]:
|
||||
new_os_window_trigger = None
|
||||
if is_macos:
|
||||
new_os_window_shortcuts = []
|
||||
|
||||
@ -20,7 +20,8 @@ def generate_stub():
|
||||
'font_features': 'typing.Dict[str, typing.Tuple[str, ...]]'
|
||||
},
|
||||
preamble_lines=(
|
||||
'from kitty.config import KeyAction, KeyMap, SequenceMap, KeySpec',
|
||||
'from kitty.constants import SingleKey',
|
||||
'from kitty.config import KeyAction, KeyMap, SequenceMap',
|
||||
),
|
||||
extra_fields=(
|
||||
('keymap', 'KeyMap'),
|
||||
|
||||
@ -6,7 +6,7 @@ from typing import Tuple
|
||||
|
||||
|
||||
BossType = ChildType = TabType = WindowType = ScreenType = None
|
||||
BadLineType = KeySpec = SequenceMap = KeyActionType = None
|
||||
BadLineType = SequenceMap = KeyActionType = None
|
||||
AddressFamily = PopenType = Socket = StartupCtx = None
|
||||
SessionTab = SessionType = LayoutType = SpecialWindowInstance = None
|
||||
MarkType = RemoteCommandType = CoreTextFont = FontConfigPattern = None
|
||||
|
||||
@ -1,5 +1,8 @@
|
||||
from asyncio import AbstractEventLoop as AbstractEventLoop # noqa
|
||||
from socket import AddressFamily as AddressFamily, socket as Socket # noqa
|
||||
from subprocess import ( # noqa; noqa
|
||||
CompletedProcess as CompletedProcess, Popen as PopenType
|
||||
)
|
||||
from typing import ( # noqa
|
||||
Literal, Protocol as Protocol, TypedDict as TypedDict
|
||||
)
|
||||
@ -18,6 +21,10 @@ from kitty.conf.utils import KittensKeyAction as KittensKeyActionType # noqa
|
||||
from .boss import Boss as BossType # noqa
|
||||
from .child import Child as ChildType # noqa
|
||||
from .conf.utils import BadLine as BadLineType # noqa
|
||||
from .config import ( # noqa; noqa
|
||||
KeyAction as KeyActionType, KeyMap as KeyMap,
|
||||
KittyCommonOpts as KittyCommonOpts, SequenceMap as SequenceMap
|
||||
)
|
||||
from .fast_data_types import ( # noqa
|
||||
CoreTextFont as CoreTextFont, FontConfigPattern as FontConfigPattern,
|
||||
Screen as ScreenType, StartupCtx as StartupCtx
|
||||
@ -32,16 +39,6 @@ from .tabs import ( # noqa
|
||||
from .utils import ScreenSize as ScreenSize # noqa
|
||||
from .window import Window as WindowType # noqa
|
||||
|
||||
from subprocess import ( # noqa; noqa
|
||||
CompletedProcess as CompletedProcess, Popen as PopenType
|
||||
)
|
||||
|
||||
|
||||
from .config import ( # noqa; noqa
|
||||
KeyAction as KeyActionType, KeyMap as KeyMap, KeySpec as KeySpec,
|
||||
KittyCommonOpts as KittyCommonOpts, SequenceMap as SequenceMap
|
||||
)
|
||||
|
||||
EdgeLiteral = Literal['left', 'top', 'right', 'bottom']
|
||||
MatchType = Literal['mime', 'ext', 'protocol', 'file', 'path', 'url', 'fragment_matches']
|
||||
GRT_a = Literal['t', 'T', 'q', 'p', 'd']
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user