Allows to open the command output in pager by mouse click

This commit is contained in:
pagedown 2021-11-17 22:25:49 +08:00
parent 17a48f6b9f
commit b91809eaa4
No known key found for this signature in database
GPG Key ID: E921CF18AC8FF6EB
10 changed files with 98 additions and 38 deletions

View File

@ -80,16 +80,27 @@ Now, when you right click on the output, the entire output is selected, ready
to be copied. to be copied.
The feature to jump to previous prompts ( The feature to jump to previous prompts (
:sc:`scroll_to_previous_prompt` and :sc:`scroll_to_next_prompt`) can be :sc:`scroll_to_previous_prompt` and :sc:`scroll_to_next_prompt`) and mouse
integrated with browsing command output as well. For example, define the actions (``mouse_select_command_output`` and ``mouse_show_command_output``) can
be integrated with browsing command output as well. For example, define the
following mapping in :file:`kitty.conf`: following mapping in :file:`kitty.conf`:
.. code:: conf .. code:: conf
map f1 show_last_visited_command_output map f1 show_last_visited_command_output
Now, pressing :kbd:`F1` will cause the output of the last jumped to command to Now, pressing :kbd:`F1` will cause the output of the last jumped to command or
be opened in a pager for easy browsing. the last mouse clicked command output to be opened in a pager for easy browsing.
In addition, You can define shortcut to get the first command output on screen.
For example, define the following in :file:`kitty.conf`:
.. code:: conf
map f1 show_first_command_output_on_screen
Now, pressing :kbd:`F1` will cause the output of the first command output on
screen to be opened in a pager.
You can also add shortcut to scroll to the last jumped position. For example, You can also add shortcut to scroll to the last jumped position. For example,
define the following in :file:`kitty.conf`: define the following in :file:`kitty.conf`:

View File

@ -1272,7 +1272,7 @@ def set_window_padding(os_window_id: int, tab_id: int, window_id: int, left: int
def click_mouse_url(os_window_id: int, tab_id: int, window_id: int) -> bool: def click_mouse_url(os_window_id: int, tab_id: int, window_id: int) -> bool:
pass pass
def click_mouse_cmd_output(os_window_id: int, tab_id: int, window_id: int) -> bool: def click_mouse_cmd_output(os_window_id: int, tab_id: int, window_id: int, select_cmd_output: bool) -> bool:
pass pass
def move_cursor_to_mouse_if_in_prompt(os_window_id: int, tab_id: int, window_id: int) -> bool: def move_cursor_to_mouse_if_in_prompt(os_window_id: int, tab_id: int, window_id: int) -> bool:

View File

@ -424,6 +424,12 @@ mouse_open_url(Window *w) {
return screen_open_url(screen); return screen_open_url(screen);
} }
bool
mouse_set_last_visited_cmd_output(Window *w) {
Screen *screen = w->render_data.screen;
return screen_set_last_visited_prompt(screen, w->mouse_pos.cell_y);
}
bool bool
mouse_select_cmd_output(Window *w) { mouse_select_cmd_output(Window *w) {
Screen *screen = w->render_data.screen; Screen *screen = w->render_data.screen;

View File

@ -616,6 +616,13 @@ mma('Select line from point even when grabbed',
mma('Extend the current selection even when grabbed', mma('Extend the current selection even when grabbed',
'extend_selection_grabbed shift+right press ungrabbed,grabbed mouse_selection extend', 'extend_selection_grabbed shift+right press ungrabbed,grabbed mouse_selection extend',
) )
mma('Show clicked command output in pager',
'show_clicked_cmd_output_ungrabbed ctrl+shift+right press ungrabbed mouse_show_command_output',
long_text='''
Requires :ref:`shell_integration` to work.
'''
)
egr() # }}} egr() # }}}
egr() # }}} egr() # }}}
@ -3006,11 +3013,14 @@ Requires :ref:`shell_integration` to work.
map('Scroll to next shell prompt', map('Scroll to next shell prompt',
'scroll_to_next_prompt kitty_mod+x scroll_to_prompt 1', 'scroll_to_next_prompt kitty_mod+x scroll_to_prompt 1',
long_text=''' long_text='''
When the parameter is zero, scroll to the last jumped position, or the last clicked position by
command output related mouse actions.
Requires :ref:`shell_integration` to work. Requires :ref:`shell_integration` to work.
''' '''
) )
map('Browse scrollback buffer in less', map('Browse scrollback buffer in pager',
'show_scrollback kitty_mod+h show_scrollback', 'show_scrollback kitty_mod+h show_scrollback',
long_text=''' long_text='''
You can pipe the contents of the current screen + history buffer as You can pipe the contents of the current screen + history buffer as
@ -3024,17 +3034,27 @@ see :doc:`launch`.
''' '''
) )
map('Browse output of the last shell command in less', map('Browse output of the last shell command in pager',
'show_last_command_output kitty_mod+g show_last_command_output', 'show_last_command_output kitty_mod+g show_last_command_output',
long_text=''' long_text='''
Requires :ref:`shell_integration` to work. You can pipe the output You can also define additional shortcuts to get the command output.
of the last command run in the shell using the :doc:`launch` function. For example, to get the first command output on screen::
map f1 show_first_command_output_on_screen
To get the command output that was last accessed by a keyboard action or mouse action::
map f1 show_last_visited_command_output
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:: 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 map f1 launch --stdin-source=@last_cmd_output --stdin-add-formatting --type=overlay less +G -R
To get the output of the first command on the screen, use :code:`@first_cmd_output_on_screen`. To get the output of the first command on the screen, use :code:`@first_cmd_output_on_screen`.
To get the output of the last jumped to command, use :code:`@last_visited_cmd_output`. To get the output of the last jumped to command, use :code:`@last_visited_cmd_output`.
Requires :ref:`shell_integration` to work.
''') ''')
egr() # }}} egr() # }}}

View File

@ -951,4 +951,6 @@ defaults.mouse_map = [
MouseMapping(1, 1, 1, True, KeyAction('mouse_selection', (1,))), MouseMapping(1, 1, 1, True, KeyAction('mouse_selection', (1,))),
# extend_selection_grabbed # extend_selection_grabbed
MouseMapping(1, 1, 1, False, KeyAction('mouse_selection', (1,))), MouseMapping(1, 1, 1, False, KeyAction('mouse_selection', (1,))),
# show_clicked_cmd_output_ungrabbed
MouseMapping(1, 5, 1, False, KeyAction('mouse_show_command_output')),
] ]

View File

@ -1269,7 +1269,7 @@ screen_cursor_to_column(Screen *self, unsigned int column) {
linebuf_init_line(self->linebuf, bottom); \ linebuf_init_line(self->linebuf, bottom); \
historybuf_add_line(self->historybuf, self->linebuf->line, &self->as_ansi_buf); \ historybuf_add_line(self->historybuf, self->linebuf->line, &self->as_ansi_buf); \
self->history_line_added_count++; \ self->history_line_added_count++; \
if (self->last_visited_prompt.is_set && self->last_visited_prompt.scrolled_by < self->historybuf->count) self->last_visited_prompt.scrolled_by++; \ if (self->last_visited_prompt.is_set && self->last_visited_prompt.scrolled_by <= self->historybuf->count) self->last_visited_prompt.scrolled_by++; \
} \ } \
linebuf_clear_line(self->linebuf, bottom, true); \ linebuf_clear_line(self->linebuf, bottom, true); \
self->is_dirty = true; \ self->is_dirty = true; \
@ -2024,7 +2024,7 @@ screen_history_scroll_to_prompt(Screen *self, int num_of_prompts_to_jump) {
if (self->linebuf != self->main_linebuf) return false; if (self->linebuf != self->main_linebuf) return false;
unsigned int old = self->scrolled_by; unsigned int old = self->scrolled_by;
if (num_of_prompts_to_jump == 0) { if (num_of_prompts_to_jump == 0) {
if (!self->last_visited_prompt.is_set || self->last_visited_prompt.scrolled_by > self->historybuf->count) return false; if (!self->last_visited_prompt.is_set || self->last_visited_prompt.scrolled_by > self->historybuf->count || self->last_visited_prompt.y >= self->lines) return false;
self->scrolled_by = self->last_visited_prompt.scrolled_by; self->scrolled_by = self->last_visited_prompt.scrolled_by;
} else { } else {
int delta = num_of_prompts_to_jump < 0 ? -1 : 1; int delta = num_of_prompts_to_jump < 0 ? -1 : 1;
@ -2041,8 +2041,7 @@ screen_history_scroll_to_prompt(Screen *self, int num_of_prompts_to_jump) {
} }
#undef ensure_y_ok #undef ensure_y_ok
self->scrolled_by = y >= 0 ? 0 : -y; self->scrolled_by = y >= 0 ? 0 : -y;
self->last_visited_prompt.scrolled_by = self->scrolled_by; screen_set_last_visited_prompt(self, 0);
self->last_visited_prompt.is_set = true;
} }
if (old != self->scrolled_by) self->scroll_changed = true; if (old != self->scrolled_by) self->scroll_changed = true;
return old != self->scrolled_by; return old != self->scrolled_by;
@ -2756,7 +2755,7 @@ cmd_output(Screen *self, PyObject *args) {
break; break;
case 2: // last visited cmd case 2: // last visited cmd
if (self->last_visited_prompt.scrolled_by <= self->historybuf->count && self->last_visited_prompt.is_set) { if (self->last_visited_prompt.scrolled_by <= self->historybuf->count && self->last_visited_prompt.is_set) {
found = find_cmd_output(self, &oo, 0, self->last_visited_prompt.scrolled_by, 0, false); found = find_cmd_output(self, &oo, self->last_visited_prompt.y, self->last_visited_prompt.scrolled_by, 0, false);
} break; } break;
default: default:
PyErr_Format(PyExc_KeyError, "%u is not a valid type of command", which); PyErr_Format(PyExc_KeyError, "%u is not a valid type of command", which);
@ -2766,6 +2765,15 @@ cmd_output(Screen *self, PyObject *args) {
Py_RETURN_NONE; Py_RETURN_NONE;
} }
bool
screen_set_last_visited_prompt(Screen *self, index_type y) {
if (y >= self->lines) return false;
self->last_visited_prompt.scrolled_by = self->scrolled_by;
self->last_visited_prompt.y = y;
self->last_visited_prompt.is_set = true;
return true;
}
bool bool
screen_select_cmd_output(Screen *self, index_type y) { screen_select_cmd_output(Screen *self, index_type y) {
if (y >= self->lines) return false; if (y >= self->lines) return false;

View File

@ -146,6 +146,7 @@ typedef struct {
} last_rendered_window_char; } last_rendered_window_char;
struct { struct {
unsigned int scrolled_by; unsigned int scrolled_by;
index_type y;
bool is_set; bool is_set;
} last_visited_prompt; } last_visited_prompt;
} Screen; } Screen;
@ -246,6 +247,7 @@ void set_active_hyperlink(Screen*, char*, char*);
hyperlink_id_type screen_mark_hyperlink(Screen*, index_type, index_type); hyperlink_id_type screen_mark_hyperlink(Screen*, index_type, index_type);
void screen_handle_graphics_command(Screen *self, const GraphicsCommand *cmd, const uint8_t *payload); void screen_handle_graphics_command(Screen *self, const GraphicsCommand *cmd, const uint8_t *payload);
bool screen_open_url(Screen*); bool screen_open_url(Screen*);
bool screen_set_last_visited_prompt(Screen*, index_type);
bool screen_select_cmd_output(Screen*, index_type); bool screen_select_cmd_output(Screen*, index_type);
void screen_dirty_sprite_positions(Screen *self); void screen_dirty_sprite_positions(Screen *self);
void screen_rescale_images(Screen *self); void screen_rescale_images(Screen *self);

View File

@ -1091,12 +1091,13 @@ click_mouse_url(id_type os_window_id, id_type tab_id, id_type window_id) {
} }
static bool static bool
click_mouse_cmd_output(id_type os_window_id, id_type tab_id, id_type window_id) { click_mouse_cmd_output(id_type os_window_id, id_type tab_id, id_type window_id, int select_cmd_output) {
bool selected = false; bool handled = false;
WITH_WINDOW(os_window_id, tab_id, window_id); WITH_WINDOW(os_window_id, tab_id, window_id);
selected = mouse_select_cmd_output(window); handled = mouse_set_last_visited_cmd_output(window);
if (select_cmd_output && handled) handled = mouse_select_cmd_output(window);
END_WITH_WINDOW; END_WITH_WINDOW;
return selected; return handled;
} }
static bool static bool
@ -1127,8 +1128,8 @@ PYWRAP1(click_mouse_url) {
} }
PYWRAP1(click_mouse_cmd_output) { PYWRAP1(click_mouse_cmd_output) {
id_type a, b, c; PA("KKK", &a, &b, &c); id_type a, b, c; int d; PA("KKKp", &a, &b, &c, &d);
if (click_mouse_cmd_output(a, b, c)) { Py_RETURN_TRUE; } if (click_mouse_cmd_output(a, b, c, d)) { Py_RETURN_TRUE; }
Py_RETURN_FALSE; Py_RETURN_FALSE;
} }

View File

@ -311,6 +311,7 @@ void update_os_window_title(OSWindow *os_window);
void fake_scroll(Window *w, int amount, bool upwards); void fake_scroll(Window *w, int amount, bool upwards);
Window* window_for_window_id(id_type kitty_window_id); Window* window_for_window_id(id_type kitty_window_id);
bool mouse_open_url(Window *w); bool mouse_open_url(Window *w);
bool mouse_set_last_visited_cmd_output(Window *w);
bool mouse_select_cmd_output(Window *w); bool mouse_select_cmd_output(Window *w);
bool move_cursor_to_mouse_if_at_shell_prompt(Window *w); bool move_cursor_to_mouse_if_at_shell_prompt(Window *w);
void mouse_selection(Window *w, int code, int button); void mouse_selection(Window *w, int code, int button);

View File

@ -1014,8 +1014,17 @@ class Window:
Requires :ref:`shell_integration` to work Requires :ref:`shell_integration` to work
''') ''')
def mouse_select_cmd_output(self) -> None: def mouse_select_command_output(self) -> None:
click_mouse_cmd_output(self.os_window_id, self.tab_id, self.id) click_mouse_cmd_output(self.os_window_id, self.tab_id, self.id, True)
@ac('mouse', '''
Show clicked command output in a pager like less
Requires :ref:`shell_integration` to work
''')
def mouse_show_command_output(self) -> None:
if click_mouse_cmd_output(self.os_window_id, self.tab_id, self.id, False):
self.show_cmd_output(CommandOutput.last_visited, 'Clicked command output')
# }}} # }}}
def text_for_selection(self) -> str: def text_for_selection(self) -> str:
@ -1087,15 +1096,18 @@ class Window:
cursor_on_screen = self.screen.scrolled_by < self.screen.lines - self.screen.cursor.y cursor_on_screen = self.screen.scrolled_by < self.screen.lines - self.screen.cursor.y
get_boss().display_scrollback(self, data['text'], data['input_line_number'], report_cursor=cursor_on_screen) get_boss().display_scrollback(self, data['text'], data['input_line_number'], report_cursor=cursor_on_screen)
def show_cmd_output(self, which: CommandOutput, title: str = 'Command ouptut', as_ansi: bool = True, add_wrap_markers: bool = True) -> None:
text = self.cmd_output(which, as_ansi=as_ansi, add_wrap_markers=add_wrap_markers)
text = text.replace('\r\n', '\n').replace('\r', '\n')
get_boss().display_scrollback(self, text, title=title, report_cursor=False)
@ac('cp', ''' @ac('cp', '''
Show output from the first shell command on screen in a pager like less Show output from the first shell command on screen in a pager like less
Requires :ref:`shell_integration` to work Requires :ref:`shell_integration` to work
''') ''')
def show_first_command_output_on_screen(self) -> None: def show_first_command_output_on_screen(self) -> None:
text = self.cmd_output(CommandOutput.first_on_screen, as_ansi=True, add_wrap_markers=True) self.show_cmd_output(CommandOutput.first_on_screen, 'First command output on screen')
text = text.replace('\r\n', '\n').replace('\r', '\n')
get_boss().display_scrollback(self, text, title='First command output on screen', report_cursor=False)
@ac('cp', ''' @ac('cp', '''
Show output from the last shell command in a pager like less Show output from the last shell command in a pager like less
@ -1103,19 +1115,16 @@ class Window:
Requires :ref:`shell_integration` to work Requires :ref:`shell_integration` to work
''') ''')
def show_last_command_output(self) -> None: def show_last_command_output(self) -> None:
text = self.cmd_output(CommandOutput.last_run, as_ansi=True, add_wrap_markers=True) self.show_cmd_output(CommandOutput.last_run, 'Last command output')
text = text.replace('\r\n', '\n').replace('\r', '\n')
get_boss().display_scrollback(self, text, title='Last command output', report_cursor=False)
@ac('cp', ''' @ac('cp', '''
Show the first output below the last scrolled position via scroll_to_prompt in a pager like less Show the first command output below the last scrolled position via scroll_to_prompt
or the last mouse clicked command output in a pager like less
Requires :ref:`shell_integration` to work Requires :ref:`shell_integration` to work
''') ''')
def show_last_visited_command_output(self) -> None: def show_last_visited_command_output(self) -> None:
text = self.cmd_output(CommandOutput.last_visited, as_ansi=True, add_wrap_markers=True) self.show_cmd_output(CommandOutput.last_visited, 'Last visited command output')
text = text.replace('\r\n', '\n').replace('\r', '\n')
get_boss().display_scrollback(self, text, title='Last visited command output', report_cursor=False)
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