hints kitten: Allow pressing :sc:goto_file_line to quickly open the selected file at the selected line in vim
Fixes #2268
This commit is contained in:
parent
6a8b7bf92f
commit
9bc1b5a2d9
@ -7,6 +7,9 @@ To update |kitty|, :doc:`follow the instructions <binary>`.
|
||||
0.15.2 [future]
|
||||
--------------------
|
||||
|
||||
- hints kitten: Allow pressing :sc:`goto_file_line` to quickly open
|
||||
the selected file at the selected line in vim (:iss:`2268`)
|
||||
|
||||
- Allow choosing OpenType features for individual fonts via the
|
||||
:opt:`font_features` option.
|
||||
|
||||
|
||||
@ -18,6 +18,11 @@ select anything that looks like a path or filename and then insert it into the
|
||||
terminal, very useful for picking files from the output of a ``git`` or ``ls`` command and
|
||||
adding them to the command line for the next command.
|
||||
|
||||
You can also press :sc:`goto_file_line` to select anything that looks
|
||||
like a path or filename followed by a colon and a line number and open
|
||||
the file in vim at the specified line number. The patterns and editor
|
||||
to be used can be modified using options passed to the kitten.
|
||||
|
||||
The hints kitten is very powerful to see more detailed help on its various
|
||||
options and modes of operation, see below. You can use these options to
|
||||
create mappings in :file:`kitty.conf` to select various different text
|
||||
|
||||
@ -21,6 +21,7 @@ from ..tui.operations import faint, styled
|
||||
|
||||
URL_PREFIXES = 'http https file ftp'.split()
|
||||
DEFAULT_HINT_ALPHABET = string.digits + string.ascii_lowercase
|
||||
DEFAULT_REGEX = r'(?m)^\s*(.+)\s*$'
|
||||
screen_size = screen_size_function()
|
||||
|
||||
|
||||
@ -265,7 +266,7 @@ def run_loop(args, text, all_marks, index_map, extra_cli_args=()):
|
||||
return {
|
||||
'match': handler.text_matches, 'programs': args.program,
|
||||
'multiple_joiner': args.multiple_joiner, 'customize_processing': args.customize_processing,
|
||||
'type': args.type, 'groupdicts': handler.groupdicts, 'extra_cli_args': extra_cli_args
|
||||
'type': args.type, 'groupdicts': handler.groupdicts, 'extra_cli_args': extra_cli_args, 'linenum_action': args.linenum_action
|
||||
}
|
||||
raise SystemExit(loop.return_code)
|
||||
|
||||
@ -323,11 +324,20 @@ def parse_input(text):
|
||||
return convert_text(text, cols)
|
||||
|
||||
|
||||
def linenum_marks(text, args, Mark, extra_cli_args, *a):
|
||||
regex = args.regex
|
||||
if regex == DEFAULT_REGEX:
|
||||
regex = r'(?P<path>(?:\S*/\S+)|(?:\S+[.][a-zA-Z0-9]{2,7})):(?P<line>\d+)'
|
||||
yield from mark(regex, [brackets, quotes], text, args)
|
||||
|
||||
|
||||
def load_custom_processor(customize_processing):
|
||||
if customize_processing.startswith('::import::'):
|
||||
import importlib
|
||||
m = importlib.import_module(customize_processing[len('::import::'):])
|
||||
return {k: getattr(m, k) for k in dir(m)}
|
||||
if customize_processing == '::linenum::':
|
||||
return {'mark': linenum_marks, 'handle_result': linenum_handle_result}
|
||||
from kitty.constants import config_dir
|
||||
customize_processing = os.path.expandvars(os.path.expanduser(customize_processing))
|
||||
if os.path.isabs(customize_processing):
|
||||
@ -342,6 +352,8 @@ def run(args, text, extra_cli_args=()):
|
||||
try:
|
||||
text = parse_input(text)
|
||||
pattern, post_processors = functions_for(args)
|
||||
if args.type == 'linenum':
|
||||
args.customize_processing = '::linenum::'
|
||||
if args.customize_processing:
|
||||
m = load_custom_processor(args.customize_processing)
|
||||
if 'mark' in m:
|
||||
@ -386,12 +398,15 @@ multiple times to run multiple programs.
|
||||
|
||||
--type
|
||||
default=url
|
||||
choices=url,regex,path,line,hash,word
|
||||
The type of text to search for.
|
||||
choices=url,regex,path,line,hash,word,linenum
|
||||
The type of text to search for. A value of :code:`linenum` looks for error messages
|
||||
using the pattern specified with :option:`--regex`, which must have the named groups,
|
||||
path and line. If not specified, will look for :code:`path:line`.
|
||||
The :option:`--linenum-action` option controls what to do with the selected error message.
|
||||
|
||||
|
||||
--regex
|
||||
default=(?m)^\s*(.+)\s*$
|
||||
default={default_regex}
|
||||
The regular expression to use when :option:`kitty +kitten hints --type`=regex.
|
||||
The regular expression is in python syntax. If you specify a numbered group in
|
||||
the regular expression only the group will be matched. This allow you to match
|
||||
@ -403,8 +418,21 @@ the program will be passed arguments corresponding to each named group of
|
||||
the form key=value.
|
||||
|
||||
|
||||
--linenum-action
|
||||
default=self
|
||||
type=choice
|
||||
choices=self,window,tab,os_window,background
|
||||
The action to perform on the matched errors. The actual action is whatever
|
||||
arguments are provided to the kitten, for example:
|
||||
:code:`kitty + kitten hints --type=linenum vim +{line} {path}`
|
||||
will open the matched path at the matched line number in vim. This option
|
||||
controls where the action is executed: :code:`self` means the current window,
|
||||
:code:`window` a new kitty window, :code:`tab` a new tab, :code:`os_window`
|
||||
a new OS window and :code:`background` run in the background.
|
||||
|
||||
|
||||
--url-prefixes
|
||||
default={0}
|
||||
default={url_prefixes}
|
||||
Comma separated list of recognized URL prefixes.
|
||||
|
||||
|
||||
@ -470,7 +498,10 @@ on selected matches. See https://sw.kovidgoyal.net/kitty/kittens/hints.html
|
||||
for details. You can also specify absolute paths to load the script from elsewhere.
|
||||
|
||||
|
||||
'''.format(','.join(sorted(URL_PREFIXES))).format
|
||||
'''.format(
|
||||
default_regex=DEFAULT_REGEX, url_prefixes=','.join(sorted(URL_PREFIXES)),
|
||||
line='{{line}}', path='{{path}}'
|
||||
).format
|
||||
help_text = 'Select text from the screen using the keyboard. Defaults to searching for URLs.'
|
||||
usage = ''
|
||||
|
||||
@ -496,12 +527,39 @@ def main(args):
|
||||
print(e.args[0], file=sys.stderr)
|
||||
input(_('Press Enter to quit'))
|
||||
return
|
||||
if items and not args.customize_processing:
|
||||
if items and not (args.customize_processing or args.type == 'linenum'):
|
||||
print('Extra command line arguments present: {}'.format(' '.join(items)), file=sys.stderr)
|
||||
input(_('Press Enter to quit'))
|
||||
return run(args, text, items)
|
||||
|
||||
|
||||
def linenum_handle_result(args, data, target_window_id, boss, extra_cli_args, *a):
|
||||
for m, g in zip(data['match'], data['groupdicts']):
|
||||
if m:
|
||||
path, line = g['path'], g['line']
|
||||
path = path.split(':')[-1]
|
||||
line = int(line)
|
||||
break
|
||||
else:
|
||||
return
|
||||
|
||||
cmd = [x.format(path=path, line=line) for x in extra_cli_args or ('vim', '+{line}', '{path}')]
|
||||
w = boss.window_id_map.get(target_window_id)
|
||||
action = data['linenum_action']
|
||||
|
||||
if action == 'self':
|
||||
if w is not None:
|
||||
import shlex
|
||||
w.paste_bytes(shlex.join(cmd) + '\r')
|
||||
elif action == 'background':
|
||||
import subprocess
|
||||
subprocess.Popen(cmd)
|
||||
else:
|
||||
getattr(boss, {
|
||||
'window': 'new_window_with_cwd', 'tab': 'new_tab_with_cwd', 'os_window': 'new_os_window_with_cwd'
|
||||
}[action])(*cmd)
|
||||
|
||||
|
||||
def handle_result(args, data, target_window_id, boss):
|
||||
if data['customize_processing']:
|
||||
m = load_custom_processor(data['customize_processing'])
|
||||
|
||||
@ -1274,6 +1274,11 @@ k('insert_selected_hash', 'kitty_mod+p>h', 'kitten hints --type hash --program -
|
||||
Select something that looks like a hash and insert it into the terminal.
|
||||
Useful with git, which uses sha1 hashes to identify commits'''))
|
||||
|
||||
k('goto_file_line', 'kitty_mod+p>n', 'kitten hints --type linenum', _('Open the selected file at the selected line'), long_text=_('''
|
||||
Select something that looks like :code:`filename:linenum` and open it in vim at
|
||||
the specified line number.'''))
|
||||
|
||||
|
||||
# }}}
|
||||
|
||||
g('shortcuts.misc') # {{{
|
||||
|
||||
@ -2243,12 +2243,21 @@ send_escape_code_to_child(Screen *self, PyObject *args) {
|
||||
|
||||
static PyObject*
|
||||
paste(Screen *self, PyObject *bytes) {
|
||||
if (!PyBytes_Check(bytes)) { PyErr_SetString(PyExc_TypeError, "Must paste() bytes"); return NULL; }
|
||||
if (self->modes.mBRACKETED_PASTE) write_escape_code_to_child(self, CSI, BRACKETED_PASTE_START);
|
||||
write_to_child(self, PyBytes_AS_STRING(bytes), PyBytes_GET_SIZE(bytes));
|
||||
if (self->modes.mBRACKETED_PASTE) write_escape_code_to_child(self, CSI, BRACKETED_PASTE_END);
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
static PyObject*
|
||||
paste_bytes(Screen *self, PyObject *bytes) {
|
||||
if (!PyBytes_Check(bytes)) { PyErr_SetString(PyExc_TypeError, "Must paste() bytes"); return NULL; }
|
||||
write_to_child(self, PyBytes_AS_STRING(bytes), PyBytes_GET_SIZE(bytes));
|
||||
Py_RETURN_NONE;
|
||||
}
|
||||
|
||||
|
||||
WRAP2(cursor_position, 1, 1)
|
||||
|
||||
#define COUNT_WRAP(name) WRAP1(name, 1)
|
||||
@ -2329,6 +2338,7 @@ static PyMethodDef methods[] = {
|
||||
MND(toggle_alt_screen, METH_NOARGS)
|
||||
MND(reset_callbacks, METH_NOARGS)
|
||||
MND(paste, METH_O)
|
||||
MND(paste_bytes, METH_O)
|
||||
MND(copy_colors_from, METH_O)
|
||||
{"select_graphic_rendition", (PyCFunction)_select_graphic_rendition, METH_VARARGS, ""},
|
||||
|
||||
|
||||
@ -535,6 +535,12 @@ class Window:
|
||||
cmd = [x.replace('INPUT_LINE_NUMBER', str(data['input_line_number'])) for x in self.opts.scrollback_pager]
|
||||
get_boss().display_scrollback(self, data['text'], cmd)
|
||||
|
||||
def paste_bytes(self, text):
|
||||
# paste raw bytes without any processing
|
||||
if isinstance(text, str):
|
||||
text = text.encode('utf-8')
|
||||
self.screen.paste_bytes(text)
|
||||
|
||||
def paste(self, text):
|
||||
if text and not self.destroyed:
|
||||
if isinstance(text, str):
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user