diff --git a/docs/marks.rst b/docs/marks.rst index 5dd315edd..3e0345565 100644 --- a/docs/marks.rst +++ b/docs/marks.rst @@ -37,7 +37,7 @@ can control the colors used for these groups in :file:`kitty.conf` with:: .. note:: For performance reasons, matching is done per line only, and only when that line is - altered in anyway. So you cannot match text that stretches across multiple + altered in any way. So you cannot match text that stretches across multiple lines. @@ -53,6 +53,9 @@ If you want to create markers dynamically rather than pre-defining them in Then pressing :kbd:`F1` will allow you to enter the marker definition and set it and pressing :kbd:`F2` will remove the marker. +You can also use the facilities for :doc:`remote-control` to dynamically +add/remove markers. + The full syntax for creating marks ------------------------------------- diff --git a/kitty/cmds.py b/kitty/cmds.py index 0154993b6..f934aef60 100644 --- a/kitty/cmds.py +++ b/kitty/cmds.py @@ -381,6 +381,82 @@ def set_tab_title(boss, window, payload): # }}} +# create_marker {{{ +@cmd( + 'Create a marker that highlights specified text', + 'Create a marker which can highlight text in the specified window. For example: ' + 'create_marker text 1 ERROR. For full details see: https://sw.kovidgoyal.net/kitty/marks.html', + options_spec=MATCH_WINDOW_OPTION + '''\n +--self +type=bool-set +If specified apply marker to the window this command is run in, rather than the active window. +''', + argspec='MARKER SPECIFICATION' +) +def cmd_create_marker(global_opts, opts, args): + ''' + match: Which window to detach + self: Boolean indicating whether to detach the window the command is run in + marker_spec: A list or arguments that define the marker specification, for example: ['text', '1', 'ERROR'] + ''' + from .config import parse_marker_spec + if len(args) < 2: + raise ValueError('Invalid marker specification: {}'.format(' '.join(args))) + parse_marker_spec(args[0], args[1:]) + return {'match': opts.match, 'self': opts.self, 'marker_spec': args} + + +def create_marker(boss, window, payload): + pg = cmd_create_marker.payload_get + match = pg(payload, 'match') + if match: + windows = tuple(boss.match_windows(match)) + if not windows: + raise MatchError(match) + else: + windows = [window if window and pg(payload, 'self') else boss.active_window] + args = pg(payload, 'marker_spec') + + for window in windows: + window.set_marker(args) + +# }}} + + +# remove_marker {{{ +@cmd( + 'Remove the currently set marker, if any.', + options_spec=MATCH_WINDOW_OPTION + '''\n +--self +type=bool-set +If specified apply marker to the window this command is run in, rather than the active window. +''', + argspec='' +) +def cmd_remove_marker(global_opts, opts, args): + ''' + match: Which window to detach + self: Boolean indicating whether to detach the window the command is run in + ''' + return {'match': opts.match, 'self': opts.self} + + +def remove_marker(boss, window, payload): + pg = cmd_create_marker.payload_get + match = pg(payload, 'match') + if match: + windows = tuple(boss.match_windows(match)) + if not windows: + raise MatchError(match) + else: + windows = [window if window and pg(payload, 'self') else boss.active_window] + + for window in windows: + window.remove_marker() + +# }}} + + # detach_window {{{ @cmd( 'Detach a window and place it in a different/new tab', diff --git a/kitty/config.py b/kitty/config.py index 19afa9dcc..30905aea9 100644 --- a/kitty/config.py +++ b/kitty/config.py @@ -241,19 +241,13 @@ def disable_ligatures_in(func, rest): return func, [where, strategy] -@func_with_args('toggle_marker') -def toggle_marker(func, rest): - parts = rest.split(maxsplit=1) - if len(parts) != 2: - raise ValueError('{} if not a valid marker specification'.format(rest)) - ftype, spec = parts +def parse_marker_spec(ftype, parts): flags = re.UNICODE if ftype in ('text', 'itext', 'regex', 'iregex'): - parts = spec.split() if ftype.startswith('i'): flags |= re.IGNORECASE if not parts or len(parts) % 2 != 0: - raise ValueError('No color specified in marker: {}'.format(spec)) + raise ValueError('No color specified in marker: {}'.format(' '.join(parts))) ans = [] for i in range(0, len(parts), 2): try: @@ -267,10 +261,20 @@ def toggle_marker(func, rest): ftype = 'regex' spec = tuple(ans) elif ftype == 'function': - pass + spec = ' '.join(parts) else: raise ValueError('Unknown marker type: {}'.format(ftype)) - return func, [ftype, spec, flags] + return ftype, spec, flags + + +@func_with_args('toggle_marker') +def toggle_marker(func, rest): + parts = rest.split(maxsplit=1) + if len(parts) != 2: + raise ValueError('{} if not a valid marker specification'.format(rest)) + ftype, spec = parts + parts = spec.split() + return func, list(parse_marker_spec(ftype, parts)) def parse_key_action(action): diff --git a/kitty/window.py b/kitty/window.py index 25796521f..1b11ea595 100644 --- a/kitty/window.py +++ b/kitty/window.py @@ -617,9 +617,12 @@ class Window: self.current_marker_spec = key def set_marker(self, spec): - from .config import toggle_marker + from .config import toggle_marker, parse_marker_spec from .marks import marker_from_spec - func, (ftype, spec, flags) = toggle_marker('toggle_marker', spec) + if isinstance(spec, str): + func, (ftype, spec, flags) = toggle_marker('toggle_marker', spec) + else: + ftype, spec, flags = parse_marker_spec(spec[0], spec[1:]) key = ftype, spec self.screen.set_marker(marker_from_spec(ftype, spec, flags)) self.current_marker_spec = key