Allows to open the command output in pager by mouse click
This commit is contained in:
parent
17a48f6b9f
commit
b91809eaa4
@ -74,29 +74,40 @@ on the output, define the following in :file:`kitty.conf`:
|
|||||||
|
|
||||||
.. code:: conf
|
.. code:: conf
|
||||||
|
|
||||||
mouse_map right press ungrabbed mouse_select_command_output
|
mouse_map right press ungrabbed mouse_select_command_output
|
||||||
|
|
||||||
Now, when you right click on the output, the entire output is selected, ready
|
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`:
|
||||||
|
|
||||||
.. code:: conf
|
.. code:: conf
|
||||||
|
|
||||||
map f1 scroll_to_prompt 0
|
map f1 scroll_to_prompt 0
|
||||||
|
|
||||||
|
|
||||||
How it works
|
How it works
|
||||||
@ -110,9 +121,9 @@ different shells.
|
|||||||
|
|
||||||
.. tab:: bash/zsh
|
.. tab:: bash/zsh
|
||||||
|
|
||||||
For these shells, kitty adds a couple of lines to
|
For these shells, kitty adds a couple of lines to
|
||||||
the bottom of the shell's rc files (in an atomic manner) to load the shell
|
the bottom of the shell's rc files (in an atomic manner) to load the shell
|
||||||
integration code.
|
integration code.
|
||||||
|
|
||||||
.. tab:: fish
|
.. tab:: fish
|
||||||
|
|
||||||
|
|||||||
@ -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:
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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() # }}}
|
||||||
|
|
||||||
|
|||||||
2
kitty/options/types.py
generated
2
kitty/options/types.py
generated
@ -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')),
|
||||||
]
|
]
|
||||||
|
|||||||
@ -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;
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -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);
|
||||||
|
|||||||
@ -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
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user