diff --git a/crates/edit/src/buffer/mod.rs b/crates/edit/src/buffer/mod.rs index 9277192e500..06b232855d9 100644 --- a/crates/edit/src/buffer/mod.rs +++ b/crates/edit/src/buffer/mod.rs @@ -3135,7 +3135,8 @@ fn detect_bom(bytes: &[u8]) -> Option<&'static str> { #[cfg(test)] mod tests { - use super::{SearchOptions, TextBuffer}; + use super::{CursorMovement, SearchOptions, TextBuffer}; + use crate::helpers::Point; fn buffer_contents(buf: &mut TextBuffer) -> String { let mut str = String::new(); @@ -3179,4 +3180,29 @@ mod tests { assert_eq!(buffer_contents(&mut buf), "ax\nbx\nx\n"); } + + #[test] + fn cursor_left_from_wrapped_eof_stays_on_text() { + let mut buf = TextBuffer::new(false).unwrap(); + buf.set_crlf(false); + let text = concat!( + "WordWrapBugTest WordWrapBugTest WordWrapBugTest ", + "WordWrapBugTestWordWrapBugTest ", + "WordWrapBugTestWordWrapBugTest ", + "WordWrapBugTestWordWrapBugTest WordWrapBugTest", + ); + buf.write_raw(text.as_bytes()); + buf.set_margin_enabled(true); + buf.set_word_wrap(true); + buf.set_width(79); + buf.cursor_move_to_logical(Point::MAX); + + assert_eq!(buf.cursor_visual_pos(), Point { x: 47, y: 2 }); + assert_eq!(buf.visual_line_count(), 3); + + buf.cursor_move_delta(CursorMovement::Grapheme, -1); + + assert_eq!(buf.cursor_visual_pos(), Point { x: 46, y: 2 }); + assert_eq!(buf.cursor_logical_pos().y, 0); + } } diff --git a/crates/edit/src/unicode/measurement.rs b/crates/edit/src/unicode/measurement.rs index aab03204589..7a6b5000cb6 100644 --- a/crates/edit/src/unicode/measurement.rs +++ b/crates/edit/src/unicode/measurement.rs @@ -359,6 +359,10 @@ impl<'doc> MeasurementConfig<'doc> { // Of course we only need to do this if the cursor isn't on a wrap opportunity already. // The loop below should not modify the target we already found. + let mut chunk_iter = Utf8Chars::new(self.buffer.read_forward(offset), 0); + let mut chunk_range = offset..offset + chunk_iter.len(); + let mut props_next_cluster = ucd_start_of_text_properties(); + let mut offset_lookahead = offset; let mut visual_pos_x_lookahead = visual_pos_x; loop { @@ -409,7 +413,7 @@ impl<'doc> MeasurementConfig<'doc> { } } - if offset_next_cluster == offset { + if offset_next_cluster == offset_lookahead { // No advance and the iterator is empty? End of text reached. if chunk_iter.is_empty() { break; @@ -443,6 +447,8 @@ impl<'doc> MeasurementConfig<'doc> { } else if !ucd_line_break_joins(props_current_cluster, props_next_cluster) { break; } + + offset_lookahead = offset_next_cluster; } }