hints kitten: Allow completely customizing the matching and actions performed by the kitten using your own script

Fixes #2124
This commit is contained in:
Kovid Goyal 2019-11-11 21:26:42 +05:30
parent 9edad62144
commit 400ab584ac
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
3 changed files with 85 additions and 5 deletions

View File

@ -16,6 +16,9 @@ To update |kitty|, :doc:`follow the instructions <binary>`.
file with color definitions. See the :doc:`FAQ <faq>` for details
(:iss:`2083`)
- hints kitten: Allow completely customizing the matching and actions performed
by the kitten using your own script (:iss:`2124`)
- Wayland: Fix key repeat not being stopped when focus leaves window. This is
expected behavior on Wayland, apparently (:iss:`2014`)

View File

@ -23,6 +23,55 @@ options and modes of operation, see below. You can use these options to
create mappings in :file:`kitty.conf` to select various different text
snippets. See :sc:`insert_selected_path` for examples.
Completely customizing the matching and actions of the kitten
---------------------------------------------------------------
The hints kitten supports writing simple python scripts that can be used to
completely customize how it finds matches and what happens when a match is
selected. This allows the hints kitten to provide the user interface, while
you can provide the logic for finding matches and performing actions on them.
This is best illustrated with an example. Create the file
:file:`custom-hints.py` in the kitty config directory with the following
contents:
.. code-block:: python
import re
def mark(text, args, Mark, *a):
# This function is responsible for finding all
# matching text.
# We mark all individual word for potential selection
for idx, m in enumerate(re.finditer(r'\w+', text)):
start, end = m.span()
mark_text = text[start:end].replace('\n', '').replace('\0', '')
# The empty dictionary below will be available as groupdicts
# in handle_result() and can contain arbitrary data.
yield Mark(idx, start, end, mark_text, {})
def handle_result(args, data, target_window_id, boss):
# This function is responsible for performing some
# action on the selected text.
# matches is a list of the selected entries and groupdicts contains
# the arbitrary data associated with each entry in mark() above
matches, groupdicts = [], []
for m, g in zip(data['match'], data['groupdicts']):
if m:
matches.append(m), groupdicts.append(g)
for word, data in zip(matches, groupdicts):
# Lookup the word in a dictionary, the open_url function
# will open the provided url in the system browser
boss.open_url(f'https://www.google.com/search?q=define:{word}')
Nor run kitty with::
kitty -o 'map f1 kitten hints --customize-processing custom-hints.py'
and when you press the :kbd:`F1` key you will be able to select a word to
look it up in the Google dictionary.
Command Line Interface
-------------------------

View File

@ -262,9 +262,11 @@ def run_loop(args, text, all_marks, index_map):
handler = Hints(text, all_marks, index_map, args)
loop.loop(handler)
if handler.chosen and loop.return_code == 0:
return {'match': handler.text_matches, 'programs': args.program,
'multiple_joiner': args.multiple_joiner,
'type': args.type, 'groupdicts': handler.groupdicts}
return {
'match': handler.text_matches, 'programs': args.program,
'multiple_joiner': args.multiple_joiner, 'customize_processing': args.customize_processing,
'type': args.type, 'groupdicts': handler.groupdicts
}
raise SystemExit(loop.return_code)
@ -321,11 +323,25 @@ def parse_input(text):
return convert_text(text, cols)
def load_custom_processor(customize_processing):
from kitty.constants import config_dir
custom_path = os.path.join(config_dir, customize_processing)
import runpy
return runpy.run_path(custom_path, run_name='__main__')
def run(args, text):
try:
pattern, post_processors = functions_for(args)
text = parse_input(text)
all_marks = tuple(mark(pattern, post_processors, text, args))
pattern, post_processors = functions_for(args)
if args.customize_processing:
m = load_custom_processor(args.customize_processing)
if 'mark' in m:
all_marks = tuple(m['mark'](text, args, Mark))
else:
all_marks = tuple(mark(pattern, post_processors, text, args))
else:
all_marks = tuple(mark(pattern, post_processors, text, args))
if not all_marks:
input(_('No {} found, press Enter to quit.').format(
'URLs' if args.type == 'url' else 'matches'
@ -431,6 +447,13 @@ unless you specify the hints offset as zero the first match will be highlighted
the second character you specify.
--customize-processing
Name of a python file in the kitty config directory which will be imported to provide
custom implementations for pattern finding and performing actions
on selected matches. See https://sw.kovidgoyal.net/kitty/kittens/hints.html
for details.
'''.format(','.join(sorted(URL_PREFIXES))).format
help_text = 'Select text from the screen using the keyboard. Defaults to searching for URLs.'
usage = ''
@ -464,6 +487,11 @@ def main(args):
def handle_result(args, data, target_window_id, boss):
if data['customize_processing']:
m = load_custom_processor(data['customize_processing'])
if 'handle_result' in m:
return m['handle_result'](args, data, target_window_id, boss)
programs = data['programs'] or ('default',)
matches, groupdicts = [], []
for m, g in zip(data['match'], data['groupdicts']):