Clamp UTF-16 to point conversions

This commit is contained in:
Julia 2022-11-16 13:29:26 -05:00
parent bb32599ded
commit 074e3cfbd6
6 changed files with 65 additions and 45 deletions

View file

@ -55,7 +55,7 @@ use link_go_to_definition::{
}; };
pub use multi_buffer::{ pub use multi_buffer::{
Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset, Anchor, AnchorRangeExt, ExcerptId, ExcerptRange, MultiBuffer, MultiBufferSnapshot, ToOffset,
ToPoint, ToOffsetClamped, ToPoint,
}; };
use multi_buffer::{MultiBufferChunks, ToOffsetUtf16}; use multi_buffer::{MultiBufferChunks, ToOffsetUtf16};
use ordered_float::OrderedFloat; use ordered_float::OrderedFloat;

View file

@ -72,6 +72,10 @@ pub trait ToOffset: 'static + fmt::Debug {
fn to_offset(&self, snapshot: &MultiBufferSnapshot) -> usize; 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 { pub trait ToOffsetUtf16: 'static + fmt::Debug {
fn to_offset_utf16(&self, snapshot: &MultiBufferSnapshot) -> OffsetUtf16; 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() { 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)>(); 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)); .offset_to_point_utf16(excerpt.range.context.start.to_offset(&excerpt.buffer));
let buffer_offset = excerpt let buffer_offset = excerpt
.buffer .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) *start_offset + (buffer_offset - excerpt_start_offset)
} else { } else {
self.excerpts.summary().text.len 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 { impl ToOffset for usize {
fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize { fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize {
assert!(*self <= snapshot.len(), "offset is out of range"); 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 { impl ToOffsetUtf16 for OffsetUtf16 {
fn to_offset_utf16(&self, _snapshot: &MultiBufferSnapshot) -> OffsetUtf16 { fn to_offset_utf16(&self, _snapshot: &MultiBufferSnapshot) -> OffsetUtf16 {
*self *self

View file

@ -14,6 +14,7 @@ use util::post_inc;
use crate::{ use crate::{
display_map::{DisplayMap, DisplaySnapshot, ToDisplayPoint}, display_map::{DisplayMap, DisplaySnapshot, ToDisplayPoint},
Anchor, DisplayPoint, ExcerptId, MultiBuffer, MultiBufferSnapshot, SelectMode, ToOffset, Anchor, DisplayPoint, ExcerptId, MultiBuffer, MultiBufferSnapshot, SelectMode, ToOffset,
ToOffsetClamped,
}; };
#[derive(Clone)] #[derive(Clone)]
@ -544,11 +545,33 @@ impl<'a> MutableSelectionsCollection<'a> {
T: ToOffset, T: ToOffset,
{ {
let buffer = self.buffer.read(self.cx).snapshot(self.cx); 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<I, T>(&mut self, ranges: I)
where
I: IntoIterator<Item = Range<T>>,
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<I>(&mut self, ranges: I)
where
I: IntoIterator<Item = Range<usize>>,
{
let selections = ranges let selections = ranges
.into_iter() .into_iter()
.map(|range| { .map(|range| {
let mut start = range.start.to_offset(&buffer); let mut start = range.start;
let mut end = range.end.to_offset(&buffer); let mut end = range.end;
let reversed = if start > end { let reversed = if start > end {
mem::swap(&mut start, &mut end); mem::swap(&mut start, &mut end);
true true

View file

@ -151,7 +151,7 @@ impl ProjectSymbolsView {
let editor = workspace.open_project_item::<Editor>(buffer, cx); let editor = workspace.open_project_item::<Editor>(buffer, cx);
editor.update(cx, |editor, cx| { editor.update(cx, |editor, cx| {
editor.change_selections(Some(Autoscroll::center()), cx, |s| { editor.change_selections(Some(Autoscroll::center()), cx, |s| {
s.select_ranges([position..position]) s.select_clamped_ranges([position..position])
}); });
}); });
}); });

View file

@ -259,7 +259,7 @@ impl Rope {
.map_or(0, |chunk| chunk.point_to_offset(overshoot)) .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() { if point >= self.summary().lines_utf16() {
return self.summary().len; return self.summary().len;
} }
@ -269,7 +269,7 @@ impl Rope {
cursor.start().1 cursor.start().1
+ cursor + cursor
.item() .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 { pub fn point_utf16_to_point(&self, point: PointUtf16) -> Point {
@ -711,7 +711,7 @@ impl Chunk {
point_utf16 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 offset = 0;
let mut point = PointUtf16::new(0, 0); let mut point = PointUtf16::new(0, 0);
for ch in self.0.chars() { for ch in self.0.chars() {
@ -724,26 +724,19 @@ impl Chunk {
point.column = 0; point.column = 0;
if point.row > target.row { if point.row > target.row {
if clamp { //point is beyond the end of the line
//Return the offset up to but not including the newline //Return the offset up to but not including the newline
return offset; return offset;
}
panic!(
"point {:?} is beyond the end of a line with length {}",
target, point.column
);
} }
} else { } else {
point.column += ch.len_utf16() as u32; point.column += ch.len_utf16() as u32;
} }
if point > target { if point > target {
if clamp { //point is inside of a codepoint
//Return the offset before adding the len of the codepoint which //Return the offset before adding the len of the codepoint which
//we have landed within, bias left //we have landed within, bias left
return offset; return offset;
}
panic!("point {:?} is inside of character {:?}", target, ch);
} }
offset += ch.len_utf8(); offset += ch.len_utf8();
@ -1222,7 +1215,7 @@ mod tests {
point point
); );
assert_eq!( assert_eq!(
actual.point_utf16_to_offset(point_utf16, false), actual.point_utf16_to_offset_clamped(point_utf16),
ix, ix,
"point_utf16_to_offset({:?})", "point_utf16_to_offset({:?})",
point_utf16 point_utf16
@ -1262,9 +1255,9 @@ mod tests {
let left_point = actual.clip_point_utf16(point_utf16, Bias::Left); let left_point = actual.clip_point_utf16(point_utf16, Bias::Left);
let right_point = actual.clip_point_utf16(point_utf16, Bias::Right); let right_point = actual.clip_point_utf16(point_utf16, Bias::Right);
assert!(right_point >= left_point); assert!(right_point >= left_point);
// Ensure translating valid UTF-16 points to offsets doesn't panic. // Ensure translating UTF-16 points to offsets doesn't panic.
actual.point_utf16_to_offset(left_point, false); actual.point_utf16_to_offset_clamped(left_point);
actual.point_utf16_to_offset(right_point, false); actual.point_utf16_to_offset_clamped(right_point);
offset_utf16.0 += 1; offset_utf16.0 += 1;
if unit == b'\n' as u16 { if unit == b'\n' as u16 {

View file

@ -1590,12 +1590,8 @@ impl BufferSnapshot {
self.visible_text.point_to_offset(point) 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 { 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 { 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 { impl ToPoint for Point {
fn to_point<'a>(&self, _: &BufferSnapshot) -> Point { fn to_point<'a>(&self, _: &BufferSnapshot) -> Point {
*self *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 { pub trait ToPointUtf16 {
fn to_point_utf16(&self, snapshot: &BufferSnapshot) -> PointUtf16; fn to_point_utf16(&self, snapshot: &BufferSnapshot) -> PointUtf16;
} }