A new watcher option for kitty.conf that replaces the old --watcher cli flag
Applies to all windows, not just initial ones.
This commit is contained in:
parent
7a16ef2cc4
commit
166ea9deb9
@ -18,6 +18,11 @@ To update |kitty|, :doc:`follow the instructions <binary>`.
|
||||
- Allow the user to supply a custom Python function to draw tab bar. See
|
||||
:opt:`tab_bar_style`
|
||||
|
||||
- **Backward incompatibility**: The command line option ``--watcher`` has been
|
||||
removed in favor of the :opt:`watcher` option in :file:`kitty.conf`. It can be set
|
||||
from the command line as: ``kitty -o watcher=/path/to/watcher``. It has the
|
||||
advantage of applying to all windows, not just the initially created ones.
|
||||
|
||||
- Add support for reporting mouse events with pixel co-ordinates using the
|
||||
``SGR_PIXEL_PROTOCOL`` introduced in xterm 359
|
||||
|
||||
|
||||
@ -89,6 +89,8 @@ For example::
|
||||
map f1 launch my-program @active-kitty-window-id
|
||||
|
||||
|
||||
.. _watchers:
|
||||
|
||||
Watching launched windows
|
||||
---------------------------
|
||||
|
||||
|
||||
15
kitty/cli.py
15
kitty/cli.py
@ -571,15 +571,6 @@ def parse_cmdline(oc: Options, disabled: OptionSpecSeq, ans: Any, args: Optional
|
||||
return leftover_args
|
||||
|
||||
|
||||
WATCHER_DEFINITION = '''
|
||||
--watcher -w
|
||||
type=list
|
||||
Path to a python file. Appropriately named functions in this file will be called
|
||||
for various events, such as when the window is resized, focused or closed. See the section
|
||||
on watchers in the launch command documentation :doc:`launch`. Relative paths are
|
||||
resolved relative to the kitty config directory.'''
|
||||
|
||||
|
||||
def options_spec() -> str:
|
||||
if not hasattr(options_spec, 'ans'):
|
||||
OPTIONS = '''
|
||||
@ -627,11 +618,6 @@ Path to a file containing the startup :italic:`session` (tabs, windows, layout,
|
||||
Use - to read from STDIN. See the README file for details and an example.
|
||||
|
||||
|
||||
{watcher}
|
||||
Note that this watcher will be added only to all initially created windows, not new windows
|
||||
created after startup.
|
||||
|
||||
|
||||
--hold
|
||||
type=bool-set
|
||||
Remain open after child process exits. Note that this only affects the first
|
||||
@ -731,7 +717,6 @@ type=bool-set
|
||||
'''
|
||||
setattr(options_spec, 'ans', OPTIONS.format(
|
||||
appname=appname, config_help=CONFIG_HELP.format(appname=appname, conf_name=appname),
|
||||
watcher=WATCHER_DEFINITION
|
||||
))
|
||||
ans: str = getattr(options_spec, 'ans')
|
||||
return ans
|
||||
|
||||
@ -44,6 +44,7 @@ def expand_opt_references(conf_name: str, text: str) -> str:
|
||||
def remove_markup(text: str) -> str:
|
||||
ref_map = {
|
||||
'layouts': f'{website_url("overview")}#layouts',
|
||||
'watchers': f'{website_url("launch")}#watchers',
|
||||
'sessions': f'{website_url("overview")}#startup-sessions',
|
||||
'functional': f'{website_url("keyboard-protocol")}#functional-key-definitions',
|
||||
'action-select_tab': f'{website_url("actions")}#select-tab',
|
||||
|
||||
@ -3,21 +3,21 @@
|
||||
# License: GPLv3 Copyright: 2019, Kovid Goyal <kovid at kovidgoyal.net>
|
||||
|
||||
|
||||
from typing import Any, Dict, List, NamedTuple, Optional, Sequence
|
||||
from typing import Any, Dict, Iterable, List, NamedTuple, Optional, Sequence
|
||||
|
||||
from .boss import Boss
|
||||
from .child import Child
|
||||
from .cli import WATCHER_DEFINITION, parse_args
|
||||
from .cli import parse_args
|
||||
from .cli_stub import LaunchCLIOptions
|
||||
from .constants import resolve_custom_file
|
||||
from .fast_data_types import (
|
||||
get_options, patch_color_profiles, set_clipboard_string
|
||||
)
|
||||
from .options.utils import env as parse_env
|
||||
from .tabs import Tab
|
||||
from .types import run_once
|
||||
from .utils import find_exe, read_shell_environment, set_primary_selection
|
||||
from .utils import find_exe, read_shell_environment, set_primary_selection, log_error
|
||||
from .window import Watchers, Window
|
||||
from .options.utils import env as parse_env
|
||||
|
||||
try:
|
||||
from typing import TypedDict
|
||||
@ -178,7 +178,14 @@ file with the same syntax as kitty.conf to read the colors from, or specify them
|
||||
individually, for example: ``--color background=white`` ``--color foreground=red``
|
||||
|
||||
|
||||
''' + WATCHER_DEFINITION
|
||||
--watcher -w
|
||||
type=list
|
||||
Path to a python file. Appropriately named functions in this file will be called
|
||||
for various events, such as when the window is resized, focused or closed. See the section
|
||||
on watchers in the launch command documentation: :ref:`watchers`. Relative paths are
|
||||
resolved relative to the kitty config directory. Global watchers for all windows can be
|
||||
specified with :opt:`watcher`.
|
||||
'''
|
||||
|
||||
|
||||
def parse_launch_args(args: Optional[Sequence[str]] = None) -> LaunchSpec:
|
||||
@ -221,14 +228,29 @@ def tab_for_window(boss: Boss, opts: LaunchCLIOptions, target_tab: Optional[Tab]
|
||||
return tab
|
||||
|
||||
|
||||
def load_watch_modules(watchers: Sequence[str]) -> Optional[Watchers]:
|
||||
watcher_modules: Dict[str, Any] = {}
|
||||
|
||||
|
||||
def load_watch_modules(watchers: Iterable[str]) -> Optional[Watchers]:
|
||||
if not watchers:
|
||||
return None
|
||||
import runpy
|
||||
ans = Watchers()
|
||||
for path in watchers:
|
||||
path = resolve_custom_file(path)
|
||||
m = watcher_modules.get(path, None)
|
||||
if m is None:
|
||||
try:
|
||||
m = runpy.run_path(path, run_name='__kitty_watcher__')
|
||||
except Exception as err:
|
||||
import traceback
|
||||
log_error(traceback.format_exc())
|
||||
log_error(f'Failed to load watcher from {path} with error: {err}')
|
||||
watcher_modules[path] = False
|
||||
continue
|
||||
watcher_modules[path] = m
|
||||
if m is False:
|
||||
continue
|
||||
w = m.get('on_close')
|
||||
if callable(w):
|
||||
ans.on_close.append(w)
|
||||
|
||||
@ -2532,6 +2532,18 @@ will delete the variable from the child process' environment.
|
||||
'''
|
||||
)
|
||||
|
||||
opt('+watcher', '',
|
||||
option_type='watcher',
|
||||
add_to_default=False,
|
||||
long_text='''
|
||||
Path to python file which will be loaded for :ref:`watchers`.
|
||||
Can be specified more than once to load multiple watchers.
|
||||
The watchers will be added to every kitty window. Relative
|
||||
paths are resolved relative to the kitty config directory.
|
||||
Note that reloading the config will only affect windows
|
||||
created after the reload.
|
||||
''')
|
||||
|
||||
opt('update_check_interval', '24',
|
||||
option_type='float',
|
||||
long_text='''
|
||||
|
||||
7
kitty/options/parse.py
generated
7
kitty/options/parse.py
generated
@ -16,7 +16,7 @@ from kitty.options.utils import (
|
||||
parse_mouse_map, resize_draw_strategy, scrollback_lines, scrollback_pager_history_size, symbol_map,
|
||||
tab_activity_symbol, tab_bar_edge, tab_bar_margin_height, tab_bar_min_tabs, tab_fade,
|
||||
tab_font_style, tab_separator, tab_title_template, to_cursor_shape, to_font_size, to_layout_names,
|
||||
to_modifiers, url_prefixes, url_style, window_border_width, window_size
|
||||
to_modifiers, url_prefixes, url_style, watcher, window_border_width, window_size
|
||||
)
|
||||
|
||||
|
||||
@ -1241,6 +1241,10 @@ class Parser:
|
||||
def visual_bell_duration(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
|
||||
ans['visual_bell_duration'] = positive_float(val)
|
||||
|
||||
def watcher(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
|
||||
for k, v in watcher(val, ans["watcher"]):
|
||||
ans["watcher"][k] = v
|
||||
|
||||
def wayland_titlebar_color(self, val: str, ans: typing.Dict[str, typing.Any]) -> None:
|
||||
ans['wayland_titlebar_color'] = macos_titlebar_color(val)
|
||||
|
||||
@ -1292,6 +1296,7 @@ def create_result_dict() -> typing.Dict[str, typing.Any]:
|
||||
'font_features': {},
|
||||
'kitten_alias': {},
|
||||
'symbol_map': {},
|
||||
'watcher': {},
|
||||
'map': [],
|
||||
'mouse_map': [],
|
||||
}
|
||||
|
||||
3
kitty/options/types.py
generated
3
kitty/options/types.py
generated
@ -429,6 +429,7 @@ option_names = ( # {{{
|
||||
'url_prefixes',
|
||||
'url_style',
|
||||
'visual_bell_duration',
|
||||
'watcher',
|
||||
'wayland_titlebar_color',
|
||||
'wheel_scroll_multiplier',
|
||||
'window_alert_on_bell',
|
||||
@ -577,6 +578,7 @@ class Options:
|
||||
font_features: typing.Dict[str, typing.Tuple[kitty.fonts.FontFeature, ...]] = {}
|
||||
kitten_alias: typing.Dict[str, typing.List[str]] = {}
|
||||
symbol_map: typing.Dict[typing.Tuple[int, int], str] = {}
|
||||
watcher: typing.Dict[str, str] = {}
|
||||
map: typing.List[kitty.options.utils.KeyDefinition] = []
|
||||
keymap: KeyMap = {}
|
||||
sequence_map: SequenceMap = {}
|
||||
@ -691,6 +693,7 @@ defaults.env = {}
|
||||
defaults.font_features = {}
|
||||
defaults.kitten_alias = {}
|
||||
defaults.symbol_map = {}
|
||||
defaults.watcher = {}
|
||||
defaults.map = [
|
||||
# copy_to_clipboard
|
||||
KeyDefinition(False, KeyAction('copy_to_clipboard'), 1024, False, 99, ()),
|
||||
|
||||
@ -7,8 +7,8 @@ import os
|
||||
import re
|
||||
import sys
|
||||
from typing import (
|
||||
Any, Callable, Dict, Iterable, List, NamedTuple, Optional, Sequence, Tuple,
|
||||
Union
|
||||
Any, Callable, Container, Dict, Iterable, List, NamedTuple, Optional,
|
||||
Sequence, Tuple, Union
|
||||
)
|
||||
|
||||
import kitty.fast_data_types as defines
|
||||
@ -745,6 +745,12 @@ def env(val: str, current_val: Dict[str, str]) -> Iterable[Tuple[str, str]]:
|
||||
yield val, DELETE_ENV_VAR
|
||||
|
||||
|
||||
def watcher(val: str, current_val: Container[str]) -> Iterable[Tuple[str, str]]:
|
||||
val = val.strip()
|
||||
if val not in current_val:
|
||||
yield val, val
|
||||
|
||||
|
||||
def kitten_alias(val: str) -> Iterable[Tuple[str, List[str]]]:
|
||||
parts = val.split(maxsplit=2)
|
||||
if len(parts) >= 2:
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
import shlex
|
||||
import sys
|
||||
from typing import Generator, List, Optional, Sequence, Union
|
||||
from typing import Generator, List, Optional, Union
|
||||
|
||||
from .cli_stub import CLIOptions
|
||||
from .options.utils import to_layout_names, window_size
|
||||
@ -39,9 +39,8 @@ class Tab:
|
||||
|
||||
class Session:
|
||||
|
||||
def __init__(self, default_title: Optional[str] = None, default_watchers: Sequence[str] = ()):
|
||||
def __init__(self, default_title: Optional[str] = None):
|
||||
self.tabs: List[Tab] = []
|
||||
self.default_watchers = list(default_watchers)
|
||||
self.active_tab_idx = 0
|
||||
self.default_title = default_title
|
||||
self.os_window_size: Optional[WindowSizes] = None
|
||||
@ -65,8 +64,6 @@ class Session:
|
||||
if isinstance(cmd, str):
|
||||
cmd = shlex.split(cmd)
|
||||
spec = parse_launch_args(cmd)
|
||||
if self.default_watchers:
|
||||
spec.opts.watcher = list(spec.opts.watcher) + self.default_watchers
|
||||
t = self.tabs[-1]
|
||||
if t.next_title and not spec.opts.window_title:
|
||||
spec.opts.window_title = t.next_title
|
||||
@ -175,8 +172,7 @@ def create_sessions(
|
||||
else:
|
||||
yield from parse_session(session_data, opts)
|
||||
return
|
||||
default_watchers = args.watcher if args else ()
|
||||
ans = Session(default_watchers=default_watchers)
|
||||
ans = Session()
|
||||
current_layout = opts.enabled_layouts[0] if opts.enabled_layouts else 'tall'
|
||||
ans.add_tab(opts)
|
||||
ans.tabs[-1].layout = current_layout
|
||||
|
||||
@ -13,7 +13,7 @@ from gettext import gettext as _
|
||||
from itertools import chain
|
||||
from typing import (
|
||||
TYPE_CHECKING, Any, Callable, Deque, Dict, Iterable, List, NamedTuple,
|
||||
Optional, Pattern, Sequence, Tuple, Union
|
||||
Optional, Pattern, Sequence, Tuple, Union, cast
|
||||
)
|
||||
|
||||
from .child import ProcessDesc
|
||||
@ -315,6 +315,17 @@ class EdgeWidths:
|
||||
return {'left': self.left, 'right': self.right, 'top': self.top, 'bottom': self.bottom}
|
||||
|
||||
|
||||
def global_watchers() -> Watchers:
|
||||
spec = get_options().watcher
|
||||
if getattr(global_watchers, 'options_spec', None) == spec:
|
||||
return cast(Watchers, getattr(global_watchers, 'ans'))
|
||||
from .launch import load_watch_modules
|
||||
ans = load_watch_modules(spec) or Watchers()
|
||||
setattr(global_watchers, 'ans', ans)
|
||||
setattr(global_watchers, 'options_spec', spec)
|
||||
return ans
|
||||
|
||||
|
||||
class Window:
|
||||
|
||||
def __init__(
|
||||
@ -326,7 +337,11 @@ class Window:
|
||||
copy_colors_from: Optional['Window'] = None,
|
||||
watchers: Optional[Watchers] = None
|
||||
):
|
||||
self.watchers = watchers or Watchers()
|
||||
if watchers:
|
||||
self.watchers = watchers
|
||||
self.watchers.add(global_watchers())
|
||||
else:
|
||||
self.watchers = global_watchers()
|
||||
self.current_mouse_event_button = 0
|
||||
self.current_clipboard_read_ask: Optional[bool] = None
|
||||
self.prev_osc99_cmd = NotificationCommand()
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user