From f35398d797315333761482471ce88f07d816de7c Mon Sep 17 00:00:00 2001 From: Jason Lee Date: Fri, 5 Sep 2025 19:09:46 +0800 Subject: [PATCH] input: Fix some Rope method call will crash. (#1213) --- crates/ui/src/input/rope_ext.rs | 25 +++++++++++++++++++++---- crates/ui/src/input/state.rs | 14 +++++++++++--- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/crates/ui/src/input/rope_ext.rs b/crates/ui/src/input/rope_ext.rs index 76ad9538..e334af92 100644 --- a/crates/ui/src/input/rope_ext.rs +++ b/crates/ui/src/input/rope_ext.rs @@ -1,23 +1,40 @@ pub(crate) trait RopeExt { /// Get the index of (line, column) (0-based) from the byte offset (0-based). /// If the offset is out of bounds, return the last line and column. + /// + /// If the `offset` is out of bounds, it returns (0, 0). fn line_column(&self, byte_offset: usize) -> (usize, usize); /// Get the byte offset (0-based) from the line, column (0-based). + /// + /// Return the last line, if line is out of bounds. + /// Return the end column of line, if the column is out of bounds. fn line_column_to_byte(&self, line_ix: usize, column_ix: usize) -> usize; } impl RopeExt for ropey::Rope { fn line_column(&self, offset: usize) -> (usize, usize) { - let line_ix = self.byte_to_line(offset); - let line_offset = offset.saturating_sub(self.line_to_byte(line_ix)); + let Ok(line_ix) = self.try_byte_to_line(offset) else { + return (0, 0); + }; + let line = self.line(line_ix); - let column_ix = line.byte_to_char(line_offset); + let line_start_byte = self.line_to_byte(line_ix); + let line_offset = offset.saturating_sub(line_start_byte); + + let column_ix = line + .try_byte_to_char(line_offset) + .unwrap_or(line.len_chars()); (line_ix, column_ix) } fn line_column_to_byte(&self, line_ix: usize, column_ix: usize) -> usize { + let line_ix = self.len_lines().saturating_sub(1).min(line_ix); let line = self.line(line_ix); - self.line_to_byte(line_ix) + line.char_to_byte(column_ix) + + self.line_to_byte(line_ix) + + line + .try_char_to_byte(column_ix) + .unwrap_or(line.len_bytes().saturating_sub(1)) } } diff --git a/crates/ui/src/input/state.rs b/crates/ui/src/input/state.rs index bbe38493..3dbbbd20 100644 --- a/crates/ui/src/input/state.rs +++ b/crates/ui/src/input/state.rs @@ -565,10 +565,18 @@ impl InputState { let offset = self.cursor().offset; let was_preferred_column = self.preferred_column; - let line_ix = self.text.byte_to_line(offset); + let Ok(line_ix) = self.text.try_byte_to_line(offset) else { + return; + }; + let new_line_ix = line_ix.saturating_add_signed(move_lines); - let line = self.text.line(new_line_ix); - let line_start_offset = self.text.line_to_byte(new_line_ix); + let Some(line) = self.text.get_line(new_line_ix) else { + return; + }; + let Ok(line_start_offset) = self.text.try_line_to_byte(new_line_ix) else { + return; + }; + let new_column = self .preferred_column .unwrap_or_default()