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 json
|
||||||
import os
|
import os
|
||||||
import re
|
|
||||||
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, FrozenSet, Generator, Iterable, List, Optional,
|
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.definition import as_conf_file, config_lines
|
||||||
from .conf.utils import (
|
from .conf.utils import (
|
||||||
BadLine, init_config, load_config as _load_config, merge_dicts,
|
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 .config_data import all_options
|
||||||
from .constants import cache_dir, defconf, is_macos
|
from .constants import cache_dir, defconf, is_macos
|
||||||
from .options_stub import Options as OptionsStub
|
from .options_stub import Options as OptionsStub
|
||||||
from .options_types import (
|
from .options_types import (
|
||||||
FuncArgsType, KeyDefinition, KeyMap, MouseMap, MouseMapping, SequenceMap,
|
KeyDefinition, KeyMap, MouseMap, MouseMapping, SequenceMap, env,
|
||||||
env, font_features, func_with_args, kitten_alias, parse_key_action,
|
font_features, kitten_alias, parse_map, parse_mouse_map, symbol_map
|
||||||
parse_map, parse_mouse_map, symbol_map
|
|
||||||
)
|
)
|
||||||
from .typing import TypedDict
|
from .typing import TypedDict
|
||||||
from .utils import log_error
|
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:
|
def parse_send_text(val: str, key_definitions: List[KeyDefinition]) -> None:
|
||||||
parts = val.split(' ')
|
parts = val.split(' ')
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
import re
|
||||||
import sys
|
import sys
|
||||||
from typing import (
|
from typing import (
|
||||||
Any, Callable, Dict, FrozenSet, Iterable, List, NamedTuple, Optional,
|
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 kitty.fast_data_types import CURSOR_BEAM, CURSOR_BLOCK, CURSOR_UNDERLINE
|
||||||
|
|
||||||
from .conf.utils import (
|
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 .constants import config_dir
|
||||||
from .fonts import FontFeature
|
from .fonts import FontFeature
|
||||||
@ -53,6 +55,309 @@ class InvalidMods(ValueError):
|
|||||||
pass
|
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 parse_mods(parts: Iterable[str], sc: str) -> Optional[int]:
|
||||||
|
|
||||||
def map_mod(m: str) -> str:
|
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
|
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)
|
parts = action.strip().split(maxsplit=1)
|
||||||
func = parts[0]
|
func = parts[0]
|
||||||
if len(parts) == 1:
|
if len(parts) == 1:
|
||||||
@ -434,9 +739,11 @@ def parse_key_action(action: str) -> Optional[KeyAction]:
|
|||||||
try:
|
try:
|
||||||
func, args = parser(func, rest)
|
func, args = parser(func, rest)
|
||||||
except Exception as err:
|
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:
|
else:
|
||||||
return KeyAction(func, args)
|
return KeyAction(func, args)
|
||||||
|
else:
|
||||||
|
log_error(f'Ignoring unknown {action_type} action: {action}')
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
@ -583,12 +890,11 @@ def parse_mouse_map(val: str) -> Iterable[MouseMapping]:
|
|||||||
log_error(f'Mouse modes: {modes} not recognized, ignoring')
|
log_error(f'Mouse modes: {modes} not recognized, ignoring')
|
||||||
return
|
return
|
||||||
try:
|
try:
|
||||||
paction = parse_key_action(action)
|
paction = parse_key_action(action, 'mouse_map')
|
||||||
except Exception:
|
except Exception:
|
||||||
log_error(f'Invalid mouse action: {action}. Ignoring.')
|
log_error(f'Invalid mouse action: {action}. Ignoring.')
|
||||||
return
|
return
|
||||||
if paction is None:
|
if paction is None:
|
||||||
log_error(f'Ignoring unknown mouse action: {action}')
|
|
||||||
return
|
return
|
||||||
for mode in specified_modes:
|
for mode in specified_modes:
|
||||||
yield MouseMapping(button, mods, count, mode == 'grabbed', paction)
|
yield MouseMapping(button, mods, count, mode == 'grabbed', paction)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user