From e23d6877c0e9121a5c60706dbc1dc125c4ff97e6 Mon Sep 17 00:00:00 2001 From: Arvin Verain Date: Mon, 17 Feb 2025 09:33:29 +0800 Subject: [PATCH 1/3] fix: Page cmd outputs properly regardless of amount of prompt lines Also skip fetching lines of wrapped prompts --- kitty/screen.c | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/kitty/screen.c b/kitty/screen.c index 5d8658847..817abefec 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -4078,7 +4078,7 @@ find_cmd_output(Screen *self, OutputOffset *oo, index_type start_screen_y, unsig } else if (line && line->attrs.prompt_kind == OUTPUT_START && !range_line_is_continued(self, y1)) { found_output = true; start = y1; found_prompt = true; - // keep finding the first output start upwards + direction = 1; } y1--; y2++; } @@ -4091,22 +4091,21 @@ find_cmd_output(Screen *self, OutputOffset *oo, index_type start_screen_y, unsig line = checked_range_line(self, y1); if (line && line->attrs.prompt_kind == PROMPT_START && !range_line_is_continued(self, y1)) { if (direction == 0) { - // find around: stop at prompt start - start = y1 + 1; + found_prompt = true; break; } found_next_prompt = true; end = y1; } else if (line && line->attrs.prompt_kind == OUTPUT_START && !range_line_is_continued(self, y1)) { - start = y1; + found_output = true; start = y1; + found_prompt = true; break; } y1--; } if (y1 < upward_limit) { oo->reached_upper_limit = true; - start = upward_limit; + found_output = true; start = upward_limit; } - found_output = true; found_prompt = true; } // find downwards @@ -4116,11 +4115,16 @@ find_cmd_output(Screen *self, OutputOffset *oo, index_type start_screen_y, unsig line = checked_range_line(self, y2); if (line && line->attrs.prompt_kind == PROMPT_START) { if (!found_prompt) found_prompt = true; - else if (found_output && !found_next_prompt) { + else if (found_prompt && !found_output) { + // skip fetching wrapped prompt lines + while (range_line_is_continued(self, y2)) { + y2++; + } + } else if (found_output && !found_next_prompt) { found_next_prompt = true; end = y2; break; } - } else if (line && line->attrs.prompt_kind == OUTPUT_START && found_prompt && !found_output) { + } else if (line && line->attrs.prompt_kind == OUTPUT_START && !found_output) { found_output = true; start = y2; } y2++; From ccffbd23ce0a95f279029569aac53ae2a2103ef6 Mon Sep 17 00:00:00 2001 From: Arvin Verain Date: Tue, 18 Feb 2025 21:53:52 +0800 Subject: [PATCH 2/3] fix: First on screen paging fix when prompt is obscured in scrollback --- kitty/screen.c | 1 + 1 file changed, 1 insertion(+) diff --git a/kitty/screen.c b/kitty/screen.c index 817abefec..3246bd2e3 100644 --- a/kitty/screen.c +++ b/kitty/screen.c @@ -4126,6 +4126,7 @@ find_cmd_output(Screen *self, OutputOffset *oo, index_type start_screen_y, unsig } } else if (line && line->attrs.prompt_kind == OUTPUT_START && !found_output) { found_output = true; start = y2; + if (!found_prompt) found_prompt = true; } y2++; } From a5cafdd8e950a1471df15d312a162a2319147737 Mon Sep 17 00:00:00 2001 From: Arvin Verain Date: Wed, 19 Feb 2025 00:20:39 +0800 Subject: [PATCH 3/3] test: Add more tests for scroll_to_prompt, fco, and lvco --- kitty_tests/screen.py | 81 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 75 insertions(+), 6 deletions(-) diff --git a/kitty_tests/screen.py b/kitty_tests/screen.py index a8dbd16f7..c1d0d2b6f 100644 --- a/kitty_tests/screen.py +++ b/kitty_tests/screen.py @@ -1147,22 +1147,44 @@ def draw_output(n, x='', m=True): s.toggle_alt_screen() self.assertFalse(str(s.line(s.cursor.y))) - s = self.create_screen() + s = self.create_screen(scrollback=10) + draw_output(1, 'start') for i in range(4): - mark_prompt() - s.draw(f'$ {i}') - s.carriage_return() - s.index(), s.index() + draw_prompt(str(i) + 'ab') + draw_output(1, 'ut') self.ae(s.scrolled_by, 0) + self.ae(str(s.visual_line(0)), '$ 2ab') + self.assertFalse(s.scroll_to_prompt(-3)) + self.assertTrue(s.scroll_to_prompt()) + self.ae(str(s.visual_line(0)), '$ 1ab') + self.assertTrue(s.scroll_to_prompt()) + self.ae(str(s.visual_line(0)), '$ 0ab') + self.assertFalse(s.scroll_to_prompt()) + self.assertFalse(s.scroll_to_prompt(4)) + self.assertTrue(s.scroll_to_prompt(1)) + self.ae(str(s.visual_line(0)), '$ 1ab') + self.assertTrue(s.scroll_to_prompt(1)) + self.ae(str(s.visual_line(0)), '$ 2ab') + self.assertFalse(s.scroll_to_prompt(1)) + # wrap prompts + s.resize(s.lines + 1, s.columns - 2) + self.ae(s.scrolled_by, 0) + self.ae(str(s.visual_line(0)), 'ab') + self.assertFalse(s.scroll_to_prompt(-4)) + self.assertTrue(s.scroll_to_prompt()) + self.ae(str(s.visual_line(0)), '$ 2') self.assertTrue(s.scroll_to_prompt()) self.ae(str(s.visual_line(0)), '$ 1') self.assertTrue(s.scroll_to_prompt()) self.ae(str(s.visual_line(0)), '$ 0') self.assertFalse(s.scroll_to_prompt()) + self.assertFalse(s.scroll_to_prompt(4)) self.assertTrue(s.scroll_to_prompt(1)) self.ae(str(s.visual_line(0)), '$ 1') self.assertTrue(s.scroll_to_prompt(1)) self.ae(str(s.visual_line(0)), '$ 2') + self.assertTrue(s.scroll_to_prompt(1)) + self.ae(str(s.visual_line(0)), 'ab') self.assertFalse(s.scroll_to_prompt(1)) s = self.create_screen() @@ -1244,10 +1266,19 @@ def lvco(): s.scroll(2, False) self.ae(str(s.visual_line(0)), '$ 1') self.ae(fco(), '0x\n1x\n') + # test: obscure prompt in scrollback + s.resize(3, 5) + self.ae(str(s.visual_line(0)), '0x') + # without succeeding prompt + self.ae(fco(), '0x\n1x\n') + s.resize(4, 5) + draw_prompt('3') + # with succeeding prompt + self.ae(fco(), '0x\n1x') # resize # get last cmd output with continued output mark - draw_prompt('3'), draw_output(1, 'long_line'), draw_output(2, 'l', False) + draw_output(1, 'long_line'), draw_output(2, 'l', False) s.resize(4, 5) s.scroll_to_prompt(-4) self.ae(str(s.visual_line(0)), '$ 0') @@ -1257,6 +1288,44 @@ def lvco(): self.ae(lvco(), '0\n1\n2') s.scroll_to_prompt(1) self.ae(lvco(), '0x\n1x') + # test: obscure prompt + s.scroll(2, False) + self.ae(lvco(), '0x\n1x') + # test: prompts without output + s.scroll(s.scrolled_by, False) + draw_prompt('4') + # resizing resets last visited, rely on scroll_to_prompt + s.resize(2, s.columns + 4) + s.scroll_to_prompt() + s.scroll_to_prompt(1) + self.ae(str(s.visual_line(0)), '$ 4') + self.ae(lvco(), '') + s.resize(5, s.columns) + draw_prompt('wrapcmd') + s.scroll_to_prompt(1) + self.ae(lvco(), '') + draw_output(1, 'wrapout'), draw_output(1, 'y', False) + self.ae(lvco(), '0wrapout\n0y\n') + # wrap long prompt with long output + s.resize(5, s.columns - 4) + self.ae(str(s.visual_line(0)), 'pcmd') + # test: set last visited to previous empty prompt + s.scroll_to_prompt(-2) + self.ae(str(s.visual_line(0)), '$ 4') + self.ae(lvco(), '0wrapout\n0y\n') + mark_prompt(), s.draw('$ end') + self.ae(lvco(), '0wrapout\n0y') + # test: set last visited to continued line of long prompt + s.scroll_to_prompt(2) + self.ae(str(s.visual_line(0)), 'pcmd') + self.ae(lvco(), '0wrapout\n0y') + s.scroll_to_prompt() + self.ae(lvco(), '0wrapout\n0y') + # test: set last visited to continued line of output + s.carriage_return(), s.index() + draw_output(1, 'z') + s.scroll_to_prompt(1) + self.ae(lvco(), '0wrapout\n0y') # test that post rewrap prompt lines have correct attributes s = self.create_screen(cols=5, lines=5, scrollback=15)