Allow getting the last non-empty command output easily via an action or remote control

Fixes #4973
This commit is contained in:
Kovid Goyal 2022-05-28 15:19:24 +05:30
parent 4c74462763
commit 0509855930
No known key found for this signature in database
GPG Key ID: 06BC317B515ACE7C
6 changed files with 63 additions and 6 deletions

View File

@ -39,6 +39,9 @@ Detailed list of changes
- A new command :command:`edit-in-kitty` to :ref:`edit_file`
- Allow getting the last non-empty command output easily via an action or
remote control (:pull:`4973`)
- Fix a bug that caused :opt:`macos_colorspace` to always be ``default`` regardless of its actual value (:iss:`5129`)
- ssh kitten: Fix bash not being executed as a login shell since kitty 0.25.0 (:iss:`5130`)

View File

@ -73,6 +73,13 @@ left_shift_line(Line *line, index_type at, index_type num) {
}
}
static inline bool
line_is_empty(const Line *line) {
for (index_type i = 0; i < line->xnum; i++) {
if (line->cpu_cells[i].ch != BLANK_CHAR) return false;
}
return true;
}
typedef Line*(get_line_func)(void *, int);
void line_clear_text(Line *self, unsigned int at, unsigned int num, char_type ch);

View File

@ -30,14 +30,15 @@ class GetText(RemoteCommand):
options_spec = MATCH_WINDOW_OPTION + '''\n
--extent
default=screen
choices=screen, all, selection, first_cmd_output_on_screen, last_cmd_output, last_visited_cmd_output
choices=screen, all, selection, first_cmd_output_on_screen, last_cmd_output, last_visited_cmd_output, last_non_empty_output
What text to get. The default of :code:`screen` means all text currently on the screen.
:code:`all` means all the screen+scrollback and :code:`selection` means the
currently selected text. :code:`first_cmd_output_on_screen` means the output of the first
command that was run in the window on screen. :code:`last_cmd_output` means
the output of the last command that was run in the window. :code:`last_visited_cmd_output` means
the first command output below the last scrolled position via scroll_to_prompt. The last three
requires :ref:`shell_integration` to be enabled.
the first command output below the last scrolled position via scroll_to_prompt.
:code:`last_non_empty_output` is the output from the last command run in the window that had
some non empty output. The last four require :ref:`shell_integration` to be enabled.
--ansi
@ -99,6 +100,12 @@ Get text from the window this command is run in, rather than the active window.
as_ansi=bool(payload_get('ansi')),
add_wrap_markers=bool(payload_get('wrap_markers')),
)
elif payload_get('extent') == 'last_non_empty_output':
ans = window.cmd_output(
CommandOutput.last_non_empty,
as_ansi=bool(payload_get('ansi')),
add_wrap_markers=bool(payload_get('wrap_markers')),
)
elif payload_get('extent') == 'last_visited_cmd_output':
ans = window.cmd_output(
CommandOutput.last_visited,

View File

@ -2859,6 +2859,30 @@ cmd_output(Screen *self, PyObject *args) {
if (self->last_visited_prompt.scrolled_by <= self->historybuf->count && self->last_visited_prompt.is_set) {
found = find_cmd_output(self, &oo, self->last_visited_prompt.y, self->last_visited_prompt.scrolled_by, 0, false);
} break;
case 3: { // last non-empty output
int y = self->cursor->y;
Line *line;
bool reached_upper_limit = false;
while (!found && !reached_upper_limit) {
line = checked_range_line(self, y);
if (!line || (line->attrs.prompt_kind == OUTPUT_START && !line->attrs.continued)) {
int start = line ? y : y + 1; reached_upper_limit = !line;
int y2 = start; unsigned int num_lines = 0;
bool found_content = false;
while ((line = checked_range_line(self, y2)) && line->attrs.prompt_kind != PROMPT_START) {
if (!found_content) found_content = !line_is_empty(line);
num_lines++; y2++;
}
if (found_content) {
found = true;
oo.reached_upper_limit = reached_upper_limit;
oo.start = start; oo.num_lines = num_lines;
break;
}
}
y--;
}
} break;
default:
PyErr_Format(PyExc_KeyError, "%u is not a valid type of command", which);
return NULL;

View File

@ -164,7 +164,7 @@ class DynamicColor(IntEnum):
class CommandOutput(IntEnum):
last_run, first_on_screen, last_visited = 0, 1, 2
last_run, first_on_screen, last_visited, last_non_empty = 0, 1, 2, 3
DYNAMIC_COLOR_CODES = {
@ -1460,6 +1460,14 @@ class Window:
def show_last_visited_command_output(self) -> None:
self.show_cmd_output(CommandOutput.last_visited, 'Last visited command output')
@ac('cp', '''
Show the last non-empty output from a shell command in a pager like less
Requires :ref:`shell_integration` to work
''')
def show_last_non_empty_command_output(self) -> None:
self.show_cmd_output(CommandOutput.last_non_empty, 'Last non-empty command output')
@ac('cp', 'Paste the specified text into the current window')
def paste(self, text: str) -> None:
self.paste_with_actions(text)

View File

@ -1009,9 +1009,9 @@ class TestScreen(BaseTest):
self.assertTrue(s.scroll_to_prompt())
self.ae(str(s.visual_line(0)), '$ 1')
def lco(as_ansi=False):
def lco(as_ansi=False, which=0):
a = []
if s.cmd_output(0, a.append, as_ansi):
if s.cmd_output(which, a.append, as_ansi):
pht = pagerhist(s, as_ansi=as_ansi, upto_output_start=True)
if pht:
a.insert(0, pht)
@ -1098,3 +1098,11 @@ class TestScreen(BaseTest):
draw_prompt('p1')
draw_output(30)
self.ae(tuple(map(int, lco().split())), tuple(range(0, 30)))
# last non empty command output
draw_prompt('a'), draw_output(2, 'a')
draw_prompt('b'), mark_output()
self.ae(lco(), '')
self.ae(lco(which=3), '0a\n1a')
s.draw('running'), s.index(), s.carriage_return()
self.ae(lco(which=3), 'running\n')