hints kitten: Add an option to select multiple items

Fixes #687
This commit is contained in:
Kovid Goyal 2018-06-29 06:49:16 +05:30
parent e3f60e6fa7
commit 65fef81f29
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
2 changed files with 45 additions and 16 deletions

View File

@ -14,6 +14,9 @@ Changelog
- macOS: Fix an error in remote control when using --listen-on (:iss:`679`) - macOS: Fix an error in remote control when using --listen-on (:iss:`679`)
- hints kitten: Add a :option:`kitty +kitten hints --multiple` option to select
multiple items (:iss:`687`)
- Fix pasting large amounts of text very slow (:iss:`682`) - Fix pasting large amounts of text very slow (:iss:`682`)
- Add a :option:`kitty --hold` command line option to stay open after the child process exits (:iss:`667`) - Add a :option:`kitty --hold` command line option to stay open after the child process exits (:iss:`667`)

View File

@ -24,7 +24,7 @@ HINT_ALPHABET = string.digits + string.ascii_lowercase
screen_size = screen_size_function() screen_size = screen_size_function()
class Mark(object): class Mark:
__slots__ = ('index', 'start', 'end', 'text') __slots__ = ('index', 'start', 'end', 'text')
@ -63,8 +63,10 @@ def highlight_mark(m, text, current_input):
) )
def render(text, current_input, all_marks): def render(text, current_input, all_marks, ignore_mark_indices):
for mark in reversed(all_marks): for mark in reversed(all_marks):
if mark.index in ignore_mark_indices:
continue
mtext = highlight_mark(mark, text[mark.start:mark.end], current_input) mtext = highlight_mark(mark, text[mark.start:mark.end], current_input)
text = text[:mark.start] + mtext + text[mark.end:] text = text[:mark.start] + mtext + text[mark.end:]
@ -78,11 +80,16 @@ class Hints(Handler):
def __init__(self, text, all_marks, index_map, args): def __init__(self, text, all_marks, index_map, args):
self.text, self.index_map = text, index_map self.text, self.index_map = text, index_map
self.all_marks = all_marks self.all_marks = all_marks
self.current_input = '' self.ignore_mark_indices = set()
self.current_text = None
self.args = args self.args = args
self.window_title = _('Choose URL') if args.type == 'url' else _('Choose text') self.window_title = _('Choose URL') if args.type == 'url' else _('Choose text')
self.chosen = None self.multiple = args.multiple
self.chosen = []
self.reset()
def reset(self):
self.current_input = ''
self.current_text = None
def init_terminal_state(self): def init_terminal_state(self):
self.cmd.set_cursor_visible(False) self.cmd.set_cursor_visible(False)
@ -101,11 +108,15 @@ class Hints(Handler):
changed = True changed = True
if changed: if changed:
matches = [ matches = [
m.text for idx, m in self.index_map.items() m for idx, m in self.index_map.items()
if encode_hint(idx).startswith(self.current_input) if encode_hint(idx).startswith(self.current_input)
] ]
if len(matches) == 1: if len(matches) == 1:
self.chosen = matches[0] self.chosen.append(matches[0].text)
if self.multiple:
self.ignore_mark_indices.add(matches[0].index)
self.reset()
else:
self.quit_loop(0) self.quit_loop(0)
return return
self.current_text = None self.current_text = None
@ -119,15 +130,20 @@ class Hints(Handler):
elif key_event is enter_key and self.current_input: elif key_event is enter_key and self.current_input:
try: try:
idx = decode_hint(self.current_input) idx = decode_hint(self.current_input)
self.chosen = self.index_map[idx].text self.chosen.append(self.index_map[idx].text)
self.ignore_mark_indices.add(idx)
except Exception: except Exception:
self.current_input = '' self.current_input = ''
self.current_text = None self.current_text = None
self.draw_screen() self.draw_screen()
else:
if self.multiple:
self.reset()
self.draw_screen()
else: else:
self.quit_loop(0) self.quit_loop(0)
elif key_event.key is ESCAPE: elif key_event.key is ESCAPE:
self.quit_loop(1) self.quit_loop(0 if self.multiple else 1)
def on_interrupt(self): def on_interrupt(self):
self.quit_loop(1) self.quit_loop(1)
@ -141,7 +157,7 @@ class Hints(Handler):
def draw_screen(self): def draw_screen(self):
if self.current_text is None: if self.current_text is None:
self.current_text = render(self.text, self.current_input, self.all_marks) self.current_text = render(self.text, self.current_input, self.all_marks, self.ignore_mark_indices)
self.cmd.clear_screen() self.cmd.clear_screen()
self.write(self.current_text) self.write(self.current_text)
@ -342,6 +358,12 @@ Defaults to the select_by_word_characters setting from kitty.conf.
default=3 default=3
type=int type=int
The minimum number of characters to consider a match. The minimum number of characters to consider a match.
--multiple
type=bool-set
Select multiple matches and perform the action on all of them together at the end.
In this mode, press :kbd:`Esc` to finish selecting.
'''.format(','.join(sorted(URL_PREFIXES))).format '''.format(','.join(sorted(URL_PREFIXES))).format
help_text = 'Select text from the screen using the keyboard. Defaults to searching for URLs.' help_text = 'Select text from the screen using the keyboard. Defaults to searching for URLs.'
usage = '' usage = ''
@ -377,18 +399,22 @@ def main(args):
def handle_result(args, data, target_window_id, boss): def handle_result(args, data, target_window_id, boss):
program = data['program'] program = data['program']
matches = tuple(filter(None, data['match']))
if program == '-': if program == '-':
w = boss.window_id_map.get(target_window_id) w = boss.window_id_map.get(target_window_id)
if w is not None: if w is not None:
w.paste(data['match']) for m in matches:
w.paste(m)
elif program == '@': elif program == '@':
set_clipboard_string(data['match']) set_clipboard_string(matches[-1])
else: else:
cwd = None cwd = None
w = boss.window_id_map.get(target_window_id) w = boss.window_id_map.get(target_window_id)
if w is not None: if w is not None:
cwd = w.cwd_of_child cwd = w.cwd_of_child
boss.open_url(data['match'], None if program == 'default' else program, cwd=cwd) program = None if program == 'default' else program
for m in matches:
boss.open_url(m, program, cwd=cwd)
handle_result.type_of_input = 'screen' handle_result.type_of_input = 'screen'