Allow action_alias to work with any action
This commit is contained in:
parent
ee2520e036
commit
a97a05b1ec
@ -1951,8 +1951,18 @@ class Boss:
|
||||
def format_bad_line(bad_line: BadLine) -> str:
|
||||
return f'{bad_line.number}:{bad_line.exception} in line: {bad_line.line}\n'
|
||||
|
||||
msg = '\n'.join(map(format_bad_line, bad_lines)).rstrip()
|
||||
self.show_error(_('Errors in kitty.conf'), msg)
|
||||
groups: Dict[str, List[BadLine]] = {}
|
||||
for bl in bad_lines:
|
||||
groups.setdefault(bl.file, []).append(bl)
|
||||
ans: List[str] = []
|
||||
a = ans.append
|
||||
for file in sorted(groups):
|
||||
if file:
|
||||
a(f'In file {file}:')
|
||||
[a(format_bad_line(x)) for x in groups[file]]
|
||||
|
||||
msg = '\n'.join(ans).rstrip()
|
||||
self.show_error(_('Errors parsing configuration'), msg)
|
||||
|
||||
@ac('misc', '''
|
||||
Change colors in the specified windows
|
||||
|
||||
@ -269,6 +269,11 @@ def generate_class(defn: Definition, loc: str) -> Tuple[str, str]:
|
||||
a('if not is_macos:')
|
||||
a(f' defaults.{option_name}.update({mval["linux"]!r}')
|
||||
|
||||
def resolve_action(a: Any) -> Any:
|
||||
if hasattr(a, 'resolve_aliases_and_parse'):
|
||||
a.resolve_aliases_and_parse({})
|
||||
return a
|
||||
|
||||
for aname, func in action_parsers.items():
|
||||
a(f'defaults.{aname} = [')
|
||||
only: Dict[str, List[Tuple[str, Callable[..., Any]]]] = {}
|
||||
@ -281,7 +286,7 @@ def generate_class(defn: Definition, loc: str) -> Tuple[str, str]:
|
||||
else:
|
||||
for val in func(text):
|
||||
a(f' # {sc.name}')
|
||||
a(f' {val!r},')
|
||||
a(f' {resolve_action(val)!r},')
|
||||
a(']')
|
||||
if only:
|
||||
imports.add(('kitty.constants', 'is_macos'))
|
||||
@ -290,7 +295,7 @@ def generate_class(defn: Definition, loc: str) -> Tuple[str, str]:
|
||||
a(f'if {cond}:')
|
||||
for (text, func) in items:
|
||||
for val in func(text):
|
||||
a(f' defaults.{aname}.append({val!r})')
|
||||
a(f' defaults.{aname}.append({resolve_action(val)!r})')
|
||||
|
||||
t('')
|
||||
t('')
|
||||
|
||||
@ -4,13 +4,14 @@
|
||||
import os
|
||||
import re
|
||||
import shlex
|
||||
from contextlib import contextmanager
|
||||
from typing import (
|
||||
Any, Callable, Dict, Generator, Iterable, List, NamedTuple, Optional,
|
||||
Sequence, Set, Tuple, TypeVar, Union, Generic
|
||||
Any, Callable, Dict, Generator, Generic, Iterable, Iterator, List,
|
||||
NamedTuple, Optional, Sequence, Set, Tuple, TypeVar, Union
|
||||
)
|
||||
|
||||
from ..rgb import to_color as as_color
|
||||
from ..fast_data_types import Color
|
||||
from ..rgb import to_color as as_color
|
||||
from ..types import ConvertibleToNumbers, ParsedShortcut
|
||||
from ..typing import Protocol
|
||||
from ..utils import expandvars, log_error
|
||||
@ -30,6 +31,7 @@ class BadLine(NamedTuple):
|
||||
number: int
|
||||
line: str
|
||||
exception: Exception
|
||||
file: str
|
||||
|
||||
|
||||
def positive_int(x: ConvertibleToNumbers) -> int:
|
||||
@ -118,6 +120,40 @@ def choices(*choices: str) -> Choice:
|
||||
return Choice(choices)
|
||||
|
||||
|
||||
class CurrentlyParsing:
|
||||
__slots__ = 'line', 'number', 'file'
|
||||
|
||||
def __init__(self, line: str = '', number: int = -1, file: str = ''):
|
||||
self.line = line
|
||||
self.number = number
|
||||
self.file = file
|
||||
|
||||
def __copy__(self) -> 'CurrentlyParsing':
|
||||
return CurrentlyParsing(self.line, self.number, self.file)
|
||||
|
||||
@contextmanager
|
||||
def set_line(self, line: str, number: int) -> Iterator['CurrentlyParsing']:
|
||||
orig = self.line, self.number
|
||||
self.line = line
|
||||
self.number = number
|
||||
try:
|
||||
yield self
|
||||
finally:
|
||||
self.line, self.number = orig
|
||||
|
||||
@contextmanager
|
||||
def set_file(self, file: str) -> Iterator['CurrentlyParsing']:
|
||||
orig = self.file
|
||||
self.file = file
|
||||
try:
|
||||
yield self
|
||||
finally:
|
||||
self.file = orig
|
||||
|
||||
|
||||
currently_parsing = CurrentlyParsing()
|
||||
|
||||
|
||||
def parse_line(
|
||||
line: str,
|
||||
parse_conf_item: ItemParser,
|
||||
@ -139,7 +175,8 @@ def parse_line(
|
||||
val = os.path.join(base_path_for_includes, val)
|
||||
try:
|
||||
with open(val, encoding='utf-8', errors='replace') as include:
|
||||
_parse(include, parse_conf_item, ans, accumulate_bad_lines)
|
||||
with currently_parsing.set_file(val):
|
||||
_parse(include, parse_conf_item, ans, accumulate_bad_lines)
|
||||
except FileNotFoundError:
|
||||
log_error(
|
||||
'Could not find included config file: {}, ignoring'.
|
||||
@ -169,13 +206,12 @@ def _parse(
|
||||
base_path_for_includes = config_dir
|
||||
for i, line in enumerate(lines):
|
||||
try:
|
||||
parse_line(
|
||||
line, parse_conf_item, ans, base_path_for_includes, accumulate_bad_lines
|
||||
)
|
||||
with currently_parsing.set_line(line, i + 1):
|
||||
parse_line(line, parse_conf_item, ans, base_path_for_includes, accumulate_bad_lines)
|
||||
except Exception as e:
|
||||
if accumulate_bad_lines is None:
|
||||
raise
|
||||
accumulate_bad_lines.append(BadLine(i + 1, line.rstrip(), e))
|
||||
accumulate_bad_lines.append(BadLine(i + 1, line.rstrip(), e, currently_parsing.file))
|
||||
|
||||
|
||||
def parse_config_base(
|
||||
@ -219,13 +255,15 @@ def load_config(
|
||||
continue
|
||||
try:
|
||||
with open(path, encoding='utf-8', errors='replace') as f:
|
||||
vals = parse_config(f)
|
||||
with currently_parsing.set_file(path):
|
||||
vals = parse_config(f)
|
||||
except (FileNotFoundError, PermissionError):
|
||||
continue
|
||||
found_paths.append(path)
|
||||
ans = merge_configs(ans, vals)
|
||||
if overrides is not None:
|
||||
vals = parse_config(overrides)
|
||||
with currently_parsing.set_file('<override>'):
|
||||
vals = parse_config(overrides)
|
||||
ans = merge_configs(ans, vals)
|
||||
return ans, tuple(found_paths)
|
||||
|
||||
|
||||
@ -91,13 +91,20 @@ def prepare_config_file_for_editing() -> str:
|
||||
return defconf
|
||||
|
||||
|
||||
def finalize_keys(opts: Options, alias_map: Dict[str, List[ActionAlias]]) -> None:
|
||||
def finalize_keys(opts: Options, alias_map: Dict[str, List[ActionAlias]], accumulate_bad_lines: Optional[List[BadLine]] = None) -> None:
|
||||
defns: List[KeyDefinition] = []
|
||||
for d in opts.map:
|
||||
if d is None: # clear_all_shortcuts
|
||||
defns = [] # type: ignore
|
||||
else:
|
||||
defns.append(d.resolve_and_copy(opts.kitty_mod, alias_map))
|
||||
try:
|
||||
defns.append(d.resolve_and_copy(opts.kitty_mod, alias_map))
|
||||
except Exception as err:
|
||||
if accumulate_bad_lines is None:
|
||||
log_error(f'Ignoring map with invalid action: {d.unresolved_action}. Error: {err}')
|
||||
else:
|
||||
accumulate_bad_lines.append(BadLine(d.definition_location.number, d.definition_location.line, err, d.definition_location.file))
|
||||
|
||||
keymap: KeyMap = {}
|
||||
sequence_map: SequenceMap = {}
|
||||
|
||||
@ -122,13 +129,19 @@ def finalize_keys(opts: Options, alias_map: Dict[str, List[ActionAlias]]) -> Non
|
||||
opts.sequence_map = sequence_map
|
||||
|
||||
|
||||
def finalize_mouse_mappings(opts: Options, alias_map: Dict[str, List[ActionAlias]]) -> None:
|
||||
def finalize_mouse_mappings(opts: Options, alias_map: Dict[str, List[ActionAlias]], accumulate_bad_lines: Optional[List[BadLine]] = None) -> None:
|
||||
defns: List[MouseMapping] = []
|
||||
for d in opts.mouse_map:
|
||||
if d is None: # clear_all_mouse_actions
|
||||
defns = [] # type: ignore
|
||||
else:
|
||||
defns.append(d.resolve_and_copy(opts.kitty_mod, alias_map))
|
||||
try:
|
||||
defns.append(d.resolve_and_copy(opts.kitty_mod, alias_map))
|
||||
except Exception as err:
|
||||
if accumulate_bad_lines is None:
|
||||
log_error(f'Ignoring mouse_map with invalid action: {d.unresolved_action}. Error: {err}')
|
||||
else:
|
||||
accumulate_bad_lines.append(BadLine(d.definition_location.number, d.definition_location.line, err, d.definition_location.file))
|
||||
mousemap: MouseMap = {}
|
||||
|
||||
for defn in defns:
|
||||
@ -161,8 +174,8 @@ def load_config(*paths: str, overrides: Optional[Iterable[str]] = None, accumula
|
||||
|
||||
alias_map = build_action_aliases(opts.kitten_alias, 'kitten')
|
||||
alias_map.update(build_action_aliases(opts.action_alias))
|
||||
finalize_keys(opts, alias_map)
|
||||
finalize_mouse_mappings(opts, alias_map)
|
||||
finalize_keys(opts, alias_map, accumulate_bad_lines)
|
||||
finalize_mouse_mappings(opts, alias_map, accumulate_bad_lines)
|
||||
# delete no longer needed definitions, replacing with empty placeholders
|
||||
opts.kitten_alias = {}
|
||||
opts.action_alias = {}
|
||||
|
||||
@ -2875,10 +2875,9 @@ opt('+action_alias', 'launch_tab launch --type=tab --cwd=current',
|
||||
add_to_default=False,
|
||||
long_text='''
|
||||
Define aliases to avoid repeating the same options in multiple mappings. Aliases
|
||||
can be defined for the :ref:`launch <action-launch>`, :ref:`kitten <action-kitten>`
|
||||
and :ref:`action-remote_control` actions. Aliases are expanded recursively.
|
||||
For example, the above alias allows you to create mappings to launch a new tab without
|
||||
duplication::
|
||||
can be defined for any action. Aliases are expanded recursively.
|
||||
For example, the above alias allows you to create mappings to launch a new tab
|
||||
in the current working directory without duplication::
|
||||
|
||||
map f1 launch_tab vim
|
||||
map f2 launch_tab emacs
|
||||
|
||||
4
kitty/options/types.py
generated
4
kitty/options/types.py
generated
@ -584,10 +584,10 @@ class Options:
|
||||
window_padding_width: FloatEdges = FloatEdges(left=0, top=0, right=0, bottom=0)
|
||||
window_resize_step_cells: int = 2
|
||||
window_resize_step_lines: int = 2
|
||||
action_alias: typing.Dict[str, typing.List[str]] = {}
|
||||
action_alias: typing.Dict[str, str] = {}
|
||||
env: typing.Dict[str, str] = {}
|
||||
font_features: typing.Dict[str, typing.Tuple[kitty.fonts.FontFeature, ...]] = {}
|
||||
kitten_alias: typing.Dict[str, typing.List[str]] = {}
|
||||
kitten_alias: typing.Dict[str, str] = {}
|
||||
symbol_map: typing.Dict[typing.Tuple[int, int], str] = {}
|
||||
watcher: typing.Dict[str, str] = {}
|
||||
map: typing.List[kitty.options.utils.KeyDefinition] = []
|
||||
|
||||
@ -12,8 +12,9 @@ from typing import (
|
||||
|
||||
import kitty.fast_data_types as defines
|
||||
from kitty.conf.utils import (
|
||||
KeyAction, KeyFuncWrapper, positive_float, positive_int, python_string,
|
||||
to_bool, to_cmdline, to_color, uniq, unit_float
|
||||
CurrentlyParsing, KeyAction, KeyFuncWrapper, currently_parsing,
|
||||
positive_float, positive_int, python_string, to_bool, to_cmdline, to_color,
|
||||
uniq, unit_float
|
||||
)
|
||||
from kitty.constants import config_dir, is_macos
|
||||
from kitty.fast_data_types import (
|
||||
@ -765,11 +766,11 @@ def watcher(val: str, current_val: Container[str]) -> Iterable[Tuple[str, str]]:
|
||||
yield val, val
|
||||
|
||||
|
||||
def action_alias(val: str) -> Iterable[Tuple[str, List[str]]]:
|
||||
def action_alias(val: str) -> Iterable[Tuple[str, str]]:
|
||||
parts = val.split(maxsplit=1)
|
||||
if len(parts) > 1:
|
||||
alias_name, rest = parts
|
||||
yield alias_name, to_cmdline(rest)
|
||||
yield alias_name, rest
|
||||
|
||||
|
||||
kitten_alias = action_alias
|
||||
@ -802,98 +803,122 @@ def symbol_map(val: str) -> Iterable[Tuple[Tuple[int, int], str]]:
|
||||
yield (a, b), family
|
||||
|
||||
|
||||
def parse_combine(rest: str, action_type: str = 'map') -> Iterator[KeyAction]:
|
||||
sep, rest = rest.split(maxsplit=1)
|
||||
parts = re.split(r'\s*' + re.escape(sep) + r'\s*', rest)
|
||||
for x in parts:
|
||||
if x:
|
||||
yield from parse_key_actions(x, action_type)
|
||||
|
||||
|
||||
def parse_key_actions(action: str, action_type: str = 'map') -> Iterator[KeyAction]:
|
||||
def parse_key_action(action: str, action_type: str = 'map') -> KeyAction:
|
||||
parts = action.strip().split(maxsplit=1)
|
||||
func = parts[0]
|
||||
if len(parts) == 1:
|
||||
yield KeyAction(func, ())
|
||||
return
|
||||
return KeyAction(func, ())
|
||||
rest = parts[1]
|
||||
if func == 'combine':
|
||||
yield from parse_combine(rest, action_type)
|
||||
else:
|
||||
parser = func_with_args.get(func) or shlex_parse
|
||||
try:
|
||||
func, args = parser(func, rest)
|
||||
except Exception as err:
|
||||
log_error(f'Ignoring invalid {action_type} action: {action} with err: {err}')
|
||||
else:
|
||||
yield KeyAction(func, tuple(args))
|
||||
parser = func_with_args.get(func)
|
||||
if parser is None:
|
||||
raise KeyError(f'Unknown action: {func}')
|
||||
func, args = parser(func, rest)
|
||||
return KeyAction(func, tuple(args))
|
||||
|
||||
|
||||
class ActionAlias(NamedTuple):
|
||||
func_name: str
|
||||
args: Tuple[str, ...]
|
||||
second_arg_test: Optional[Callable[[Any], bool]] = None
|
||||
name: str
|
||||
value: str
|
||||
is_recursive: bool
|
||||
replace_second_arg: bool = False
|
||||
|
||||
|
||||
def build_action_aliases(raw: Dict[str, List[str]], first_arg_replacement: str = '') -> Dict[str, List[ActionAlias]]:
|
||||
def build_action_aliases(raw: Dict[str, str], first_arg_replacement: str = '') -> Dict[str, List[ActionAlias]]:
|
||||
ans: Dict[str, List[ActionAlias]] = {}
|
||||
if first_arg_replacement:
|
||||
for alias_name, args in raw.items():
|
||||
ans.setdefault(first_arg_replacement, []).append(ActionAlias(first_arg_replacement, tuple(args), alias_name.__eq__))
|
||||
for alias_name, rest in raw.items():
|
||||
is_recursive = alias_name == rest.split(maxsplit=1)[0]
|
||||
ans.setdefault(first_arg_replacement, []).append(ActionAlias(alias_name, rest, is_recursive, True))
|
||||
else:
|
||||
for alias_name, args in raw.items():
|
||||
ans[alias_name] = [ActionAlias(args[0], tuple(args[1:]))]
|
||||
for alias_name, rest in raw.items():
|
||||
is_recursive = alias_name == rest.split(maxsplit=1)[0]
|
||||
ans[alias_name] = [ActionAlias(alias_name, rest, is_recursive)]
|
||||
return ans
|
||||
|
||||
|
||||
def resolve_aliases_in_action(action: KeyAction, aliases: Dict[str, List[ActionAlias]]) -> KeyAction:
|
||||
for alias in aliases.get(action.func, ()):
|
||||
if alias.second_arg_test is None:
|
||||
new_action = action._replace(func=alias.func_name, args=alias.args + action.args)
|
||||
if new_action.func == action.func:
|
||||
def resolve_aliases_and_parse_actions(
|
||||
defn: str, aliases: Dict[str, List[ActionAlias]], map_type: str
|
||||
) -> Iterator[KeyAction]:
|
||||
parts = defn.split(maxsplit=1)
|
||||
if len(parts) == 1:
|
||||
possible_alias = defn
|
||||
rest = ''
|
||||
else:
|
||||
possible_alias = parts[0]
|
||||
rest = parts[1]
|
||||
for alias in aliases.get(possible_alias, ()):
|
||||
if alias.replace_second_arg: # kitten_alias
|
||||
if not rest:
|
||||
continue
|
||||
parts = rest.split(maxsplit=1)
|
||||
if parts[0] != alias.name:
|
||||
continue
|
||||
new_defn = possible_alias + ' ' + alias.value + ((' ' + parts[1]) if len(parts) > 1 else '')
|
||||
new_aliases = aliases
|
||||
if alias.is_recursive:
|
||||
new_aliases = aliases.copy()
|
||||
new_aliases.pop(alias.func_name)
|
||||
else:
|
||||
new_aliases = aliases
|
||||
return resolve_aliases_in_action(new_action, new_aliases)
|
||||
if action.args and alias.second_arg_test(action.args[0]):
|
||||
new_action = action._replace(func=alias.func_name, args=alias.args + action.args[1:])
|
||||
if new_action.func == action.func and new_action.args and alias.second_arg_test(new_action.args[0]):
|
||||
new_aliases[possible_alias] = [a for a in aliases[possible_alias] if a is not alias]
|
||||
yield from resolve_aliases_and_parse_actions(new_defn, new_aliases, map_type)
|
||||
return
|
||||
else: # action_alias
|
||||
new_defn = alias.value + ((' ' + rest) if rest else '')
|
||||
new_aliases = aliases
|
||||
if alias.is_recursive:
|
||||
new_aliases = aliases.copy()
|
||||
new_aliases[action.func] = [x for x in aliases[action.func] if x is not alias]
|
||||
else:
|
||||
new_aliases = aliases
|
||||
return resolve_aliases_in_action(new_action, new_aliases)
|
||||
return action
|
||||
new_aliases.pop(possible_alias)
|
||||
yield from resolve_aliases_and_parse_actions(new_defn, new_aliases, map_type)
|
||||
return
|
||||
|
||||
if possible_alias == 'combine':
|
||||
sep, rest = rest.split(maxsplit=1)
|
||||
parts = re.split(r'\s*' + re.escape(sep) + r'\s*', rest)
|
||||
for x in parts:
|
||||
if x:
|
||||
yield from resolve_aliases_and_parse_actions(x, aliases, map_type)
|
||||
else:
|
||||
yield parse_key_action(defn, map_type)
|
||||
|
||||
|
||||
class BaseDefinition:
|
||||
actions: Tuple[KeyAction, ...]
|
||||
actions: Tuple[KeyAction, ...] = ()
|
||||
no_op_actions = frozenset(('noop', 'no-op', 'no_op'))
|
||||
map_type: str = 'map'
|
||||
definition_location: CurrentlyParsing
|
||||
|
||||
def __init__(self, unresolved_action: str) -> None:
|
||||
self.unresolved_action = unresolved_action
|
||||
self.definition_location = currently_parsing.__copy__()
|
||||
|
||||
@property
|
||||
def is_no_op(self) -> bool:
|
||||
return len(self.actions) == 1 and self.actions[0].func in self.no_op_actions
|
||||
|
||||
def resolve_aliases(self, aliases: Dict[str, List[ActionAlias]]) -> Tuple[KeyAction, ...]:
|
||||
self.actions = tuple(resolve_aliases_in_action(a, aliases) for a in self.actions)
|
||||
return self.actions
|
||||
def resolve_aliases_and_parse(self, aliases: Dict[str, List[ActionAlias]]) -> None:
|
||||
if self.unresolved_action:
|
||||
self.actions = tuple(resolve_aliases_and_parse_actions(
|
||||
self.unresolved_action, aliases, self.map_type))
|
||||
self.unresolved_action = ''
|
||||
|
||||
|
||||
class MouseMapping(BaseDefinition):
|
||||
map_type: str = 'mouse_map'
|
||||
|
||||
def __init__(self, button: int, mods: int, repeat_count: int, grabbed: bool, actions: Tuple[KeyAction, ...]):
|
||||
def __init__(self, button: int, mods: int, repeat_count: int, grabbed: bool, actions: Tuple[KeyAction, ...] = (), unresolved_action: str = ''):
|
||||
super().__init__(unresolved_action)
|
||||
self.button = button
|
||||
self.mods = mods
|
||||
self.actions = actions
|
||||
self.repeat_count = repeat_count
|
||||
self.grabbed = grabbed
|
||||
self.actions = actions
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return f'MouseMapping({self.button}, {self.mods}, {self.repeat_count}, {self.grabbed}, {self.actions})'
|
||||
|
||||
def resolve_and_copy(self, kitty_mod: int, aliases: Dict[str, List[ActionAlias]]) -> 'MouseMapping':
|
||||
return MouseMapping(self.button, defines.resolve_key_mods(kitty_mod, self.mods), self.repeat_count, self.grabbed, self.resolve_aliases(aliases))
|
||||
ans = MouseMapping(
|
||||
self.button, defines.resolve_key_mods(kitty_mod, self.mods), self.repeat_count, self.grabbed, self.actions, self.unresolved_action)
|
||||
ans.resolve_aliases_and_parse(aliases)
|
||||
return ans
|
||||
|
||||
@property
|
||||
def trigger(self) -> MouseEvent:
|
||||
@ -902,7 +927,11 @@ class MouseMapping(BaseDefinition):
|
||||
|
||||
class KeyDefinition(BaseDefinition):
|
||||
|
||||
def __init__(self, is_sequence: bool, actions: Tuple[KeyAction, ...], mods: int, is_native: bool, key: int, rest: Tuple[SingleKey, ...] = ()):
|
||||
def __init__(
|
||||
self, is_sequence: bool, actions: Tuple[KeyAction, ...], mods: int, is_native: bool, key: int,
|
||||
rest: Tuple[SingleKey, ...] = (), unresolved_action: str = ''
|
||||
):
|
||||
super().__init__(unresolved_action)
|
||||
self.is_sequence = is_sequence
|
||||
self.actions = actions
|
||||
self.trigger = SingleKey(mods, is_native, key)
|
||||
@ -915,10 +944,12 @@ class KeyDefinition(BaseDefinition):
|
||||
def r(k: SingleKey) -> SingleKey:
|
||||
mods = defines.resolve_key_mods(kitty_mod, k.mods)
|
||||
return k._replace(mods=mods)
|
||||
return KeyDefinition(
|
||||
self.is_sequence, self.resolve_aliases(aliases),
|
||||
defines.resolve_key_mods(kitty_mod, self.trigger.mods),
|
||||
self.trigger.is_native, self.trigger.key, tuple(map(r, self.rest)))
|
||||
ans = KeyDefinition(
|
||||
self.is_sequence, self.actions, defines.resolve_key_mods(kitty_mod, self.trigger.mods),
|
||||
self.trigger.is_native, self.trigger.key, tuple(map(r, self.rest)), self.unresolved_action
|
||||
)
|
||||
ans.resolve_aliases_and_parse(aliases)
|
||||
return ans
|
||||
|
||||
|
||||
def parse_map(val: str) -> Iterable[KeyDefinition]:
|
||||
@ -956,18 +987,12 @@ def parse_map(val: str) -> Iterable[KeyDefinition]:
|
||||
if mods is not None:
|
||||
log_error(f'Shortcut: {sc} has unknown key, ignoring')
|
||||
return
|
||||
try:
|
||||
paction = tuple(parse_key_actions(action))
|
||||
except Exception:
|
||||
log_error(f'Invalid shortcut action: {action}. Ignoring.')
|
||||
if is_sequence:
|
||||
if trigger is not None:
|
||||
yield KeyDefinition(True, (), trigger[0], trigger[1], trigger[2], rest, unresolved_action=action)
|
||||
else:
|
||||
if paction:
|
||||
if is_sequence:
|
||||
if trigger is not None:
|
||||
yield KeyDefinition(True, paction, trigger[0], trigger[1], trigger[2], rest)
|
||||
else:
|
||||
assert key is not None
|
||||
yield KeyDefinition(False, paction, mods, is_native, key)
|
||||
assert key is not None
|
||||
yield KeyDefinition(False, (), mods, is_native, key, unresolved_action=action)
|
||||
|
||||
|
||||
def parse_mouse_map(val: str) -> Iterable[MouseMapping]:
|
||||
@ -1000,15 +1025,8 @@ def parse_mouse_map(val: str) -> Iterable[MouseMapping]:
|
||||
if specified_modes - {'grabbed', 'ungrabbed'}:
|
||||
log_error(f'Mouse modes: {modes} not recognized, ignoring')
|
||||
return
|
||||
try:
|
||||
paction = tuple(parse_key_actions(action, 'mouse_map'))
|
||||
except Exception:
|
||||
log_error(f'Invalid mouse action: {action}. Ignoring.')
|
||||
return
|
||||
if not paction:
|
||||
return
|
||||
for mode in sorted(specified_modes):
|
||||
yield MouseMapping(button, mods, count, mode == 'grabbed', paction)
|
||||
yield MouseMapping(button, mods, count, mode == 'grabbed', unresolved_action=action)
|
||||
|
||||
|
||||
def deprecated_hide_window_decorations_aliases(key: str, val: str, ans: Dict[str, Any]) -> None:
|
||||
|
||||
@ -27,7 +27,10 @@ class TestConfParsing(BaseTest):
|
||||
del bad_lines[:]
|
||||
del self.error_messages[:]
|
||||
ans = load_config(overrides=lines, accumulate_bad_lines=bad_lines)
|
||||
self.ae(len(bad_lines), bad_line_num)
|
||||
if bad_line_num:
|
||||
self.ae(len(bad_lines), bad_line_num)
|
||||
else:
|
||||
self.assertFalse(bad_lines)
|
||||
return ans
|
||||
|
||||
def keys_for_func(opts, name):
|
||||
@ -51,6 +54,7 @@ class TestConfParsing(BaseTest):
|
||||
opts = p('pointer_shape_when_grabbed XXX', bad_line_num=1)
|
||||
self.ae(opts.pointer_shape_when_grabbed, defaults.pointer_shape_when_grabbed)
|
||||
|
||||
# test the aliasing options
|
||||
opts = p('env A=1', 'env B=x$A', 'env C=', 'env D', 'clear_all_shortcuts y', 'kitten_alias a b --moo', 'map f1 kitten a arg')
|
||||
self.ae(opts.env, {'A': '1', 'B': 'x1', 'C': '', 'D': DELETE_ENV_VAR})
|
||||
ka = tuple(opts.keymap.values())[0][0]
|
||||
@ -77,10 +81,20 @@ class TestConfParsing(BaseTest):
|
||||
self.ae(ka.func, 'launch')
|
||||
self.ae(ka.args, ('--moo', 'XXX'))
|
||||
|
||||
opts = p('clear_all_shortcuts y', 'action_alias cfs change_font_size current', 'map f1 cfs +2')
|
||||
ka = tuple(opts.keymap.values())[0][0]
|
||||
self.ae(ka.func, 'change_font_size')
|
||||
self.ae(ka.args, (False, '+', 2.0))
|
||||
|
||||
opts = p('clear_all_shortcuts y', 'action_alias la launch --moo', 'map f1 combine : new_window : la ')
|
||||
ka = tuple(opts.keymap.values())[0]
|
||||
self.ae((ka[0].func, ka[1].func), ('new_window', 'launch'))
|
||||
|
||||
opts = p('clear_all_shortcuts y', 'action_alias cc combine : new_window : launch --moo', 'map f1 cc XXX')
|
||||
ka = tuple(opts.keymap.values())[0]
|
||||
self.ae((ka[0].func, ka[1].func), ('new_window', 'launch'))
|
||||
self.ae(ka[1].args, ('--moo', 'XXX'))
|
||||
|
||||
opts = p('kitty_mod alt')
|
||||
self.ae(opts.kitty_mod, to_modifiers('alt'))
|
||||
self.ae(next(keys_for_func(opts, 'next_layout')).mods, opts.kitty_mod)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user