Move action parsing to option_types
This commit is contained in:
parent
9f8a120664
commit
bfbb85399e
312
kitty/config.py
312
kitty/config.py
@ -4,333 +4,29 @@
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
from contextlib import contextmanager, suppress
|
||||
from functools import partial
|
||||
from typing import (
|
||||
Any, Callable, Dict, FrozenSet, Generator, Iterable, List, Optional,
|
||||
Sequence, Tuple, Type, Union
|
||||
Sequence, Tuple, Type
|
||||
)
|
||||
|
||||
from . import fast_data_types as defines
|
||||
from .conf.definition import as_conf_file, config_lines
|
||||
from .conf.utils import (
|
||||
BadLine, init_config, load_config as _load_config, merge_dicts,
|
||||
parse_config_base, python_string, to_bool, to_cmdline
|
||||
parse_config_base, to_bool
|
||||
)
|
||||
from .config_data import all_options
|
||||
from .constants import cache_dir, defconf, is_macos
|
||||
from .options_stub import Options as OptionsStub
|
||||
from .options_types import (
|
||||
FuncArgsType, KeyDefinition, KeyMap, MouseMap, MouseMapping, SequenceMap,
|
||||
env, font_features, func_with_args, kitten_alias, parse_key_action,
|
||||
parse_map, parse_mouse_map, symbol_map
|
||||
KeyDefinition, KeyMap, MouseMap, MouseMapping, SequenceMap, env,
|
||||
font_features, kitten_alias, parse_map, parse_mouse_map, symbol_map
|
||||
)
|
||||
from .typing import TypedDict
|
||||
from .utils import log_error
|
||||
|
||||
|
||||
@func_with_args(
|
||||
'pass_selection_to_program', 'new_window', 'new_tab', 'new_os_window',
|
||||
'new_window_with_cwd', 'new_tab_with_cwd', 'new_os_window_with_cwd',
|
||||
'launch'
|
||||
)
|
||||
def shlex_parse(func: str, rest: str) -> FuncArgsType:
|
||||
return func, to_cmdline(rest)
|
||||
|
||||
|
||||
@func_with_args('combine')
|
||||
def combine_parse(func: str, rest: str) -> FuncArgsType:
|
||||
sep, rest = rest.split(maxsplit=1)
|
||||
parts = re.split(r'\s*' + re.escape(sep) + r'\s*', rest)
|
||||
args = tuple(map(parse_key_action, filter(None, parts)))
|
||||
return func, args
|
||||
|
||||
|
||||
@func_with_args('send_text')
|
||||
def send_text_parse(func: str, rest: str) -> FuncArgsType:
|
||||
args = rest.split(maxsplit=1)
|
||||
mode = ''
|
||||
data = b''
|
||||
if len(args) > 1:
|
||||
mode = args[0]
|
||||
try:
|
||||
data = parse_send_text_bytes(args[1])
|
||||
except Exception:
|
||||
log_error('Ignoring invalid send_text string: ' + args[1])
|
||||
return func, [mode, data]
|
||||
|
||||
|
||||
@func_with_args('run_kitten', 'run_simple_kitten', 'kitten')
|
||||
def kitten_parse(func: str, rest: str) -> FuncArgsType:
|
||||
if func == 'kitten':
|
||||
args = rest.split(maxsplit=1)
|
||||
else:
|
||||
args = rest.split(maxsplit=2)[1:]
|
||||
func = 'kitten'
|
||||
return func, args
|
||||
|
||||
|
||||
@func_with_args('goto_tab')
|
||||
def goto_tab_parse(func: str, rest: str) -> FuncArgsType:
|
||||
args = (max(0, int(rest)), )
|
||||
return func, args
|
||||
|
||||
|
||||
@func_with_args('detach_window')
|
||||
def detach_window_parse(func: str, rest: str) -> FuncArgsType:
|
||||
if rest not in ('new', 'new-tab', 'ask'):
|
||||
log_error('Ignoring invalid detach_window argument: {}'.format(rest))
|
||||
rest = 'new'
|
||||
return func, (rest,)
|
||||
|
||||
|
||||
@func_with_args('detach_tab')
|
||||
def detach_tab_parse(func: str, rest: str) -> FuncArgsType:
|
||||
if rest not in ('new', 'ask'):
|
||||
log_error('Ignoring invalid detach_tab argument: {}'.format(rest))
|
||||
rest = 'new'
|
||||
return func, (rest,)
|
||||
|
||||
|
||||
@func_with_args('set_background_opacity', 'goto_layout', 'kitty_shell')
|
||||
def simple_parse(func: str, rest: str) -> FuncArgsType:
|
||||
return func, [rest]
|
||||
|
||||
|
||||
@func_with_args('set_font_size')
|
||||
def float_parse(func: str, rest: str) -> FuncArgsType:
|
||||
return func, (float(rest),)
|
||||
|
||||
|
||||
@func_with_args('signal_child')
|
||||
def signal_child_parse(func: str, rest: str) -> FuncArgsType:
|
||||
import signal
|
||||
signals = []
|
||||
for q in rest.split():
|
||||
try:
|
||||
signum = getattr(signal, q.upper())
|
||||
except AttributeError:
|
||||
log_error(f'Unknown signal: {rest} ignoring')
|
||||
else:
|
||||
signals.append(signum)
|
||||
return func, tuple(signals)
|
||||
|
||||
|
||||
@func_with_args('change_font_size')
|
||||
def parse_change_font_size(func: str, rest: str) -> Tuple[str, Tuple[bool, Optional[str], float]]:
|
||||
vals = rest.strip().split(maxsplit=1)
|
||||
if len(vals) != 2:
|
||||
log_error('Invalid change_font_size specification: {}, treating it as default'.format(rest))
|
||||
return func, (True, None, 0)
|
||||
c_all = vals[0].lower() == 'all'
|
||||
sign: Optional[str] = None
|
||||
amt = vals[1]
|
||||
if amt[0] in '+-':
|
||||
sign = amt[0]
|
||||
amt = amt[1:]
|
||||
return func, (c_all, sign, float(amt.strip()))
|
||||
|
||||
|
||||
@func_with_args('clear_terminal')
|
||||
def clear_terminal(func: str, rest: str) -> FuncArgsType:
|
||||
vals = rest.strip().split(maxsplit=1)
|
||||
if len(vals) != 2:
|
||||
log_error('clear_terminal needs two arguments, using defaults')
|
||||
args: List[Union[str, bool]] = ['reset', 'active']
|
||||
else:
|
||||
args = [vals[0].lower(), vals[1].lower() == 'active']
|
||||
return func, args
|
||||
|
||||
|
||||
@func_with_args('copy_to_buffer')
|
||||
def copy_to_buffer(func: str, rest: str) -> FuncArgsType:
|
||||
return func, [rest]
|
||||
|
||||
|
||||
@func_with_args('paste_from_buffer')
|
||||
def paste_from_buffer(func: str, rest: str) -> FuncArgsType:
|
||||
return func, [rest]
|
||||
|
||||
|
||||
@func_with_args('neighboring_window')
|
||||
def neighboring_window(func: str, rest: str) -> FuncArgsType:
|
||||
rest = rest.lower()
|
||||
rest = {'up': 'top', 'down': 'bottom'}.get(rest, rest)
|
||||
if rest not in ('left', 'right', 'top', 'bottom'):
|
||||
log_error('Invalid neighbor specification: {}'.format(rest))
|
||||
rest = 'right'
|
||||
return func, [rest]
|
||||
|
||||
|
||||
@func_with_args('resize_window')
|
||||
def resize_window(func: str, rest: str) -> FuncArgsType:
|
||||
vals = rest.strip().split(maxsplit=1)
|
||||
if len(vals) > 2:
|
||||
log_error('resize_window needs one or two arguments, using defaults')
|
||||
args = ['wider', 1]
|
||||
else:
|
||||
quality = vals[0].lower()
|
||||
if quality not in ('taller', 'shorter', 'wider', 'narrower'):
|
||||
log_error('Invalid quality specification: {}'.format(quality))
|
||||
quality = 'wider'
|
||||
increment = 1
|
||||
if len(vals) == 2:
|
||||
try:
|
||||
increment = int(vals[1])
|
||||
except Exception:
|
||||
log_error('Invalid increment specification: {}'.format(vals[1]))
|
||||
args = [quality, increment]
|
||||
return func, args
|
||||
|
||||
|
||||
@func_with_args('move_window')
|
||||
def move_window(func: str, rest: str) -> FuncArgsType:
|
||||
rest = rest.lower()
|
||||
rest = {'up': 'top', 'down': 'bottom'}.get(rest, rest)
|
||||
prest: Union[int, str] = rest
|
||||
try:
|
||||
prest = int(prest)
|
||||
except Exception:
|
||||
if prest not in ('left', 'right', 'top', 'bottom'):
|
||||
log_error('Invalid move_window specification: {}'.format(rest))
|
||||
prest = 0
|
||||
return func, [prest]
|
||||
|
||||
|
||||
@func_with_args('pipe')
|
||||
def pipe(func: str, rest: str) -> FuncArgsType:
|
||||
import shlex
|
||||
r = shlex.split(rest)
|
||||
if len(r) < 3:
|
||||
log_error('Too few arguments to pipe function')
|
||||
r = ['none', 'none', 'true']
|
||||
return func, r
|
||||
|
||||
|
||||
@func_with_args('set_colors')
|
||||
def set_colors(func: str, rest: str) -> FuncArgsType:
|
||||
import shlex
|
||||
r = shlex.split(rest)
|
||||
if len(r) < 1:
|
||||
log_error('Too few arguments to set_colors function')
|
||||
return func, r
|
||||
|
||||
|
||||
@func_with_args('remote_control')
|
||||
def remote_control(func: str, rest: str) -> FuncArgsType:
|
||||
import shlex
|
||||
r = shlex.split(rest)
|
||||
if len(r) < 1:
|
||||
log_error('Too few arguments to remote_control function')
|
||||
return func, r
|
||||
|
||||
|
||||
@func_with_args('nth_window')
|
||||
def nth_window(func: str, rest: str) -> FuncArgsType:
|
||||
try:
|
||||
num = int(rest)
|
||||
except Exception:
|
||||
log_error('Invalid nth_window number: {}'.format(rest))
|
||||
num = 1
|
||||
return func, [num]
|
||||
|
||||
|
||||
@func_with_args('disable_ligatures_in')
|
||||
def disable_ligatures_in(func: str, rest: str) -> FuncArgsType:
|
||||
parts = rest.split(maxsplit=1)
|
||||
if len(parts) == 1:
|
||||
where, strategy = 'active', parts[0]
|
||||
else:
|
||||
where, strategy = parts
|
||||
if where not in ('active', 'all', 'tab'):
|
||||
raise ValueError('{} is not a valid set of windows to disable ligatures in'.format(where))
|
||||
if strategy not in ('never', 'always', 'cursor'):
|
||||
raise ValueError('{} is not a valid disable ligatures strategy'.format(strategy))
|
||||
return func, [where, strategy]
|
||||
|
||||
|
||||
@func_with_args('layout_action')
|
||||
def layout_action(func: str, rest: str) -> FuncArgsType:
|
||||
parts = rest.split(maxsplit=1)
|
||||
if not parts:
|
||||
raise ValueError('layout_action must have at least one argument')
|
||||
return func, [parts[0], tuple(parts[1:])]
|
||||
|
||||
|
||||
def parse_marker_spec(ftype: str, parts: Sequence[str]) -> Tuple[str, Union[str, Tuple[Tuple[int, str], ...]], int]:
|
||||
flags = re.UNICODE
|
||||
if ftype in ('text', 'itext', 'regex', 'iregex'):
|
||||
if ftype.startswith('i'):
|
||||
flags |= re.IGNORECASE
|
||||
if not parts or len(parts) % 2 != 0:
|
||||
raise ValueError('No color specified in marker: {}'.format(' '.join(parts)))
|
||||
ans = []
|
||||
for i in range(0, len(parts), 2):
|
||||
try:
|
||||
color = max(1, min(int(parts[i]), 3))
|
||||
except Exception:
|
||||
raise ValueError('color {} in marker specification is not an integer'.format(parts[i]))
|
||||
sspec = parts[i + 1]
|
||||
if 'regex' not in ftype:
|
||||
sspec = re.escape(sspec)
|
||||
ans.append((color, sspec))
|
||||
ftype = 'regex'
|
||||
spec: Union[str, Tuple[Tuple[int, str], ...]] = tuple(ans)
|
||||
elif ftype == 'function':
|
||||
spec = ' '.join(parts)
|
||||
else:
|
||||
raise ValueError('Unknown marker type: {}'.format(ftype))
|
||||
return ftype, spec, flags
|
||||
|
||||
|
||||
@func_with_args('toggle_marker')
|
||||
def toggle_marker(func: str, rest: str) -> FuncArgsType:
|
||||
import shlex
|
||||
parts = rest.split(maxsplit=1)
|
||||
if len(parts) != 2:
|
||||
raise ValueError('{} is not a valid marker specification'.format(rest))
|
||||
ftype, spec = parts
|
||||
parts = shlex.split(spec)
|
||||
return func, list(parse_marker_spec(ftype, parts))
|
||||
|
||||
|
||||
@func_with_args('scroll_to_mark')
|
||||
def scroll_to_mark(func: str, rest: str) -> FuncArgsType:
|
||||
parts = rest.split()
|
||||
if not parts or not rest:
|
||||
return func, [True, 0]
|
||||
if len(parts) == 1:
|
||||
q = parts[0].lower()
|
||||
if q in ('prev', 'previous', 'next'):
|
||||
return func, [q != 'next', 0]
|
||||
try:
|
||||
return func, [True, max(0, min(int(q), 3))]
|
||||
except Exception:
|
||||
raise ValueError('{} is not a valid scroll_to_mark destination'.format(rest))
|
||||
return func, [parts[0] != 'next', max(0, min(int(parts[1]), 3))]
|
||||
|
||||
|
||||
@func_with_args('mouse_selection')
|
||||
def mouse_selection(func: str, rest: str) -> FuncArgsType:
|
||||
cmap = getattr(mouse_selection, 'code_map', None)
|
||||
if cmap is None:
|
||||
cmap = {
|
||||
'normal': defines.MOUSE_SELECTION_NORMAL,
|
||||
'extend': defines.MOUSE_SELECTION_EXTEND,
|
||||
'rectangle': defines.MOUSE_SELECTION_RECTANGLE,
|
||||
'word': defines.MOUSE_SELECTION_WORD,
|
||||
'line': defines.MOUSE_SELECTION_LINE,
|
||||
'line_from_point': defines.MOUSE_SELECTION_LINE_FROM_POINT,
|
||||
}
|
||||
setattr(mouse_selection, 'code_map', cmap)
|
||||
return func, [cmap[rest]]
|
||||
|
||||
|
||||
def parse_send_text_bytes(text: str) -> bytes:
|
||||
return python_string(text).encode('utf-8')
|
||||
|
||||
|
||||
def parse_send_text(val: str, key_definitions: List[KeyDefinition]) -> None:
|
||||
parts = val.split(' ')
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
|
||||
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from typing import (
|
||||
Any, Callable, Dict, FrozenSet, Iterable, List, NamedTuple, Optional,
|
||||
@ -14,7 +15,8 @@ import kitty.fast_data_types as defines
|
||||
from kitty.fast_data_types import CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE
|
||||
|
||||
from .conf.utils import (
|
||||
key_func, positive_float, positive_int, to_bool, to_color, uniq, unit_float
|
||||
key_func, positive_float, positive_int, python_string, to_bool, to_cmdline,
|
||||
to_color, uniq, unit_float
|
||||
)
|
||||
from .constants import config_dir
|
||||
from .fonts import FontFeature
|
||||
@ -53,6 +55,309 @@ class InvalidMods(ValueError):
|
||||
pass
|
||||
|
||||
|
||||
# Actions {{{
|
||||
@func_with_args(
|
||||
'pass_selection_to_program', 'new_window', 'new_tab', 'new_os_window',
|
||||
'new_window_with_cwd', 'new_tab_with_cwd', 'new_os_window_with_cwd',
|
||||
'launch'
|
||||
)
|
||||
def shlex_parse(func: str, rest: str) -> FuncArgsType:
|
||||
return func, to_cmdline(rest)
|
||||
|
||||
|
||||
@func_with_args('combine')
|
||||
def combine_parse(func: str, rest: str) -> FuncArgsType:
|
||||
sep, rest = rest.split(maxsplit=1)
|
||||
parts = re.split(r'\s*' + re.escape(sep) + r'\s*', rest)
|
||||
args = tuple(map(parse_key_action, filter(None, parts)))
|
||||
return func, args
|
||||
|
||||
|
||||
def parse_send_text_bytes(text: str) -> bytes:
|
||||
return python_string(text).encode('utf-8')
|
||||
|
||||
|
||||
@func_with_args('send_text')
|
||||
def send_text_parse(func: str, rest: str) -> FuncArgsType:
|
||||
args = rest.split(maxsplit=1)
|
||||
mode = ''
|
||||
data = b''
|
||||
if len(args) > 1:
|
||||
mode = args[0]
|
||||
try:
|
||||
data = parse_send_text_bytes(args[1])
|
||||
except Exception:
|
||||
log_error('Ignoring invalid send_text string: ' + args[1])
|
||||
return func, [mode, data]
|
||||
|
||||
|
||||
@func_with_args('run_kitten', 'run_simple_kitten', 'kitten')
|
||||
def kitten_parse(func: str, rest: str) -> FuncArgsType:
|
||||
if func == 'kitten':
|
||||
args = rest.split(maxsplit=1)
|
||||
else:
|
||||
args = rest.split(maxsplit=2)[1:]
|
||||
func = 'kitten'
|
||||
return func, args
|
||||
|
||||
|
||||
@func_with_args('goto_tab')
|
||||
def goto_tab_parse(func: str, rest: str) -> FuncArgsType:
|
||||
args = (max(0, int(rest)), )
|
||||
return func, args
|
||||
|
||||
|
||||
@func_with_args('detach_window')
|
||||
def detach_window_parse(func: str, rest: str) -> FuncArgsType:
|
||||
if rest not in ('new', 'new-tab', 'ask'):
|
||||
log_error('Ignoring invalid detach_window argument: {}'.format(rest))
|
||||
rest = 'new'
|
||||
return func, (rest,)
|
||||
|
||||
|
||||
@func_with_args('detach_tab')
|
||||
def detach_tab_parse(func: str, rest: str) -> FuncArgsType:
|
||||
if rest not in ('new', 'ask'):
|
||||
log_error('Ignoring invalid detach_tab argument: {}'.format(rest))
|
||||
rest = 'new'
|
||||
return func, (rest,)
|
||||
|
||||
|
||||
@func_with_args('set_background_opacity', 'goto_layout', 'kitty_shell')
|
||||
def simple_parse(func: str, rest: str) -> FuncArgsType:
|
||||
return func, [rest]
|
||||
|
||||
|
||||
@func_with_args('set_font_size')
|
||||
def float_parse(func: str, rest: str) -> FuncArgsType:
|
||||
return func, (float(rest),)
|
||||
|
||||
|
||||
@func_with_args('signal_child')
|
||||
def signal_child_parse(func: str, rest: str) -> FuncArgsType:
|
||||
import signal
|
||||
signals = []
|
||||
for q in rest.split():
|
||||
try:
|
||||
signum = getattr(signal, q.upper())
|
||||
except AttributeError:
|
||||
log_error(f'Unknown signal: {rest} ignoring')
|
||||
else:
|
||||
signals.append(signum)
|
||||
return func, tuple(signals)
|
||||
|
||||
|
||||
@func_with_args('change_font_size')
|
||||
def parse_change_font_size(func: str, rest: str) -> Tuple[str, Tuple[bool, Optional[str], float]]:
|
||||
vals = rest.strip().split(maxsplit=1)
|
||||
if len(vals) != 2:
|
||||
log_error('Invalid change_font_size specification: {}, treating it as default'.format(rest))
|
||||
return func, (True, None, 0)
|
||||
c_all = vals[0].lower() == 'all'
|
||||
sign: Optional[str] = None
|
||||
amt = vals[1]
|
||||
if amt[0] in '+-':
|
||||
sign = amt[0]
|
||||
amt = amt[1:]
|
||||
return func, (c_all, sign, float(amt.strip()))
|
||||
|
||||
|
||||
@func_with_args('clear_terminal')
|
||||
def clear_terminal(func: str, rest: str) -> FuncArgsType:
|
||||
vals = rest.strip().split(maxsplit=1)
|
||||
if len(vals) != 2:
|
||||
log_error('clear_terminal needs two arguments, using defaults')
|
||||
args: List[Union[str, bool]] = ['reset', 'active']
|
||||
else:
|
||||
args = [vals[0].lower(), vals[1].lower() == 'active']
|
||||
return func, args
|
||||
|
||||
|
||||
@func_with_args('copy_to_buffer')
|
||||
def copy_to_buffer(func: str, rest: str) -> FuncArgsType:
|
||||
return func, [rest]
|
||||
|
||||
|
||||
@func_with_args('paste_from_buffer')
|
||||
def paste_from_buffer(func: str, rest: str) -> FuncArgsType:
|
||||
return func, [rest]
|
||||
|
||||
|
||||
@func_with_args('neighboring_window')
|
||||
def neighboring_window(func: str, rest: str) -> FuncArgsType:
|
||||
rest = rest.lower()
|
||||
rest = {'up': 'top', 'down': 'bottom'}.get(rest, rest)
|
||||
if rest not in ('left', 'right', 'top', 'bottom'):
|
||||
log_error('Invalid neighbor specification: {}'.format(rest))
|
||||
rest = 'right'
|
||||
return func, [rest]
|
||||
|
||||
|
||||
@func_with_args('resize_window')
|
||||
def resize_window(func: str, rest: str) -> FuncArgsType:
|
||||
vals = rest.strip().split(maxsplit=1)
|
||||
if len(vals) > 2:
|
||||
log_error('resize_window needs one or two arguments, using defaults')
|
||||
args = ['wider', 1]
|
||||
else:
|
||||
quality = vals[0].lower()
|
||||
if quality not in ('taller', 'shorter', 'wider', 'narrower'):
|
||||
log_error('Invalid quality specification: {}'.format(quality))
|
||||
quality = 'wider'
|
||||
increment = 1
|
||||
if len(vals) == 2:
|
||||
try:
|
||||
increment = int(vals[1])
|
||||
except Exception:
|
||||
log_error('Invalid increment specification: {}'.format(vals[1]))
|
||||
args = [quality, increment]
|
||||
return func, args
|
||||
|
||||
|
||||
@func_with_args('move_window')
|
||||
def move_window(func: str, rest: str) -> FuncArgsType:
|
||||
rest = rest.lower()
|
||||
rest = {'up': 'top', 'down': 'bottom'}.get(rest, rest)
|
||||
prest: Union[int, str] = rest
|
||||
try:
|
||||
prest = int(prest)
|
||||
except Exception:
|
||||
if prest not in ('left', 'right', 'top', 'bottom'):
|
||||
log_error('Invalid move_window specification: {}'.format(rest))
|
||||
prest = 0
|
||||
return func, [prest]
|
||||
|
||||
|
||||
@func_with_args('pipe')
|
||||
def pipe(func: str, rest: str) -> FuncArgsType:
|
||||
import shlex
|
||||
r = shlex.split(rest)
|
||||
if len(r) < 3:
|
||||
log_error('Too few arguments to pipe function')
|
||||
r = ['none', 'none', 'true']
|
||||
return func, r
|
||||
|
||||
|
||||
@func_with_args('set_colors')
|
||||
def set_colors(func: str, rest: str) -> FuncArgsType:
|
||||
import shlex
|
||||
r = shlex.split(rest)
|
||||
if len(r) < 1:
|
||||
log_error('Too few arguments to set_colors function')
|
||||
return func, r
|
||||
|
||||
|
||||
@func_with_args('remote_control')
|
||||
def remote_control(func: str, rest: str) -> FuncArgsType:
|
||||
import shlex
|
||||
r = shlex.split(rest)
|
||||
if len(r) < 1:
|
||||
log_error('Too few arguments to remote_control function')
|
||||
return func, r
|
||||
|
||||
|
||||
@func_with_args('nth_window')
|
||||
def nth_window(func: str, rest: str) -> FuncArgsType:
|
||||
try:
|
||||
num = int(rest)
|
||||
except Exception:
|
||||
log_error('Invalid nth_window number: {}'.format(rest))
|
||||
num = 1
|
||||
return func, [num]
|
||||
|
||||
|
||||
@func_with_args('disable_ligatures_in')
|
||||
def disable_ligatures_in(func: str, rest: str) -> FuncArgsType:
|
||||
parts = rest.split(maxsplit=1)
|
||||
if len(parts) == 1:
|
||||
where, strategy = 'active', parts[0]
|
||||
else:
|
||||
where, strategy = parts
|
||||
if where not in ('active', 'all', 'tab'):
|
||||
raise ValueError('{} is not a valid set of windows to disable ligatures in'.format(where))
|
||||
if strategy not in ('never', 'always', 'cursor'):
|
||||
raise ValueError('{} is not a valid disable ligatures strategy'.format(strategy))
|
||||
return func, [where, strategy]
|
||||
|
||||
|
||||
@func_with_args('layout_action')
|
||||
def layout_action(func: str, rest: str) -> FuncArgsType:
|
||||
parts = rest.split(maxsplit=1)
|
||||
if not parts:
|
||||
raise ValueError('layout_action must have at least one argument')
|
||||
return func, [parts[0], tuple(parts[1:])]
|
||||
|
||||
|
||||
def parse_marker_spec(ftype: str, parts: Sequence[str]) -> Tuple[str, Union[str, Tuple[Tuple[int, str], ...]], int]:
|
||||
flags = re.UNICODE
|
||||
if ftype in ('text', 'itext', 'regex', 'iregex'):
|
||||
if ftype.startswith('i'):
|
||||
flags |= re.IGNORECASE
|
||||
if not parts or len(parts) % 2 != 0:
|
||||
raise ValueError('No color specified in marker: {}'.format(' '.join(parts)))
|
||||
ans = []
|
||||
for i in range(0, len(parts), 2):
|
||||
try:
|
||||
color = max(1, min(int(parts[i]), 3))
|
||||
except Exception:
|
||||
raise ValueError('color {} in marker specification is not an integer'.format(parts[i]))
|
||||
sspec = parts[i + 1]
|
||||
if 'regex' not in ftype:
|
||||
sspec = re.escape(sspec)
|
||||
ans.append((color, sspec))
|
||||
ftype = 'regex'
|
||||
spec: Union[str, Tuple[Tuple[int, str], ...]] = tuple(ans)
|
||||
elif ftype == 'function':
|
||||
spec = ' '.join(parts)
|
||||
else:
|
||||
raise ValueError('Unknown marker type: {}'.format(ftype))
|
||||
return ftype, spec, flags
|
||||
|
||||
|
||||
@func_with_args('toggle_marker')
|
||||
def toggle_marker(func: str, rest: str) -> FuncArgsType:
|
||||
import shlex
|
||||
parts = rest.split(maxsplit=1)
|
||||
if len(parts) != 2:
|
||||
raise ValueError('{} is not a valid marker specification'.format(rest))
|
||||
ftype, spec = parts
|
||||
parts = shlex.split(spec)
|
||||
return func, list(parse_marker_spec(ftype, parts))
|
||||
|
||||
|
||||
@func_with_args('scroll_to_mark')
|
||||
def scroll_to_mark(func: str, rest: str) -> FuncArgsType:
|
||||
parts = rest.split()
|
||||
if not parts or not rest:
|
||||
return func, [True, 0]
|
||||
if len(parts) == 1:
|
||||
q = parts[0].lower()
|
||||
if q in ('prev', 'previous', 'next'):
|
||||
return func, [q != 'next', 0]
|
||||
try:
|
||||
return func, [True, max(0, min(int(q), 3))]
|
||||
except Exception:
|
||||
raise ValueError('{} is not a valid scroll_to_mark destination'.format(rest))
|
||||
return func, [parts[0] != 'next', max(0, min(int(parts[1]), 3))]
|
||||
|
||||
|
||||
@func_with_args('mouse_selection')
|
||||
def mouse_selection(func: str, rest: str) -> FuncArgsType:
|
||||
cmap = getattr(mouse_selection, 'code_map', None)
|
||||
if cmap is None:
|
||||
cmap = {
|
||||
'normal': defines.MOUSE_SELECTION_NORMAL,
|
||||
'extend': defines.MOUSE_SELECTION_EXTEND,
|
||||
'rectangle': defines.MOUSE_SELECTION_RECTANGLE,
|
||||
'word': defines.MOUSE_SELECTION_WORD,
|
||||
'line': defines.MOUSE_SELECTION_LINE,
|
||||
'line_from_point': defines.MOUSE_SELECTION_LINE_FROM_POINT,
|
||||
}
|
||||
setattr(mouse_selection, 'code_map', cmap)
|
||||
return func, [cmap[rest]]
|
||||
# }}}
|
||||
|
||||
|
||||
def parse_mods(parts: Iterable[str], sc: str) -> Optional[int]:
|
||||
|
||||
def map_mod(m: str) -> str:
|
||||
@ -423,7 +728,7 @@ def symbol_map(val: str) -> Iterable[Tuple[Tuple[int, int], str]]:
|
||||
yield (a, b), family
|
||||
|
||||
|
||||
def parse_key_action(action: str) -> Optional[KeyAction]:
|
||||
def parse_key_action(action: str, action_type: str = 'map') -> Optional[KeyAction]:
|
||||
parts = action.strip().split(maxsplit=1)
|
||||
func = parts[0]
|
||||
if len(parts) == 1:
|
||||
@ -434,9 +739,11 @@ def parse_key_action(action: str) -> Optional[KeyAction]:
|
||||
try:
|
||||
func, args = parser(func, rest)
|
||||
except Exception as err:
|
||||
log_error('Ignoring invalid key action: {} with err: {}'.format(action, err))
|
||||
log_error(f'Ignoring invalid {action_type} action: {action} with err: {err}')
|
||||
else:
|
||||
return KeyAction(func, args)
|
||||
else:
|
||||
log_error(f'Ignoring unknown {action_type} action: {action}')
|
||||
return None
|
||||
|
||||
|
||||
@ -583,12 +890,11 @@ def parse_mouse_map(val: str) -> Iterable[MouseMapping]:
|
||||
log_error(f'Mouse modes: {modes} not recognized, ignoring')
|
||||
return
|
||||
try:
|
||||
paction = parse_key_action(action)
|
||||
paction = parse_key_action(action, 'mouse_map')
|
||||
except Exception:
|
||||
log_error(f'Invalid mouse action: {action}. Ignoring.')
|
||||
return
|
||||
if paction is None:
|
||||
log_error(f'Ignoring unknown mouse action: {action}')
|
||||
return
|
||||
for mode in specified_modes:
|
||||
yield MouseMapping(button, mods, count, mode == 'grabbed', paction)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user