From 7066fb7bbbd3e69fccc7bdd25b2151ecbc26b598 Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Sun, 19 Nov 2023 21:07:16 -0800 Subject: [PATCH] terminal: ESC [ 2 J does a scroll and clear if viewport is at a prompt --- src/terminal/Terminal.zig | 36 ++++++++++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/src/terminal/Terminal.zig b/src/terminal/Terminal.zig index 6544a6d67..22a57a546 100644 --- a/src/terminal/Terminal.zig +++ b/src/terminal/Terminal.zig @@ -1159,6 +1159,42 @@ pub fn eraseDisplay( }, .complete => { + // If we're on the primary screen and our last non-empty row is + // a prompt, then we do a scroll_complete instead. This is a + // heuristic to get the generally desirable behavior that ^L + // at a prompt scrolls the screen contents prior to clearing. + // Most shells send `ESC [ H ESC [ 2 J` so we can't just check + // our current cursor position. See #905 + if (self.active_screen == .primary) at_prompt: { + // Go from the bottom of the viewport up and see if we're + // at a prompt. + const viewport_max = Screen.RowIndexTag.viewport.maxLen(&self.screen); + for (0..viewport_max) |y| { + const bottom_y = viewport_max - y - 1; + const row = self.screen.getRow(.{ .viewport = bottom_y }); + if (row.isEmpty()) continue; + switch (row.getSemanticPrompt()) { + // If we're at a prompt or input area, then we are at a prompt. + .prompt, + .prompt_continuation, + .input, + => break, + + // If we have command output, then we're most certainly not + // at a prompt. + .command => break :at_prompt, + + // If we don't know, we keep searching. + .unknown => {}, + } + } else break :at_prompt; + + self.screen.scroll(.{ .clear = {} }) catch { + // If we fail, we just fall back to doing a normal clear + // so we don't worry about the error. + }; + } + var it = self.screen.rowIterator(.active); while (it.next()) |row| { row.setWrapped(false);