Add a shortcut to easily browse the output of the last command run in the shell
This commit is contained in:
parent
51fa25e03d
commit
07b971ad5f
@ -11,9 +11,9 @@ windows are:
|
|||||||
Scrolling
|
Scrolling
|
||||||
~~~~~~~~~~~~~~
|
~~~~~~~~~~~~~~
|
||||||
|
|
||||||
======================== =======================
|
========================= =======================
|
||||||
Action Shortcut
|
Action Shortcut
|
||||||
======================== =======================
|
========================= =======================
|
||||||
Line up :sc:`scroll_line_up` (also :kbd:`⌥+⌘+⇞` and :kbd:`⌘+↑` on macOS)
|
Line up :sc:`scroll_line_up` (also :kbd:`⌥+⌘+⇞` and :kbd:`⌘+↑` on macOS)
|
||||||
Line down :sc:`scroll_line_down` (also :kbd:`⌥+⌘+⇟` and :kbd:`⌘+↓` on macOS)
|
Line down :sc:`scroll_line_down` (also :kbd:`⌥+⌘+⇟` and :kbd:`⌘+↓` on macOS)
|
||||||
Page up :sc:`scroll_page_up` (also :kbd:`⌘+⇞` on macOS)
|
Page up :sc:`scroll_page_up` (also :kbd:`⌘+⇞` on macOS)
|
||||||
@ -22,7 +22,9 @@ Top :sc:`scroll_home` (also :kbd:`⌘+↖` on macOS)
|
|||||||
Bottom :sc:`scroll_end` (also :kbd:`⌘+↘` on macOS)
|
Bottom :sc:`scroll_end` (also :kbd:`⌘+↘` on macOS)
|
||||||
Previous shell prompt :sc:`scroll_to_previous_prompt` (see :ref:`shell_integration`)
|
Previous shell prompt :sc:`scroll_to_previous_prompt` (see :ref:`shell_integration`)
|
||||||
Next shell prompt :sc:`scroll_to_next_prompt` (see :ref:`shell_integration`)
|
Next shell prompt :sc:`scroll_to_next_prompt` (see :ref:`shell_integration`)
|
||||||
======================== =======================
|
Browse scrollback in less :sc:`show_scrollback`
|
||||||
|
Browse last cmd output :sc:`show_last_command_output` (see :ref:`shell_integration`)
|
||||||
|
========================= =======================
|
||||||
|
|
||||||
Tabs
|
Tabs
|
||||||
~~~~~~~~~~~
|
~~~~~~~~~~~
|
||||||
|
|||||||
@ -92,7 +92,10 @@ This will send the plain text of the active window to the kitten's
|
|||||||
instead. If you want line wrap markers as well, use ``screen-ansi``
|
instead. If you want line wrap markers as well, use ``screen-ansi``
|
||||||
or just ``screen``. For the scrollback buffer as well, use
|
or just ``screen``. For the scrollback buffer as well, use
|
||||||
``history``, ``ansi-history`` or ``screen-history``. To get
|
``history``, ``ansi-history`` or ``screen-history``. To get
|
||||||
the currently selected text, use ``selection``.
|
the currently selected text, use ``selection``. To get the output
|
||||||
|
of the last command run in the shell, use ``output`` or ``output-ansi``
|
||||||
|
or ``output-screen-ansi``. Note that using ``output`` requires
|
||||||
|
:ref:`shell_integration`.
|
||||||
|
|
||||||
|
|
||||||
Using kittens to script kitty, without any terminal UI
|
Using kittens to script kitty, without any terminal UI
|
||||||
|
|||||||
@ -107,6 +107,10 @@ def data_for_at(w: Optional[Window], arg: str, add_wrap_markers: bool = False) -
|
|||||||
return as_text(as_ansi=True, alternate_screen=True)
|
return as_text(as_ansi=True, alternate_screen=True)
|
||||||
if arg == '@ansi_alternate_scrollback':
|
if arg == '@ansi_alternate_scrollback':
|
||||||
return as_text(as_ansi=True, alternate_screen=True, add_history=True)
|
return as_text(as_ansi=True, alternate_screen=True, add_history=True)
|
||||||
|
if arg == '@last_cmd_output':
|
||||||
|
return w.last_cmd_output(add_wrap_markers=add_wrap_markers)
|
||||||
|
if arg == '@ansi_last_cmd_output':
|
||||||
|
return w.last_cmd_output(as_ansi=True, add_wrap_markers=add_wrap_markers)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
@ -1072,6 +1076,9 @@ class Boss:
|
|||||||
data = sel.encode('utf-8') if sel else None
|
data = sel.encode('utf-8') if sel else None
|
||||||
elif type_of_input is None:
|
elif type_of_input is None:
|
||||||
data = None
|
data = None
|
||||||
|
elif type_of_input in ('output', 'output-screen', 'output-screen-ansi', 'output-ansi'):
|
||||||
|
q = type_of_input.split('-')
|
||||||
|
data = w.last_cmd_output(as_ansi='ansi' in q, add_wrap_markers='screen' in q).encode('utf-8')
|
||||||
else:
|
else:
|
||||||
raise ValueError('Unknown type_of_input: {}'.format(type_of_input))
|
raise ValueError('Unknown type_of_input: {}'.format(type_of_input))
|
||||||
else:
|
else:
|
||||||
|
|||||||
@ -1042,6 +1042,7 @@ class Screen:
|
|||||||
pass
|
pass
|
||||||
as_text_non_visual = as_text
|
as_text_non_visual = as_text
|
||||||
as_text_alternate = as_text
|
as_text_alternate = as_text
|
||||||
|
last_cmd_output = as_text
|
||||||
|
|
||||||
def scroll_until_cursor(self) -> None:
|
def scroll_until_cursor(self) -> None:
|
||||||
pass
|
pass
|
||||||
|
|||||||
@ -122,14 +122,15 @@ computers (for example, over ssh) or as other users.
|
|||||||
--stdin-source
|
--stdin-source
|
||||||
type=choices
|
type=choices
|
||||||
default=none
|
default=none
|
||||||
choices=none,@selection,@screen,@screen_scrollback,@alternate,@alternate_scrollback
|
choices=none,@selection,@screen,@screen_scrollback,@alternate,@alternate_scrollback,@last_cmd_output
|
||||||
Pass the screen contents as :code:`STDIN` to the child process. :code:`@selection` is
|
Pass the screen contents as :code:`STDIN` to the child process. :code:`@selection` is
|
||||||
the currently selected text. :code:`@screen` is the contents of the currently active
|
the currently selected text. :code:`@screen` is the contents of the currently active
|
||||||
window. :code:`@screen_scrollback` is the same as :code:`@screen`, but includes the
|
window. :code:`@screen_scrollback` is the same as :code:`@screen`, but includes the
|
||||||
scrollback buffer as well. :code:`@alternate` is the secondary screen of the current
|
scrollback buffer as well. :code:`@alternate` is the secondary screen of the current
|
||||||
active window. For example if you run a full screen terminal application, the
|
active window. For example if you run a full screen terminal application, the
|
||||||
secondary screen will be the screen you return to when quitting the
|
secondary screen will be the screen you return to when quitting the
|
||||||
application.
|
application. :code:`@last_cmd_output` is the output from the last command run in the shell,
|
||||||
|
this needs :ref:`shell_integration` to work.
|
||||||
|
|
||||||
|
|
||||||
--stdin-add-formatting
|
--stdin-add-formatting
|
||||||
|
|||||||
@ -2919,6 +2919,16 @@ For more details on piping screen and buffer contents to external programs,
|
|||||||
see :doc:`launch`.
|
see :doc:`launch`.
|
||||||
'''
|
'''
|
||||||
)
|
)
|
||||||
|
|
||||||
|
map('Browse output of the last shell command in less',
|
||||||
|
'show_last_command_output kitty_mod+g show_last_command_output',
|
||||||
|
long_text='''
|
||||||
|
Requires :ref:`shell_integration` to work. You can pipe the output
|
||||||
|
of the last command run in the shell using the :doc:`launch` function.
|
||||||
|
For example, the following opens the output in less in an overlay window::
|
||||||
|
|
||||||
|
map f1 launch --stdin-source=@last_cmd_output --stdin-add-formatting --type=overlay less +G -R
|
||||||
|
''')
|
||||||
egr() # }}}
|
egr() # }}}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
2
kitty/options/types.py
generated
2
kitty/options/types.py
generated
@ -718,6 +718,8 @@ defaults.map = [
|
|||||||
KeyDefinition(False, KeyAction('scroll_to_prompt', (1,)), 1024, False, 100, ()),
|
KeyDefinition(False, KeyAction('scroll_to_prompt', (1,)), 1024, False, 100, ()),
|
||||||
# show_scrollback
|
# show_scrollback
|
||||||
KeyDefinition(False, KeyAction('show_scrollback'), 1024, False, 104, ()),
|
KeyDefinition(False, KeyAction('show_scrollback'), 1024, False, 104, ()),
|
||||||
|
# show_last_command_output
|
||||||
|
KeyDefinition(False, KeyAction('show_last_command_output'), 1024, False, 103, ()),
|
||||||
# new_window
|
# new_window
|
||||||
KeyDefinition(False, KeyAction('new_window'), 1024, False, 57345, ()),
|
KeyDefinition(False, KeyAction('new_window'), 1024, False, 57345, ()),
|
||||||
# new_os_window
|
# new_os_window
|
||||||
|
|||||||
@ -2342,6 +2342,41 @@ as_text_alternate(Screen *self, PyObject *args) {
|
|||||||
return ans;
|
return ans;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef struct OutputOffset {
|
||||||
|
Screen *screen;
|
||||||
|
int start;
|
||||||
|
} OutputOffset;
|
||||||
|
|
||||||
|
static Line*
|
||||||
|
get_line_from_offset(void *x, int y) {
|
||||||
|
OutputOffset *r = x;
|
||||||
|
return range_line_(r->screen, r->start + y);
|
||||||
|
}
|
||||||
|
|
||||||
|
static PyObject*
|
||||||
|
last_cmd_output(Screen *self, PyObject *args) {
|
||||||
|
if (self->linebuf != self->main_linebuf) return PyUnicode_FromString("");
|
||||||
|
|
||||||
|
OutputOffset oo = {.screen=self};
|
||||||
|
unsigned num_lines = 0;
|
||||||
|
int prompt_pos = self->cursor->y, y = self->cursor->y;
|
||||||
|
const int limit = -self->historybuf->count;
|
||||||
|
while (y >= limit) {
|
||||||
|
Line *line = range_line_(self, y);
|
||||||
|
if (line->is_prompt_start) prompt_pos = y;
|
||||||
|
if (line->is_output_start) {
|
||||||
|
oo.start = y;
|
||||||
|
num_lines = prompt_pos - y;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
y--;
|
||||||
|
}
|
||||||
|
if (y < limit) {
|
||||||
|
oo.start = limit;
|
||||||
|
num_lines = prompt_pos - limit;
|
||||||
|
}
|
||||||
|
return as_text_generic(args, &oo, get_line_from_offset, num_lines, &self->as_ansi_buf);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
static PyObject*
|
static PyObject*
|
||||||
@ -3246,6 +3281,7 @@ static PyMethodDef methods[] = {
|
|||||||
MND(as_text, METH_VARARGS)
|
MND(as_text, METH_VARARGS)
|
||||||
MND(as_text_non_visual, METH_VARARGS)
|
MND(as_text_non_visual, METH_VARARGS)
|
||||||
MND(as_text_alternate, METH_VARARGS)
|
MND(as_text_alternate, METH_VARARGS)
|
||||||
|
MND(last_cmd_output, METH_VARARGS)
|
||||||
MND(tab, METH_NOARGS)
|
MND(tab, METH_NOARGS)
|
||||||
MND(backspace, METH_NOARGS)
|
MND(backspace, METH_NOARGS)
|
||||||
MND(linefeed, METH_NOARGS)
|
MND(linefeed, METH_NOARGS)
|
||||||
|
|||||||
@ -914,6 +914,11 @@ class Window:
|
|||||||
) -> str:
|
) -> str:
|
||||||
return as_text(self.screen, as_ansi, add_history, add_wrap_markers, alternate_screen, add_cursor)
|
return as_text(self.screen, as_ansi, add_history, add_wrap_markers, alternate_screen, add_cursor)
|
||||||
|
|
||||||
|
def last_cmd_output(self, as_ansi: bool = False, add_wrap_markers: bool = False) -> str:
|
||||||
|
lines: List[str] = []
|
||||||
|
self.screen.last_cmd_output(lines.append, as_ansi, add_wrap_markers)
|
||||||
|
return ''.join(lines)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def cwd_of_child(self) -> Optional[str]:
|
def cwd_of_child(self) -> Optional[str]:
|
||||||
return self.child.foreground_cwd or self.child.current_cwd
|
return self.child.foreground_cwd or self.child.current_cwd
|
||||||
@ -942,6 +947,15 @@ class Window:
|
|||||||
data = self.pipe_data(text, has_wrap_markers=True)
|
data = self.pipe_data(text, has_wrap_markers=True)
|
||||||
get_boss().display_scrollback(self, data['text'], data['input_line_number'])
|
get_boss().display_scrollback(self, data['text'], data['input_line_number'])
|
||||||
|
|
||||||
|
def show_last_command_output(self) -> None:
|
||||||
|
'''
|
||||||
|
@ac:cp: Show output from the last shell command in a pager like less
|
||||||
|
|
||||||
|
Requires :ref:`shell_integration` to work
|
||||||
|
'''
|
||||||
|
text = self.last_cmd_output(as_ansi=True, add_wrap_markers=True)
|
||||||
|
get_boss().display_scrollback(self, text, title='Last command output')
|
||||||
|
|
||||||
def paste_bytes(self, text: Union[str, bytes]) -> None:
|
def paste_bytes(self, text: Union[str, bytes]) -> None:
|
||||||
# paste raw bytes without any processing
|
# paste raw bytes without any processing
|
||||||
if isinstance(text, str):
|
if isinstance(text, str):
|
||||||
|
|||||||
@ -919,6 +919,9 @@ class TestScreen(BaseTest):
|
|||||||
def mark_prompt():
|
def mark_prompt():
|
||||||
parse_bytes(s, '\033]133;A\007'.encode('ascii'))
|
parse_bytes(s, '\033]133;A\007'.encode('ascii'))
|
||||||
|
|
||||||
|
def mark_output():
|
||||||
|
parse_bytes(s, '\033]133;C\007'.encode('ascii'))
|
||||||
|
|
||||||
for i in range(4):
|
for i in range(4):
|
||||||
mark_prompt()
|
mark_prompt()
|
||||||
s.draw(f'$ {i}')
|
s.draw(f'$ {i}')
|
||||||
@ -945,3 +948,21 @@ class TestScreen(BaseTest):
|
|||||||
s.draw(str(i))
|
s.draw(str(i))
|
||||||
self.assertTrue(s.scroll_to_prompt())
|
self.assertTrue(s.scroll_to_prompt())
|
||||||
self.ae(str(s.visual_line(0)), '$ 0')
|
self.ae(str(s.visual_line(0)), '$ 0')
|
||||||
|
|
||||||
|
def lco():
|
||||||
|
a = []
|
||||||
|
s.last_cmd_output(a.append)
|
||||||
|
return ''.join(a)
|
||||||
|
|
||||||
|
s = self.create_screen()
|
||||||
|
s.draw('abcd'), s.index(), s.carriage_return()
|
||||||
|
s.draw('12'), s.index(), s.carriage_return()
|
||||||
|
self.ae(lco(), 'abcd\n12')
|
||||||
|
s = self.create_screen()
|
||||||
|
mark_prompt(), s.draw('$ 0')
|
||||||
|
s.carriage_return(), s.index()
|
||||||
|
mark_output()
|
||||||
|
s.draw('abcd'), s.index(), s.carriage_return()
|
||||||
|
s.draw('12'), s.index(), s.carriage_return()
|
||||||
|
mark_prompt(), s.draw('$ 1')
|
||||||
|
self.ae(lco(), 'abcd\n12')
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user