From 074e3cfbd68a5429e244b204773486959845a90d Mon Sep 17 00:00:00 2001 From: Julia Date: Wed, 16 Nov 2022 13:29:26 -0500 Subject: [PATCH] Clamp UTF-16 to point conversions --- crates/editor/src/editor.rs | 2 +- crates/editor/src/multi_buffer.rs | 22 +++++++----- crates/editor/src/selections_collection.rs | 27 ++++++++++++-- crates/project_symbols/src/project_symbols.rs | 2 +- crates/rope/src/rope.rs | 35 ++++++++----------- crates/text/src/text.rs | 22 ++++++------ 6 files changed, 65 insertions(+), 45 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index dd5934f979..7206dbb199 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -55,7 +55,7 @@ use link_go_to_definition::{ }; pub use multi_buffer::{ Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset, - ToPoint, + ToOffsetClamped, ToPoint, }; use multi_buffer::{MultiBufferChunks, ToOffsetUtf16}; use ordered_float::OrderedFloat; diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index 38475daf28..70645bbfe0 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -72,6 +72,10 @@ pub trait ToOffset: 'static + fmt::Debug { fn to_offset(&self, snapshot: &MultiBufferSnapshot) -> usize; } +pub trait ToOffsetClamped: 'static + fmt::Debug { + fn to_offset_clamped(&self, snapshot: &MultiBufferSnapshot) -> usize; +} + pub trait ToOffsetUtf16: 'static + fmt::Debug { fn to_offset_utf16(&self, snapshot: &MultiBufferSnapshot) -> OffsetUtf16; } @@ -1945,9 +1949,9 @@ impl MultiBufferSnapshot { } } - pub fn point_utf16_to_offset(&self, point: PointUtf16) -> usize { + pub fn point_utf16_to_offset_clamped(&self, point: PointUtf16) -> usize { if let Some((_, _, buffer)) = self.as_singleton() { - return buffer.point_utf16_to_offset(point); + return buffer.point_utf16_to_offset_clamped(point); } let mut cursor = self.excerpts.cursor::<(PointUtf16, usize)>(); @@ -1961,7 +1965,7 @@ impl MultiBufferSnapshot { .offset_to_point_utf16(excerpt.range.context.start.to_offset(&excerpt.buffer)); let buffer_offset = excerpt .buffer - .point_utf16_to_offset(excerpt_start_point + overshoot); + .point_utf16_to_offset_clamped(excerpt_start_point + overshoot); *start_offset + (buffer_offset - excerpt_start_offset) } else { self.excerpts.summary().text.len @@ -3274,12 +3278,6 @@ impl ToOffset for Point { } } -impl ToOffset for PointUtf16 { - fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize { - snapshot.point_utf16_to_offset(*self) - } -} - impl ToOffset for usize { fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize { assert!(*self <= snapshot.len(), "offset is out of range"); @@ -3293,6 +3291,12 @@ impl ToOffset for OffsetUtf16 { } } +impl ToOffsetClamped for PointUtf16 { + fn to_offset_clamped<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize { + snapshot.point_utf16_to_offset_clamped(*self) + } +} + impl ToOffsetUtf16 for OffsetUtf16 { fn to_offset_utf16(&self, _snapshot: &MultiBufferSnapshot) -> OffsetUtf16 { *self diff --git a/crates/editor/src/selections_collection.rs b/crates/editor/src/selections_collection.rs index 256405f20e..01e38d269e 100644 --- a/crates/editor/src/selections_collection.rs +++ b/crates/editor/src/selections_collection.rs @@ -14,6 +14,7 @@ use util::post_inc; use crate::{ display_map::{DisplayMap, DisplaySnapshot, ToDisplayPoint}, Anchor, DisplayPoint, ExcerptId, MultiBuffer, MultiBufferSnapshot, SelectMode, ToOffset, + ToOffsetClamped, }; #[derive(Clone)] @@ -544,11 +545,33 @@ impl<'a> MutableSelectionsCollection<'a> { T: ToOffset, { let buffer = self.buffer.read(self.cx).snapshot(self.cx); + let ranges = ranges + .into_iter() + .map(|range| range.start.to_offset(&buffer)..range.end.to_offset(&buffer)); + self.select_offset_ranges(ranges); + } + + pub fn select_clamped_ranges(&mut self, ranges: I) + where + I: IntoIterator>, + T: ToOffsetClamped, + { + let buffer = self.buffer.read(self.cx).snapshot(self.cx); + let ranges = ranges.into_iter().map(|range| { + range.start.to_offset_clamped(&buffer)..range.end.to_offset_clamped(&buffer) + }); + self.select_offset_ranges(ranges); + } + + fn select_offset_ranges(&mut self, ranges: I) + where + I: IntoIterator>, + { let selections = ranges .into_iter() .map(|range| { - let mut start = range.start.to_offset(&buffer); - let mut end = range.end.to_offset(&buffer); + let mut start = range.start; + let mut end = range.end; let reversed = if start > end { mem::swap(&mut start, &mut end); true diff --git a/crates/project_symbols/src/project_symbols.rs b/crates/project_symbols/src/project_symbols.rs index 273230fe26..60623e99ca 100644 --- a/crates/project_symbols/src/project_symbols.rs +++ b/crates/project_symbols/src/project_symbols.rs @@ -151,7 +151,7 @@ impl ProjectSymbolsView { let editor = workspace.open_project_item::(buffer, cx); editor.update(cx, |editor, cx| { editor.change_selections(Some(Autoscroll::center()), cx, |s| { - s.select_ranges([position..position]) + s.select_clamped_ranges([position..position]) }); }); }); diff --git a/crates/rope/src/rope.rs b/crates/rope/src/rope.rs index b6a4930f0b..83e9c96ca9 100644 --- a/crates/rope/src/rope.rs +++ b/crates/rope/src/rope.rs @@ -259,7 +259,7 @@ impl Rope { .map_or(0, |chunk| chunk.point_to_offset(overshoot)) } - pub fn point_utf16_to_offset(&self, point: PointUtf16, clamp: bool) -> usize { + pub fn point_utf16_to_offset_clamped(&self, point: PointUtf16) -> usize { if point >= self.summary().lines_utf16() { return self.summary().len; } @@ -269,7 +269,7 @@ impl Rope { cursor.start().1 + cursor .item() - .map_or(0, |chunk| chunk.point_utf16_to_offset(overshoot, clamp)) + .map_or(0, |chunk| chunk.point_utf16_to_offset_clamped(overshoot)) } pub fn point_utf16_to_point(&self, point: PointUtf16) -> Point { @@ -711,7 +711,7 @@ impl Chunk { point_utf16 } - fn point_utf16_to_offset(&self, target: PointUtf16, clamp: bool) -> usize { + fn point_utf16_to_offset_clamped(&self, target: PointUtf16) -> usize { let mut offset = 0; let mut point = PointUtf16::new(0, 0); for ch in self.0.chars() { @@ -724,26 +724,19 @@ impl Chunk { point.column = 0; if point.row > target.row { - if clamp { - //Return the offset up to but not including the newline - return offset; - } - panic!( - "point {:?} is beyond the end of a line with length {}", - target, point.column - ); + //point is beyond the end of the line + //Return the offset up to but not including the newline + return offset; } } else { point.column += ch.len_utf16() as u32; } if point > target { - if clamp { - //Return the offset before adding the len of the codepoint which - //we have landed within, bias left - return offset; - } - panic!("point {:?} is inside of character {:?}", target, ch); + //point is inside of a codepoint + //Return the offset before adding the len of the codepoint which + //we have landed within, bias left + return offset; } offset += ch.len_utf8(); @@ -1222,7 +1215,7 @@ mod tests { point ); assert_eq!( - actual.point_utf16_to_offset(point_utf16, false), + actual.point_utf16_to_offset_clamped(point_utf16), ix, "point_utf16_to_offset({:?})", point_utf16 @@ -1262,9 +1255,9 @@ mod tests { let left_point = actual.clip_point_utf16(point_utf16, Bias::Left); let right_point = actual.clip_point_utf16(point_utf16, Bias::Right); assert!(right_point >= left_point); - // Ensure translating valid UTF-16 points to offsets doesn't panic. - actual.point_utf16_to_offset(left_point, false); - actual.point_utf16_to_offset(right_point, false); + // Ensure translating UTF-16 points to offsets doesn't panic. + actual.point_utf16_to_offset_clamped(left_point); + actual.point_utf16_to_offset_clamped(right_point); offset_utf16.0 += 1; if unit == b'\n' as u16 { diff --git a/crates/text/src/text.rs b/crates/text/src/text.rs index 42ffe5c6ed..aec46b03cf 100644 --- a/crates/text/src/text.rs +++ b/crates/text/src/text.rs @@ -1590,12 +1590,8 @@ impl BufferSnapshot { self.visible_text.point_to_offset(point) } - pub fn point_utf16_to_offset(&self, point: PointUtf16) -> usize { - self.visible_text.point_utf16_to_offset(point, false) - } - pub fn point_utf16_to_offset_clamped(&self, point: PointUtf16) -> usize { - self.visible_text.point_utf16_to_offset(point, true) + self.visible_text.point_utf16_to_offset_clamped(point) } pub fn point_utf16_to_point(&self, point: PointUtf16) -> Point { @@ -2425,18 +2421,22 @@ impl ToPoint for usize { } } -impl ToPoint for PointUtf16 { - fn to_point<'a>(&self, snapshot: &BufferSnapshot) -> Point { - snapshot.point_utf16_to_point(*self) - } -} - impl ToPoint for Point { fn to_point<'a>(&self, _: &BufferSnapshot) -> Point { *self } } +pub trait ToPointClamped { + fn to_point_clamped(&self, snapshot: &BufferSnapshot) -> Point; +} + +impl ToPointClamped for PointUtf16 { + fn to_point_clamped<'a>(&self, snapshot: &BufferSnapshot) -> Point { + snapshot.point_utf16_to_point(*self) + } +} + pub trait ToPointUtf16 { fn to_point_utf16(&self, snapshot: &BufferSnapshot) -> PointUtf16; }