From a4bfaa512be8b7214b80c63327930155da86978a Mon Sep 17 00:00:00 2001 From: Daniel Somerfield <6019932+danielsomerfield@users.noreply.github.com> Date: Fri, 15 Dec 2023 09:18:05 -0800 Subject: [PATCH] Better behaviour on resize (#675) * First shot at resolving the resizing issues with terminals * Comment for known bug * Checking the start row repeatedly to see how it affects performance --------- Co-authored-by: Daniel Somerfield --- src/painting/painter.rs | 45 +++++++++++++++++------------------- src/painting/prompt_lines.rs | 1 + 2 files changed, 22 insertions(+), 24 deletions(-) diff --git a/src/painting/painter.rs b/src/painting/painter.rs index e6dc79a8..bee86429 100644 --- a/src/painting/painter.rs +++ b/src/painting/painter.rs @@ -82,7 +82,7 @@ impl Painter { /// Returns the available lines from the prompt down pub fn remaining_lines(&self) -> u16 { - self.screen_height() - self.prompt_start_row + self.screen_height().saturating_sub(self.prompt_start_row) } /// Sets the prompt origin position and screen size for a new line editor @@ -151,8 +151,14 @@ impl Painter { // Marking the painter state as larger buffer to avoid animations self.large_buffer = required_lines >= screen_height; + // This might not be terribly performant. Testing it out + let is_reset = || match cursor::position() { + Ok(position) => position.1 < self.prompt_start_row, + Err(_) => false, + }; + // Moving the start position of the cursor based on the size of the required lines - if self.large_buffer { + if self.large_buffer || is_reset() { self.prompt_start_row = 0; } else if required_lines >= remaining_lines { let extra = required_lines.saturating_sub(remaining_lines); @@ -400,31 +406,22 @@ impl Painter { /// Updates prompt origin and offset to handle a screen resize event pub(crate) fn handle_resize(&mut self, width: u16, height: u16) { - let prev_terminal_size = self.terminal_size; - let prev_prompt_row = self.prompt_start_row; - self.terminal_size = (width, height); - if prev_prompt_row < height - && height <= prev_terminal_size.1 - && width == prev_terminal_size.0 - { - // The terminal got smaller in height but the start of the prompt is still visible - // The width didn't change - return; + // `cursor::position() is blocking and can timeout. + // The question is whether we can afford it. If not, perhaps we should use it in some scenarios but not others + // The problem is trying to calculate this internally doesn't seem to be reliable because terminals might + // have additional text in their buffer that messes with the offset on scroll. + // It seems like it _should_ be ok because it only happens on resize. + + // Known bug: on iterm2 and kitty, clearing the screen via CMD-K doesn't reset + // the position. Might need to special-case this. + // + // I assume this is a bug with the position() call but haven't figured that + // out yet. + if let Ok(position) = cursor::position() { + self.prompt_start_row = position.1; } - - // Either: - // - The terminal got larger in height - // - Note: if the terminal doesn't have sufficient history, this will leave a trail - // of previous prompts currently. - // - Note: if the the prompt contains multiple lines, this will leave a trail of - // previous prompts currently. - // - The terminal got smaller in height and the whole prompt is no longer visible - // - Note: if the the prompt contains multiple lines, this will leave a trail of - // previous prompts currently. - // - The width changed - self.prompt_start_row = height.saturating_sub(1); } /// Writes `line` to the terminal with a following carriage return and newline diff --git a/src/painting/prompt_lines.rs b/src/painting/prompt_lines.rs index 30c1ca61..aa9c35de 100644 --- a/src/painting/prompt_lines.rs +++ b/src/painting/prompt_lines.rs @@ -7,6 +7,7 @@ use crate::{ use std::borrow::Cow; /// Aggregate of prompt and input string used by `Painter` +#[derive(Debug)] pub(crate) struct PromptLines<'prompt> { pub(crate) prompt_str_left: Cow<'prompt, str>, pub(crate) prompt_str_right: Cow<'prompt, str>,