From 65fef81f296fd9cd4f79cbef39edb2b0860f1ecc Mon Sep 17 00:00:00 2001 From: Kovid Goyal Date: Fri, 29 Jun 2018 06:49:16 +0530 Subject: [PATCH] hints kitten: Add an option to select multiple items Fixes #687 --- docs/changelog.rst | 3 +++ kittens/hints/main.py | 58 +++++++++++++++++++++++++++++++------------ 2 files changed, 45 insertions(+), 16 deletions(-) diff --git a/docs/changelog.rst b/docs/changelog.rst index d73bca54c..557a07b5e 100644 --- a/docs/changelog.rst +++ b/docs/changelog.rst @@ -14,6 +14,9 @@ Changelog - 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`) - Add a :option:`kitty --hold` command line option to stay open after the child process exits (:iss:`667`) diff --git a/kittens/hints/main.py b/kittens/hints/main.py index 880c4639a..b7560c689 100644 --- a/kittens/hints/main.py +++ b/kittens/hints/main.py @@ -24,7 +24,7 @@ HINT_ALPHABET = string.digits + string.ascii_lowercase screen_size = screen_size_function() -class Mark(object): +class Mark: __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): + if mark.index in ignore_mark_indices: + continue mtext = highlight_mark(mark, text[mark.start:mark.end], current_input) text = text[:mark.start] + mtext + text[mark.end:] @@ -78,11 +80,16 @@ class Hints(Handler): def __init__(self, text, all_marks, index_map, args): self.text, self.index_map = text, index_map self.all_marks = all_marks - self.current_input = '' - self.current_text = None + self.ignore_mark_indices = set() self.args = args 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): self.cmd.set_cursor_visible(False) @@ -101,13 +108,17 @@ class Hints(Handler): changed = True if changed: 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 len(matches) == 1: - self.chosen = matches[0] - self.quit_loop(0) - return + self.chosen.append(matches[0].text) + if self.multiple: + self.ignore_mark_indices.add(matches[0].index) + self.reset() + else: + self.quit_loop(0) + return self.current_text = None self.draw_screen() @@ -119,15 +130,20 @@ class Hints(Handler): elif key_event is enter_key and self.current_input: try: 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: self.current_input = '' self.current_text = None self.draw_screen() else: - self.quit_loop(0) + if self.multiple: + self.reset() + self.draw_screen() + else: + self.quit_loop(0) elif key_event.key is ESCAPE: - self.quit_loop(1) + self.quit_loop(0 if self.multiple else 1) def on_interrupt(self): self.quit_loop(1) @@ -141,7 +157,7 @@ class Hints(Handler): def draw_screen(self): 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.write(self.current_text) @@ -342,6 +358,12 @@ Defaults to the select_by_word_characters setting from kitty.conf. default=3 type=int 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 help_text = 'Select text from the screen using the keyboard. Defaults to searching for URLs.' usage = '' @@ -377,18 +399,22 @@ def main(args): def handle_result(args, data, target_window_id, boss): program = data['program'] + matches = tuple(filter(None, data['match'])) if program == '-': w = boss.window_id_map.get(target_window_id) if w is not None: - w.paste(data['match']) + for m in matches: + w.paste(m) elif program == '@': - set_clipboard_string(data['match']) + set_clipboard_string(matches[-1]) else: cwd = None w = boss.window_id_map.get(target_window_id) if w is not None: 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'