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.
The feature to jump to previous prompts (
:sc:`scroll_to_previous_prompt` and :sc:`scroll_to_next_prompt`) can be
integrated with browsing command output as well. For example, define the
:sc:`scroll_to_previous_prompt` and :sc:`scroll_to_next_prompt`) and mouse
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`:
.. code:: conf
map f1 show_last_visited_command_output
Now, pressing :kbd:`F1` will cause the output of the last jumped to command to
be opened in a pager for easy browsing.
Now, pressing :kbd:`F1` will cause the output of the last jumped to command or
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,
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:
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
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);
}
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
mouse_select_cmd_output(Window *w) {
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',
'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() # }}}
@ -3006,11 +3013,14 @@ Requires :ref:`shell_integration` to work.
map('Scroll to next shell prompt',
'scroll_to_next_prompt kitty_mod+x scroll_to_prompt 1',
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.
'''
)
map('Browse scrollback buffer in less',
map('Browse scrollback buffer in pager',
'show_scrollback kitty_mod+h show_scrollback',
long_text='''
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',
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.
You can also define additional shortcuts to get the command output.
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::
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 last jumped to command, use :code:`@last_visited_cmd_output`.
Requires :ref:`shell_integration` to work.
''')
egr() # }}}

View File

@ -951,4 +951,6 @@ defaults.mouse_map = [
MouseMapping(1, 1, 1, True, KeyAction('mouse_selection', (1,))),
# extend_selection_grabbed
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); \
historybuf_add_line(self->historybuf, self->linebuf->line, &self->as_ansi_buf); \
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); \
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;
unsigned int old = self->scrolled_by;
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;
} else {
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
self->scrolled_by = y >= 0 ? 0 : -y;
self->last_visited_prompt.scrolled_by = self->scrolled_by;
self->last_visited_prompt.is_set = true;
screen_set_last_visited_prompt(self, 0);
}
if (old != self->scrolled_by) self->scroll_changed = true;
return old != self->scrolled_by;
@ -2756,7 +2755,7 @@ cmd_output(Screen *self, PyObject *args) {
break;
case 2: // last visited cmd
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;
default:
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;
}
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
screen_select_cmd_output(Screen *self, index_type y) {
if (y >= self->lines) return false;

View File

@ -146,6 +146,7 @@ typedef struct {
} last_rendered_window_char;
struct {
unsigned int scrolled_by;
index_type y;
bool is_set;
} last_visited_prompt;
} Screen;
@ -246,6 +247,7 @@ void set_active_hyperlink(Screen*, char*, char*);
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);
bool screen_open_url(Screen*);
bool screen_set_last_visited_prompt(Screen*, index_type);
bool screen_select_cmd_output(Screen*, index_type);
void screen_dirty_sprite_positions(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
click_mouse_cmd_output(id_type os_window_id, id_type tab_id, id_type window_id) {
bool selected = false;
click_mouse_cmd_output(id_type os_window_id, id_type tab_id, id_type window_id, int select_cmd_output) {
bool handled = false;
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;
return selected;
return handled;
}
static bool
@ -1127,8 +1128,8 @@ PYWRAP1(click_mouse_url) {
}
PYWRAP1(click_mouse_cmd_output) {
id_type a, b, c; PA("KKK", &a, &b, &c);
if (click_mouse_cmd_output(a, b, c)) { Py_RETURN_TRUE; }
id_type a, b, c; int d; PA("KKKp", &a, &b, &c, &d);
if (click_mouse_cmd_output(a, b, c, d)) { Py_RETURN_TRUE; }
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);
Window* window_for_window_id(id_type kitty_window_id);
bool mouse_open_url(Window *w);
bool mouse_set_last_visited_cmd_output(Window *w);
bool mouse_select_cmd_output(Window *w);
bool move_cursor_to_mouse_if_at_shell_prompt(Window *w);
void mouse_selection(Window *w, int code, int button);

View File

@ -1014,8 +1014,17 @@ class Window:
Requires :ref:`shell_integration` to work
''')
def mouse_select_cmd_output(self) -> None:
click_mouse_cmd_output(self.os_window_id, self.tab_id, self.id)
def mouse_select_command_output(self) -> None:
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:
@ -1087,15 +1096,18 @@ class Window:
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)
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', '''
Show output from the first shell command on screen in a pager like less
Requires :ref:`shell_integration` to work
''')
def show_first_command_output_on_screen(self) -> None:
text = self.cmd_output(CommandOutput.first_on_screen, as_ansi=True, add_wrap_markers=True)
text = text.replace('\r\n', '\n').replace('\r', '\n')
get_boss().display_scrollback(self, text, title='First command output on screen', report_cursor=False)
self.show_cmd_output(CommandOutput.first_on_screen, 'First command output on screen')
@ac('cp', '''
Show output from the last shell command in a pager like less
@ -1103,19 +1115,16 @@ class Window:
Requires :ref:`shell_integration` to work
''')
def show_last_command_output(self) -> None:
text = self.cmd_output(CommandOutput.last_run, as_ansi=True, add_wrap_markers=True)
text = text.replace('\r\n', '\n').replace('\r', '\n')
get_boss().display_scrollback(self, text, title='Last command output', report_cursor=False)
self.show_cmd_output(CommandOutput.last_run, 'Last command output')
@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
''')
def show_last_visited_command_output(self) -> None:
text = self.cmd_output(CommandOutput.last_visited, as_ansi=True, add_wrap_markers=True)
text = text.replace('\r\n', '\n').replace('\r', '\n')
get_boss().display_scrollback(self, text, title='Last visited command output', report_cursor=False)
self.show_cmd_output(CommandOutput.last_visited, 'Last visited command output')
def paste_bytes(self, text: Union[str, bytes]) -> None:
# paste raw bytes without any processing