From 38c05984e0fbf59d892d28870e3c94b7aed6bdfd Mon Sep 17 00:00:00 2001 From: Roman Stingler Date: Wed, 31 Dec 2025 17:37:14 +0100 Subject: [PATCH] fix: prevent error message corruption during scrolling - Preserve error messages during scroll actions (ScrollUp/ScrollDown) - Force error line to be included in render diff to prevent artifacts - Skip bottom-right corner cell to prevent terminal scrolling - Clean newlines from error messages and ensure proper width - Fix scroll boundary check (< instead of <=) - Add lifetime annotation to Change struct for better memory safety The issue was that scrolling would corrupt the error display because --- src/editor.rs | 9 +++++++-- src/editor/render_buffer.rs | 2 +- src/editor/rendering.rs | 36 ++++++++++++++++++++++++++++++++---- 3 files changed, 40 insertions(+), 7 deletions(-) diff --git a/src/editor.rs b/src/editor.rs index 42fbf75..2152e97 100644 --- a/src/editor.rs +++ b/src/editor.rs @@ -1975,9 +1975,14 @@ impl Editor { tracking: bool, ) -> anyhow::Result { // log!("Action: {action:?}"); - self.last_error = None; self.actions.push(action.clone()); + // Don't clear error on scroll actions to preserve error messages + match action { + Action::ScrollUp | Action::ScrollDown => {} + _ => self.last_error = None, + } + let mut add_to_history = tracking; match action { @@ -2653,7 +2658,7 @@ impl Editor { if self.vtop >= scroll_lines { self.vtop -= scroll_lines; let desired_cy = self.cy + scroll_lines; - if desired_cy <= self.vheight() { + if desired_cy < self.vheight() { self.cy = desired_cy; } self.render(buffer)?; diff --git a/src/editor/render_buffer.rs b/src/editor/render_buffer.rs index d50cb21..d19f571 100644 --- a/src/editor/render_buffer.rs +++ b/src/editor/render_buffer.rs @@ -231,7 +231,7 @@ impl RenderBuffer { s } - pub fn diff(&self, other: &RenderBuffer) -> Vec { + pub fn diff(&self, other: &RenderBuffer) -> Vec> { let mut changes = vec![]; for (pos, cell) in self.cells.iter().enumerate() { if *cell != other.cells[pos] { diff --git a/src/editor/rendering.rs b/src/editor/rendering.rs index a2feba5..ce763a2 100644 --- a/src/editor/rendering.rs +++ b/src/editor/rendering.rs @@ -47,7 +47,26 @@ impl Editor { self.update_and_render_overlays(buffer)?; // Flush changes to terminal - let diff = buffer.diff(¤t_buffer); + let mut diff = buffer.diff(¤t_buffer); + + // If there's an active error, ensure the error line is always included in the diff + // This prevents scrolling artifacts from corrupting the error display + if self.last_error.is_some() { + let error_line = self.size.1 as usize - 1; + let width = self.size.0 as usize; + // Force all cells on the error line to be included in the diff + for x in 0..width { + let pos = error_line * width + x; + if pos < buffer.cells.len() { + diff.push(Change { + x, + y: error_line, + cell: &buffer.cells[pos], + }); + } + } + } + self.render_diff(diff)?; Ok(()) @@ -909,14 +928,22 @@ impl Editor { let mut skip_next = false; for (i, change) in sorted_changes.iter().enumerate() { + let x = change.x; + let y = change.y; + + // Skip the bottom-right corner to prevent scrolling + if x == (self.size.0 as usize).saturating_sub(1) + && y == (self.size.1 as usize).saturating_sub(1) + { + continue; + } + // Skip if this was a padding space after an emoji if skip_next { skip_next = false; continue; } - let x = change.x; - let y = change.y; let cell = change.cell; // Check if this is an emoji followed by a space (padding) @@ -1086,7 +1113,8 @@ impl Editor { let wc = format!("{: