Skip to content

Commit 8fd5f18

Browse files
committed
Merge branch 'feat-cmd-output-mouse-pager' of https://github.com/page-down/kitty
2 parents ca214ff + fe075ea commit 8fd5f18

File tree

10 files changed

+101
-38
lines changed

10 files changed

+101
-38
lines changed

docs/shell-integration.rst

Lines changed: 21 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -74,29 +74,40 @@ on the output, define the following in :file:`kitty.conf`:
7474

7575
.. code:: conf
7676
77-
mouse_map right press ungrabbed mouse_select_command_output
77+
mouse_map right press ungrabbed mouse_select_command_output
7878
7979
Now, when you right click on the output, the entire output is selected, ready
8080
to be copied.
8181

8282
The feature to jump to previous prompts (
83-
:sc:`scroll_to_previous_prompt` and :sc:`scroll_to_next_prompt`) can be
84-
integrated with browsing command output as well. For example, define the
83+
:sc:`scroll_to_previous_prompt` and :sc:`scroll_to_next_prompt`) and mouse
84+
actions (``mouse_select_command_output`` and ``mouse_show_command_output``) can
85+
be integrated with browsing command output as well. For example, define the
8586
following mapping in :file:`kitty.conf`:
8687

8788
.. code:: conf
8889
89-
map f1 show_last_visited_command_output
90+
map f1 show_last_visited_command_output
9091
91-
Now, pressing :kbd:`F1` will cause the output of the last jumped to command to
92-
be opened in a pager for easy browsing.
92+
Now, pressing :kbd:`F1` will cause the output of the last jumped to command or
93+
the last mouse clicked command output to be opened in a pager for easy browsing.
94+
95+
In addition, You can define shortcut to get the first command output on screen.
96+
For example, define the following in :file:`kitty.conf`:
97+
98+
.. code:: conf
99+
100+
map f1 show_first_command_output_on_screen
101+
102+
Now, pressing :kbd:`F1` will cause the output of the first command output on
103+
screen to be opened in a pager.
93104

94105
You can also add shortcut to scroll to the last jumped position. For example,
95106
define the following in :file:`kitty.conf`:
96107

97108
.. code:: conf
98109
99-
map f1 scroll_to_prompt 0
110+
map f1 scroll_to_prompt 0
100111
101112
102113
How it works
@@ -110,9 +121,9 @@ different shells.
110121

111122
.. tab:: bash/zsh
112123

113-
For these shells, kitty adds a couple of lines to
114-
the bottom of the shell's rc files (in an atomic manner) to load the shell
115-
integration code.
124+
For these shells, kitty adds a couple of lines to
125+
the bottom of the shell's rc files (in an atomic manner) to load the shell
126+
integration code.
116127

117128
.. tab:: fish
118129

kitty/fast_data_types.pyi

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1272,7 +1272,7 @@ def set_window_padding(os_window_id: int, tab_id: int, window_id: int, left: int
12721272
def click_mouse_url(os_window_id: int, tab_id: int, window_id: int) -> bool:
12731273
pass
12741274

1275-
def click_mouse_cmd_output(os_window_id: int, tab_id: int, window_id: int) -> bool:
1275+
def click_mouse_cmd_output(os_window_id: int, tab_id: int, window_id: int, select_cmd_output: bool) -> bool:
12761276
pass
12771277

12781278
def move_cursor_to_mouse_if_in_prompt(os_window_id: int, tab_id: int, window_id: int) -> bool:

kitty/mouse.c

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -424,6 +424,12 @@ mouse_open_url(Window *w) {
424424
return screen_open_url(screen);
425425
}
426426

427+
bool
428+
mouse_set_last_visited_cmd_output(Window *w) {
429+
Screen *screen = w->render_data.screen;
430+
return screen_set_last_visited_prompt(screen, w->mouse_pos.cell_y);
431+
}
432+
427433
bool
428434
mouse_select_cmd_output(Window *w) {
429435
Screen *screen = w->render_data.screen;

kitty/options/definition.py

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -616,6 +616,13 @@
616616
mma('Extend the current selection even when grabbed',
617617
'extend_selection_grabbed shift+right press ungrabbed,grabbed mouse_selection extend',
618618
)
619+
620+
mma('Show clicked command output in pager',
621+
'show_clicked_cmd_output_ungrabbed ctrl+shift+right press ungrabbed mouse_show_command_output',
622+
long_text='''
623+
Requires :ref:`shell_integration` to work.
624+
'''
625+
)
619626
egr() # }}}
620627
egr() # }}}
621628

@@ -3006,11 +3013,14 @@
30063013
map('Scroll to next shell prompt',
30073014
'scroll_to_next_prompt kitty_mod+x scroll_to_prompt 1',
30083015
long_text='''
3016+
When the parameter is zero, scroll to the last jumped position, or the last clicked position by
3017+
command output related mouse actions.
3018+
30093019
Requires :ref:`shell_integration` to work.
30103020
'''
30113021
)
30123022

3013-
map('Browse scrollback buffer in less',
3023+
map('Browse scrollback buffer in pager',
30143024
'show_scrollback kitty_mod+h show_scrollback',
30153025
long_text='''
30163026
You can pipe the contents of the current screen + history buffer as
@@ -3024,17 +3034,27 @@
30243034
'''
30253035
)
30263036

3027-
map('Browse output of the last shell command in less',
3037+
map('Browse output of the last shell command in pager',
30283038
'show_last_command_output kitty_mod+g show_last_command_output',
30293039
long_text='''
3030-
Requires :ref:`shell_integration` to work. You can pipe the output
3031-
of the last command run in the shell using the :doc:`launch` function.
3040+
You can also define additional shortcuts to get the command output.
3041+
For example, to get the first command output on screen::
3042+
3043+
map f1 show_first_command_output_on_screen
3044+
3045+
To get the command output that was last accessed by a keyboard action or mouse action::
3046+
3047+
map f1 show_last_visited_command_output
3048+
3049+
You can pipe the output of the last command run in the shell using the :doc:`launch` function.
30323050
For example, the following opens the output in less in an overlay window::
30333051
30343052
map f1 launch --stdin-source=@last_cmd_output --stdin-add-formatting --type=overlay less +G -R
30353053
30363054
To get the output of the first command on the screen, use :code:`@first_cmd_output_on_screen`.
30373055
To get the output of the last jumped to command, use :code:`@last_visited_cmd_output`.
3056+
3057+
Requires :ref:`shell_integration` to work.
30383058
''')
30393059
egr() # }}}
30403060

kitty/options/types.py

Lines changed: 2 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

kitty/screen.c

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1269,7 +1269,10 @@ screen_cursor_to_column(Screen *self, unsigned int column) {
12691269
linebuf_init_line(self->linebuf, bottom); \
12701270
historybuf_add_line(self->historybuf, self->linebuf->line, &self->as_ansi_buf); \
12711271
self->history_line_added_count++; \
1272-
if (self->last_visited_prompt.is_set && self->last_visited_prompt.scrolled_by < self->historybuf->count) self->last_visited_prompt.scrolled_by++; \
1272+
if (self->last_visited_prompt.is_set) { \
1273+
if (self->last_visited_prompt.scrolled_by < self->historybuf->count) self->last_visited_prompt.scrolled_by++; \
1274+
else self->last_visited_prompt.is_set = false; \
1275+
} \
12731276
} \
12741277
linebuf_clear_line(self->linebuf, bottom, true); \
12751278
self->is_dirty = true; \
@@ -2024,7 +2027,7 @@ screen_history_scroll_to_prompt(Screen *self, int num_of_prompts_to_jump) {
20242027
if (self->linebuf != self->main_linebuf) return false;
20252028
unsigned int old = self->scrolled_by;
20262029
if (num_of_prompts_to_jump == 0) {
2027-
if (!self->last_visited_prompt.is_set || self->last_visited_prompt.scrolled_by > self->historybuf->count) return false;
2030+
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;
20282031
self->scrolled_by = self->last_visited_prompt.scrolled_by;
20292032
} else {
20302033
int delta = num_of_prompts_to_jump < 0 ? -1 : 1;
@@ -2041,8 +2044,7 @@ screen_history_scroll_to_prompt(Screen *self, int num_of_prompts_to_jump) {
20412044
}
20422045
#undef ensure_y_ok
20432046
self->scrolled_by = y >= 0 ? 0 : -y;
2044-
self->last_visited_prompt.scrolled_by = self->scrolled_by;
2045-
self->last_visited_prompt.is_set = true;
2047+
screen_set_last_visited_prompt(self, 0);
20462048
}
20472049
if (old != self->scrolled_by) self->scroll_changed = true;
20482050
return old != self->scrolled_by;
@@ -2756,7 +2758,7 @@ cmd_output(Screen *self, PyObject *args) {
27562758
break;
27572759
case 2: // last visited cmd
27582760
if (self->last_visited_prompt.scrolled_by <= self->historybuf->count && self->last_visited_prompt.is_set) {
2759-
found = find_cmd_output(self, &oo, 0, self->last_visited_prompt.scrolled_by, 0, false);
2761+
found = find_cmd_output(self, &oo, self->last_visited_prompt.y, self->last_visited_prompt.scrolled_by, 0, false);
27602762
} break;
27612763
default:
27622764
PyErr_Format(PyExc_KeyError, "%u is not a valid type of command", which);
@@ -2766,6 +2768,15 @@ cmd_output(Screen *self, PyObject *args) {
27662768
Py_RETURN_NONE;
27672769
}
27682770

2771+
bool
2772+
screen_set_last_visited_prompt(Screen *self, index_type y) {
2773+
if (y >= self->lines) return false;
2774+
self->last_visited_prompt.scrolled_by = self->scrolled_by;
2775+
self->last_visited_prompt.y = y;
2776+
self->last_visited_prompt.is_set = true;
2777+
return true;
2778+
}
2779+
27692780
bool
27702781
screen_select_cmd_output(Screen *self, index_type y) {
27712782
if (y >= self->lines) return false;

kitty/screen.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,7 @@ typedef struct {
146146
} last_rendered_window_char;
147147
struct {
148148
unsigned int scrolled_by;
149+
index_type y;
149150
bool is_set;
150151
} last_visited_prompt;
151152
} Screen;
@@ -246,6 +247,7 @@ void set_active_hyperlink(Screen*, char*, char*);
246247
hyperlink_id_type screen_mark_hyperlink(Screen*, index_type, index_type);
247248
void screen_handle_graphics_command(Screen *self, const GraphicsCommand *cmd, const uint8_t *payload);
248249
bool screen_open_url(Screen*);
250+
bool screen_set_last_visited_prompt(Screen*, index_type);
249251
bool screen_select_cmd_output(Screen*, index_type);
250252
void screen_dirty_sprite_positions(Screen *self);
251253
void screen_rescale_images(Screen *self);

kitty/state.c

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1091,12 +1091,13 @@ click_mouse_url(id_type os_window_id, id_type tab_id, id_type window_id) {
10911091
}
10921092

10931093
static bool
1094-
click_mouse_cmd_output(id_type os_window_id, id_type tab_id, id_type window_id) {
1095-
bool selected = false;
1094+
click_mouse_cmd_output(id_type os_window_id, id_type tab_id, id_type window_id, int select_cmd_output) {
1095+
bool handled = false;
10961096
WITH_WINDOW(os_window_id, tab_id, window_id);
1097-
selected = mouse_select_cmd_output(window);
1097+
handled = mouse_set_last_visited_cmd_output(window);
1098+
if (select_cmd_output && handled) handled = mouse_select_cmd_output(window);
10981099
END_WITH_WINDOW;
1099-
return selected;
1100+
return handled;
11001101
}
11011102

11021103
static bool
@@ -1127,8 +1128,8 @@ PYWRAP1(click_mouse_url) {
11271128
}
11281129

11291130
PYWRAP1(click_mouse_cmd_output) {
1130-
id_type a, b, c; PA("KKK", &a, &b, &c);
1131-
if (click_mouse_cmd_output(a, b, c)) { Py_RETURN_TRUE; }
1131+
id_type a, b, c; int d; PA("KKKp", &a, &b, &c, &d);
1132+
if (click_mouse_cmd_output(a, b, c, d)) { Py_RETURN_TRUE; }
11321133
Py_RETURN_FALSE;
11331134
}
11341135

kitty/state.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -311,6 +311,7 @@ void update_os_window_title(OSWindow *os_window);
311311
void fake_scroll(Window *w, int amount, bool upwards);
312312
Window* window_for_window_id(id_type kitty_window_id);
313313
bool mouse_open_url(Window *w);
314+
bool mouse_set_last_visited_cmd_output(Window *w);
314315
bool mouse_select_cmd_output(Window *w);
315316
bool move_cursor_to_mouse_if_at_shell_prompt(Window *w);
316317
void mouse_selection(Window *w, int code, int button);

kitty/window.py

Lines changed: 21 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1014,8 +1014,17 @@ def paste_selection_or_clipboard(self) -> None:
10141014
10151015
Requires :ref:`shell_integration` to work
10161016
''')
1017-
def mouse_select_cmd_output(self) -> None:
1018-
click_mouse_cmd_output(self.os_window_id, self.tab_id, self.id)
1017+
def mouse_select_command_output(self) -> None:
1018+
click_mouse_cmd_output(self.os_window_id, self.tab_id, self.id, True)
1019+
1020+
@ac('mouse', '''
1021+
Show clicked command output in a pager like less
1022+
1023+
Requires :ref:`shell_integration` to work
1024+
''')
1025+
def mouse_show_command_output(self) -> None:
1026+
if click_mouse_cmd_output(self.os_window_id, self.tab_id, self.id, False):
1027+
self.show_cmd_output(CommandOutput.last_visited, 'Clicked command output')
10191028
# }}}
10201029

10211030
def text_for_selection(self) -> str:
@@ -1087,35 +1096,35 @@ def show_scrollback(self) -> None:
10871096
cursor_on_screen = self.screen.scrolled_by < self.screen.lines - self.screen.cursor.y
10881097
get_boss().display_scrollback(self, data['text'], data['input_line_number'], report_cursor=cursor_on_screen)
10891098

1099+
def show_cmd_output(self, which: CommandOutput, title: str = 'Command output', as_ansi: bool = True, add_wrap_markers: bool = True) -> None:
1100+
text = self.cmd_output(which, as_ansi=as_ansi, add_wrap_markers=add_wrap_markers)
1101+
text = text.replace('\r\n', '\n').replace('\r', '\n')
1102+
get_boss().display_scrollback(self, text, title=title, report_cursor=False)
1103+
10901104
@ac('cp', '''
10911105
Show output from the first shell command on screen in a pager like less
10921106
10931107
Requires :ref:`shell_integration` to work
10941108
''')
10951109
def show_first_command_output_on_screen(self) -> None:
1096-
text = self.cmd_output(CommandOutput.first_on_screen, as_ansi=True, add_wrap_markers=True)
1097-
text = text.replace('\r\n', '\n').replace('\r', '\n')
1098-
get_boss().display_scrollback(self, text, title='First command output on screen', report_cursor=False)
1110+
self.show_cmd_output(CommandOutput.first_on_screen, 'First command output on screen')
10991111

11001112
@ac('cp', '''
11011113
Show output from the last shell command in a pager like less
11021114
11031115
Requires :ref:`shell_integration` to work
11041116
''')
11051117
def show_last_command_output(self) -> None:
1106-
text = self.cmd_output(CommandOutput.last_run, as_ansi=True, add_wrap_markers=True)
1107-
text = text.replace('\r\n', '\n').replace('\r', '\n')
1108-
get_boss().display_scrollback(self, text, title='Last command output', report_cursor=False)
1118+
self.show_cmd_output(CommandOutput.last_run, 'Last command output')
11091119

11101120
@ac('cp', '''
1111-
Show the first output below the last scrolled position via scroll_to_prompt in a pager like less
1121+
Show the first command output below the last scrolled position via scroll_to_prompt
1122+
or the last mouse clicked command output in a pager like less
11121123
11131124
Requires :ref:`shell_integration` to work
11141125
''')
11151126
def show_last_visited_command_output(self) -> None:
1116-
text = self.cmd_output(CommandOutput.last_visited, as_ansi=True, add_wrap_markers=True)
1117-
text = text.replace('\r\n', '\n').replace('\r', '\n')
1118-
get_boss().display_scrollback(self, text, title='Last visited command output', report_cursor=False)
1127+
self.show_cmd_output(CommandOutput.last_visited, 'Last visited command output')
11191128

11201129
def paste_bytes(self, text: Union[str, bytes]) -> None:
11211130
# paste raw bytes without any processing

0 commit comments

Comments
 (0)