From f9cbed5a1f6566d17ccdead161171f831c682e9e Mon Sep 17 00:00:00 2001 From: Julia Date: Tue, 15 Nov 2022 14:11:52 -0500 Subject: [PATCH 01/14] Clamp UTF-16 coordinate while performing LSP edits rather than panicing --- crates/project/src/project.rs | 2 +- crates/rope/src/rope.rs | 36 +++++++++++++++++++++++------------ crates/text/src/text.rs | 22 ++++++++++++++++++++- 3 files changed, 46 insertions(+), 14 deletions(-) diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index a7124a19698dfb365463a62beced578be12783a7..c4e920db84cd35585be6643bd68f67daaa612ebb 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -5743,7 +5743,7 @@ impl Project { // of any anchors positioned in the unchanged regions. if range.end.row > range.start.row { let mut offset = range.start.to_offset(&snapshot); - let old_text = snapshot.text_for_range(range).collect::(); + let old_text = snapshot.text_for_clamped_range(range).collect::(); let diff = TextDiff::from_lines(old_text.as_str(), &new_text); let mut moved_since_edit = true; diff --git a/crates/rope/src/rope.rs b/crates/rope/src/rope.rs index 8c357801e321034d68eda79bf54188200631ec8e..b6a4930f0b66355057d1e2538a9abb81790fa5de 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) -> usize { + pub fn point_utf16_to_offset(&self, point: PointUtf16, clamp: bool) -> 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)) + .map_or(0, |chunk| chunk.point_utf16_to_offset(overshoot, clamp)) } pub fn point_utf16_to_point(&self, point: PointUtf16) -> Point { @@ -711,29 +711,41 @@ impl Chunk { point_utf16 } - fn point_utf16_to_offset(&self, target: PointUtf16) -> usize { + fn point_utf16_to_offset(&self, target: PointUtf16, clamp: bool) -> usize { let mut offset = 0; let mut point = PointUtf16::new(0, 0); for ch in self.0.chars() { - if point >= target { - if point > target { - panic!("point {:?} is inside of character {:?}", target, ch); - } + if point == target { break; } if ch == '\n' { point.row += 1; + 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.column = 0; } 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); + } + offset += ch.len_utf8(); } offset @@ -1210,7 +1222,7 @@ mod tests { point ); assert_eq!( - actual.point_utf16_to_offset(point_utf16), + actual.point_utf16_to_offset(point_utf16, false), ix, "point_utf16_to_offset({:?})", point_utf16 @@ -1250,9 +1262,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 UTF-16 points to offsets doesn't panic. - actual.point_utf16_to_offset(left_point); - actual.point_utf16_to_offset(right_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); 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 72ae018a16a66848f6bed4435f5e809ee4c39a58..272e425651cd7dca74c57206d3de04f68d9a6d89 100644 --- a/crates/text/src/text.rs +++ b/crates/text/src/text.rs @@ -1591,7 +1591,11 @@ impl BufferSnapshot { } pub fn point_utf16_to_offset(&self, point: PointUtf16) -> usize { - self.visible_text.point_utf16_to_offset(point) + 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) } pub fn point_utf16_to_point(&self, point: PointUtf16) -> Point { @@ -1649,6 +1653,12 @@ impl BufferSnapshot { self.visible_text.chunks_in_range(start..end) } + pub fn text_for_clamped_range(&self, range: Range) -> Chunks<'_> { + let start = range.start.to_offset_clamped(self); + let end = range.end.to_offset_clamped(self); + self.visible_text.chunks_in_range(start..end) + } + pub fn line_len(&self, row: u32) -> u32 { let row_start_offset = Point::new(row, 0).to_offset(self); let row_end_offset = if row >= self.max_point().row { @@ -2390,6 +2400,16 @@ impl<'a, T: ToOffset> ToOffset for &'a T { } } +pub trait ToOffsetClamped { + fn to_offset_clamped(&self, snapshot: &BufferSnapshot) -> usize; +} + +impl ToOffsetClamped for PointUtf16 { + fn to_offset_clamped<'a>(&self, snapshot: &BufferSnapshot) -> usize { + snapshot.point_utf16_to_offset_clamped(*self) + } +} + pub trait ToPoint { fn to_point(&self, snapshot: &BufferSnapshot) -> Point; } From bb32599dedea130074c358dcce95f5458bd746be Mon Sep 17 00:00:00 2001 From: Julia Date: Tue, 15 Nov 2022 15:16:52 -0500 Subject: [PATCH 02/14] Clamp for all UTF-16 to offset conversions which used to use `ToOffset` --- crates/language/src/diagnostic_set.rs | 4 +-- crates/project/src/lsp_command.rs | 36 +++++++++++++++------------ crates/project/src/project.rs | 17 +++++++------ crates/text/src/text.rs | 25 +++++++++---------- 4 files changed, 43 insertions(+), 39 deletions(-) diff --git a/crates/language/src/diagnostic_set.rs b/crates/language/src/diagnostic_set.rs index b52327cac0e81b4ddbf7f224ebef170ac92d3f15..fe2e4c9c212a642c9f8b24e4d5347d5d46e994c2 100644 --- a/crates/language/src/diagnostic_set.rs +++ b/crates/language/src/diagnostic_set.rs @@ -70,8 +70,8 @@ impl DiagnosticSet { Self { diagnostics: SumTree::from_iter( entries.into_iter().map(|entry| DiagnosticEntry { - range: buffer.anchor_before(entry.range.start) - ..buffer.anchor_after(entry.range.end), + range: buffer.clamped_anchor_before(entry.range.start) + ..buffer.clamped_anchor_after(entry.range.end), diagnostic: entry.diagnostic, }), buffer, diff --git a/crates/project/src/lsp_command.rs b/crates/project/src/lsp_command.rs index 37f6e76340ebdc59f623a61bd1cbb9ef4e168b04..78c6b50003605bf65c1e2b3f05b75d2d00ca9e89 100644 --- a/crates/project/src/lsp_command.rs +++ b/crates/project/src/lsp_command.rs @@ -131,7 +131,9 @@ impl LspCommand for PrepareRename { if buffer.clip_point_utf16(start, Bias::Left) == start && buffer.clip_point_utf16(end, Bias::Left) == end { - return Ok(Some(buffer.anchor_after(start)..buffer.anchor_before(end))); + return Ok(Some( + buffer.clamped_anchor_after(start)..buffer.clamped_anchor_before(end), + )); } } Ok(None) @@ -143,7 +145,7 @@ impl LspCommand for PrepareRename { project_id, buffer_id: buffer.remote_id(), position: Some(language::proto::serialize_anchor( - &buffer.anchor_before(self.position), + &buffer.clamped_anchor_before(self.position), )), version: serialize_version(&buffer.version()), } @@ -262,7 +264,7 @@ impl LspCommand for PerformRename { project_id, buffer_id: buffer.remote_id(), position: Some(language::proto::serialize_anchor( - &buffer.anchor_before(self.position), + &buffer.clamped_anchor_before(self.position), )), new_name: self.new_name.clone(), version: serialize_version(&buffer.version()), @@ -360,7 +362,7 @@ impl LspCommand for GetDefinition { project_id, buffer_id: buffer.remote_id(), position: Some(language::proto::serialize_anchor( - &buffer.anchor_before(self.position), + &buffer.clamped_anchor_before(self.position), )), version: serialize_version(&buffer.version()), } @@ -446,7 +448,7 @@ impl LspCommand for GetTypeDefinition { project_id, buffer_id: buffer.remote_id(), position: Some(language::proto::serialize_anchor( - &buffer.anchor_before(self.position), + &buffer.clamped_anchor_before(self.position), )), version: serialize_version(&buffer.version()), } @@ -629,8 +631,8 @@ async fn location_links_from_lsp( origin_buffer.clip_point_utf16(point_from_lsp(origin_range.end), Bias::Left); Location { buffer: buffer.clone(), - range: origin_buffer.anchor_after(origin_start) - ..origin_buffer.anchor_before(origin_end), + range: origin_buffer.clamped_anchor_after(origin_start) + ..origin_buffer.clamped_anchor_before(origin_end), } }); @@ -641,8 +643,8 @@ async fn location_links_from_lsp( target_buffer.clip_point_utf16(point_from_lsp(target_range.end), Bias::Left); let target_location = Location { buffer: target_buffer_handle, - range: target_buffer.anchor_after(target_start) - ..target_buffer.anchor_before(target_end), + range: target_buffer.clamped_anchor_after(target_start) + ..target_buffer.clamped_anchor_before(target_end), }; definitions.push(LocationLink { @@ -741,8 +743,8 @@ impl LspCommand for GetReferences { .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left); references.push(Location { buffer: target_buffer_handle, - range: target_buffer.anchor_after(target_start) - ..target_buffer.anchor_before(target_end), + range: target_buffer.clamped_anchor_after(target_start) + ..target_buffer.clamped_anchor_before(target_end), }); }); } @@ -756,7 +758,7 @@ impl LspCommand for GetReferences { project_id, buffer_id: buffer.remote_id(), position: Some(language::proto::serialize_anchor( - &buffer.anchor_before(self.position), + &buffer.clamped_anchor_before(self.position), )), version: serialize_version(&buffer.version()), } @@ -882,7 +884,8 @@ impl LspCommand for GetDocumentHighlights { let end = buffer .clip_point_utf16(point_from_lsp(lsp_highlight.range.end), Bias::Left); DocumentHighlight { - range: buffer.anchor_after(start)..buffer.anchor_before(end), + range: buffer.clamped_anchor_after(start) + ..buffer.clamped_anchor_before(end), kind: lsp_highlight .kind .unwrap_or(lsp::DocumentHighlightKind::READ), @@ -897,7 +900,7 @@ impl LspCommand for GetDocumentHighlights { project_id, buffer_id: buffer.remote_id(), position: Some(language::proto::serialize_anchor( - &buffer.anchor_before(self.position), + &buffer.clamped_anchor_before(self.position), )), version: serialize_version(&buffer.version()), } @@ -1017,7 +1020,8 @@ impl LspCommand for GetHover { let token_start = buffer.clip_point_utf16(point_from_lsp(range.start), Bias::Left); let token_end = buffer.clip_point_utf16(point_from_lsp(range.end), Bias::Left); - buffer.anchor_after(token_start)..buffer.anchor_before(token_end) + buffer.clamped_anchor_after(token_start) + ..buffer.clamped_anchor_before(token_end) }) }); @@ -1099,7 +1103,7 @@ impl LspCommand for GetHover { project_id, buffer_id: buffer.remote_id(), position: Some(language::proto::serialize_anchor( - &buffer.anchor_before(self.position), + &buffer.clamped_anchor_before(self.position), )), version: serialize_version(&buffer.version), } diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index c4e920db84cd35585be6643bd68f67daaa612ebb..2036ef3cd8bc3729aa95da920a99bc7c6852e8c0 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -25,7 +25,8 @@ use language::{ range_from_lsp, range_to_lsp, Anchor, Bias, Buffer, CachedLspAdapter, CharKind, CodeAction, CodeLabel, Completion, Diagnostic, DiagnosticEntry, DiagnosticSet, Event as BufferEvent, File as _, Language, LanguageRegistry, LanguageServerName, LocalFile, OffsetRangeExt, - Operation, Patch, PointUtf16, TextBufferSnapshot, ToOffset, ToPointUtf16, Transaction, + Operation, Patch, PointUtf16, TextBufferSnapshot, ToOffset, ToOffsetClamped, ToPointUtf16, + Transaction, }; use lsp::{ DiagnosticSeverity, DiagnosticTag, DocumentHighlightKind, LanguageServer, LanguageString, @@ -3289,7 +3290,7 @@ impl Project { }; let position = position.to_point_utf16(source_buffer); - let anchor = source_buffer.anchor_after(position); + let anchor = source_buffer.clamped_anchor_after(position); if worktree.read(cx).as_local().is_some() { let buffer_abs_path = buffer_abs_path.unwrap(); @@ -3355,7 +3356,7 @@ impl Project { return None; } ( - snapshot.anchor_before(start)..snapshot.anchor_after(end), + snapshot.clamped_anchor_before(start)..snapshot.clamped_anchor_after(end), edit.new_text.clone(), ) } @@ -3368,7 +3369,7 @@ impl Project { } let Range { start, end } = range_for_token .get_or_insert_with(|| { - let offset = position.to_offset(&snapshot); + let offset = position.to_offset_clamped(&snapshot); let (range, kind) = snapshot.surrounding_word(offset); if kind == Some(CharKind::Word) { range @@ -5742,7 +5743,7 @@ impl Project { // we can identify the changes more precisely, preserving the locations // of any anchors positioned in the unchanged regions. if range.end.row > range.start.row { - let mut offset = range.start.to_offset(&snapshot); + let mut offset = range.start.to_offset_clamped(&snapshot); let old_text = snapshot.text_for_clamped_range(range).collect::(); let diff = TextDiff::from_lines(old_text.as_str(), &new_text); @@ -5778,11 +5779,11 @@ impl Project { } } } else if range.end == range.start { - let anchor = snapshot.anchor_after(range.start); + let anchor = snapshot.clamped_anchor_after(range.start); edits.push((anchor..anchor, new_text)); } else { - let edit_start = snapshot.anchor_after(range.start); - let edit_end = snapshot.anchor_before(range.end); + let edit_start = snapshot.clamped_anchor_after(range.start); + let edit_end = snapshot.clamped_anchor_before(range.end); edits.push((edit_start..edit_end, new_text)); } } diff --git a/crates/text/src/text.rs b/crates/text/src/text.rs index 272e425651cd7dca74c57206d3de04f68d9a6d89..42ffe5c6edc266283f0e54590f5c7dc6b1c877fa 100644 --- a/crates/text/src/text.rs +++ b/crates/text/src/text.rs @@ -1808,12 +1808,23 @@ impl BufferSnapshot { self.anchor_at(position, Bias::Left) } + pub fn clamped_anchor_before(&self, position: T) -> Anchor { + self.anchor_at_offset(position.to_offset_clamped(self), Bias::Left) + } + pub fn anchor_after(&self, position: T) -> Anchor { self.anchor_at(position, Bias::Right) } + pub fn clamped_anchor_after(&self, position: T) -> Anchor { + self.anchor_at_offset(position.to_offset_clamped(self), Bias::Right) + } + pub fn anchor_at(&self, position: T, bias: Bias) -> Anchor { - let offset = position.to_offset(self); + self.anchor_at_offset(position.to_offset(self), bias) + } + + fn anchor_at_offset(&self, offset: usize, bias: Bias) -> Anchor { if bias == Bias::Left && offset == 0 { Anchor::MIN } else if bias == Bias::Right && offset == self.len() { @@ -2369,12 +2380,6 @@ impl ToOffset for Point { } } -impl ToOffset for PointUtf16 { - fn to_offset<'a>(&self, snapshot: &BufferSnapshot) -> usize { - snapshot.point_utf16_to_offset(*self) - } -} - impl ToOffset for usize { fn to_offset<'a>(&self, snapshot: &BufferSnapshot) -> usize { assert!(*self <= snapshot.len(), "offset {self} is out of range"); @@ -2382,12 +2387,6 @@ impl ToOffset for usize { } } -impl ToOffset for OffsetUtf16 { - fn to_offset<'a>(&self, snapshot: &BufferSnapshot) -> usize { - snapshot.offset_utf16_to_offset(*self) - } -} - impl ToOffset for Anchor { fn to_offset<'a>(&self, snapshot: &BufferSnapshot) -> usize { snapshot.summary_for_anchor(self) From 074e3cfbd68a5429e244b204773486959845a90d Mon Sep 17 00:00:00 2001 From: Julia Date: Wed, 16 Nov 2022 13:29:26 -0500 Subject: [PATCH 03/14] 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 dd5934f9794c4d65300db90bd3aedc7e69e9429f..7206dbb199f9123503a69bda3202357b9208bb78 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 38475daf28f75a74278d36f5e4d2c16e006a1b05..70645bbfe00fd68f2d8b19e4b9e19d40b904e1ae 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 256405f20eeeecb59a34fa581e6a5bd8f33854e9..01e38d269eeeb88d35fd07ce1fad110991fb1d2f 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 273230fe26feb5f01cc1cdcbacd7c321d689a446..60623e99caf8bc95f6488915586e288dae3b8c99 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 b6a4930f0b66355057d1e2538a9abb81790fa5de..83e9c96ca968b273a1ac749c611ac897c427f3d2 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 42ffe5c6edc266283f0e54590f5c7dc6b1c877fa..aec46b03cfc152a70903654fd06ffb40112356d7 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; } From 4ead1ecbbffcabef97ddba9327818ee0cd27b960 Mon Sep 17 00:00:00 2001 From: Julia Date: Wed, 16 Nov 2022 14:27:19 -0500 Subject: [PATCH 04/14] Simply logic of this method Co-Authored-By: Max Brunsfeld --- crates/rope/src/rope.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/crates/rope/src/rope.rs b/crates/rope/src/rope.rs index 83e9c96ca968b273a1ac749c611ac897c427f3d2..ca1f35bb13ec47d9208363595c38b812b405332a 100644 --- a/crates/rope/src/rope.rs +++ b/crates/rope/src/rope.rs @@ -714,6 +714,7 @@ impl Chunk { 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() { if point == target { break; @@ -722,25 +723,19 @@ impl Chunk { if ch == '\n' { point.row += 1; point.column = 0; - - if point.row > target.row { - //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 { - //point is inside of a codepoint - //Return the offset before adding the len of the codepoint which - //we have landed within, bias left + // If the point is past the end of a line or inside of a code point, + // return the last valid offset before the point. return offset; } offset += ch.len_utf8(); } + offset } From 436c89650a14ce2063dffecefafc74c6675d1e29 Mon Sep 17 00:00:00 2001 From: Julia Date: Wed, 16 Nov 2022 15:12:52 -0500 Subject: [PATCH 05/14] Rename clamped -> clipped --- crates/editor/src/editor.rs | 2 +- crates/editor/src/multi_buffer.rs | 16 +++++------ crates/editor/src/selections_collection.rs | 8 +++--- crates/project/src/project.rs | 6 ++-- crates/project_symbols/src/project_symbols.rs | 2 +- crates/rope/src/rope.rs | 12 ++++---- crates/text/src/text.rs | 28 +++++++++---------- 7 files changed, 37 insertions(+), 37 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 7206dbb199f9123503a69bda3202357b9208bb78..4575e9ce5e9c1e0bdf603bc0fae2429af56ffa93 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, - ToOffsetClamped, ToPoint, + ToOffsetClipped, 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 70645bbfe00fd68f2d8b19e4b9e19d40b904e1ae..aa25b476803418f6c0ad8b5a2ae4bb04c96a4d3e 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -72,8 +72,8 @@ 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 ToOffsetClipped: 'static + fmt::Debug { + fn to_offset_clipped(&self, snapshot: &MultiBufferSnapshot) -> usize; } pub trait ToOffsetUtf16: 'static + fmt::Debug { @@ -1949,9 +1949,9 @@ impl MultiBufferSnapshot { } } - pub fn point_utf16_to_offset_clamped(&self, point: PointUtf16) -> usize { + pub fn point_utf16_to_offset_clipped(&self, point: PointUtf16) -> usize { if let Some((_, _, buffer)) = self.as_singleton() { - return buffer.point_utf16_to_offset_clamped(point); + return buffer.point_utf16_to_offset_clipped(point); } let mut cursor = self.excerpts.cursor::<(PointUtf16, usize)>(); @@ -1965,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_clamped(excerpt_start_point + overshoot); + .point_utf16_to_offset_clipped(excerpt_start_point + overshoot); *start_offset + (buffer_offset - excerpt_start_offset) } else { self.excerpts.summary().text.len @@ -3291,9 +3291,9 @@ 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 ToOffsetClipped for PointUtf16 { + fn to_offset_clipped<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize { + snapshot.point_utf16_to_offset_clipped(*self) } } diff --git a/crates/editor/src/selections_collection.rs b/crates/editor/src/selections_collection.rs index 01e38d269eeeb88d35fd07ce1fad110991fb1d2f..14026d9f4e86ce93ac3c40c710a614420d55ca37 100644 --- a/crates/editor/src/selections_collection.rs +++ b/crates/editor/src/selections_collection.rs @@ -14,7 +14,7 @@ use util::post_inc; use crate::{ display_map::{DisplayMap, DisplaySnapshot, ToDisplayPoint}, Anchor, DisplayPoint, ExcerptId, MultiBuffer, MultiBufferSnapshot, SelectMode, ToOffset, - ToOffsetClamped, + ToOffsetClipped, }; #[derive(Clone)] @@ -551,14 +551,14 @@ impl<'a> MutableSelectionsCollection<'a> { self.select_offset_ranges(ranges); } - pub fn select_clamped_ranges(&mut self, ranges: I) + pub fn select_clipped_ranges(&mut self, ranges: I) where I: IntoIterator>, - T: ToOffsetClamped, + T: ToOffsetClipped, { 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) + range.start.to_offset_clipped(&buffer)..range.end.to_offset_clipped(&buffer) }); self.select_offset_ranges(ranges); } diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 2036ef3cd8bc3729aa95da920a99bc7c6852e8c0..e1339ba434c3a80d44da7b97c85693dc25249e23 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -25,7 +25,7 @@ use language::{ range_from_lsp, range_to_lsp, Anchor, Bias, Buffer, CachedLspAdapter, CharKind, CodeAction, CodeLabel, Completion, Diagnostic, DiagnosticEntry, DiagnosticSet, Event as BufferEvent, File as _, Language, LanguageRegistry, LanguageServerName, LocalFile, OffsetRangeExt, - Operation, Patch, PointUtf16, TextBufferSnapshot, ToOffset, ToOffsetClamped, ToPointUtf16, + Operation, Patch, PointUtf16, TextBufferSnapshot, ToOffset, ToOffsetClipped, ToPointUtf16, Transaction, }; use lsp::{ @@ -3369,7 +3369,7 @@ impl Project { } let Range { start, end } = range_for_token .get_or_insert_with(|| { - let offset = position.to_offset_clamped(&snapshot); + let offset = position.to_offset_clipped(&snapshot); let (range, kind) = snapshot.surrounding_word(offset); if kind == Some(CharKind::Word) { range @@ -5743,7 +5743,7 @@ impl Project { // we can identify the changes more precisely, preserving the locations // of any anchors positioned in the unchanged regions. if range.end.row > range.start.row { - let mut offset = range.start.to_offset_clamped(&snapshot); + let mut offset = range.start.to_offset_clipped(&snapshot); let old_text = snapshot.text_for_clamped_range(range).collect::(); let diff = TextDiff::from_lines(old_text.as_str(), &new_text); diff --git a/crates/project_symbols/src/project_symbols.rs b/crates/project_symbols/src/project_symbols.rs index 60623e99caf8bc95f6488915586e288dae3b8c99..eb755d2d2f360a2691a447c4549c4d83de4150ba 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_clamped_ranges([position..position]) + s.select_clipped_ranges([position..position]) }); }); }); diff --git a/crates/rope/src/rope.rs b/crates/rope/src/rope.rs index ca1f35bb13ec47d9208363595c38b812b405332a..9b52815ae34db77934952c5717d8645194977f3f 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_clamped(&self, point: PointUtf16) -> usize { + pub fn point_utf16_to_offset_clipped(&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_clamped(overshoot)) + .map_or(0, |chunk| chunk.point_utf16_to_offset_clipped(overshoot)) } pub fn point_utf16_to_point(&self, point: PointUtf16) -> Point { @@ -711,7 +711,7 @@ impl Chunk { point_utf16 } - fn point_utf16_to_offset_clamped(&self, target: PointUtf16) -> usize { + fn point_utf16_to_offset_clipped(&self, target: PointUtf16) -> usize { let mut offset = 0; let mut point = PointUtf16::new(0, 0); @@ -1210,7 +1210,7 @@ mod tests { point ); assert_eq!( - actual.point_utf16_to_offset_clamped(point_utf16), + actual.point_utf16_to_offset_clipped(point_utf16), ix, "point_utf16_to_offset({:?})", point_utf16 @@ -1251,8 +1251,8 @@ mod tests { let right_point = actual.clip_point_utf16(point_utf16, Bias::Right); assert!(right_point >= left_point); // 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); + actual.point_utf16_to_offset_clipped(left_point); + actual.point_utf16_to_offset_clipped(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 aec46b03cfc152a70903654fd06ffb40112356d7..8cdf087b53c538aabafa0581a2d31d63d4a56846 100644 --- a/crates/text/src/text.rs +++ b/crates/text/src/text.rs @@ -1590,8 +1590,8 @@ impl BufferSnapshot { self.visible_text.point_to_offset(point) } - pub fn point_utf16_to_offset_clamped(&self, point: PointUtf16) -> usize { - self.visible_text.point_utf16_to_offset_clamped(point) + pub fn point_utf16_to_offset_clipped(&self, point: PointUtf16) -> usize { + self.visible_text.point_utf16_to_offset_clipped(point) } pub fn point_utf16_to_point(&self, point: PointUtf16) -> Point { @@ -1649,9 +1649,9 @@ impl BufferSnapshot { self.visible_text.chunks_in_range(start..end) } - pub fn text_for_clamped_range(&self, range: Range) -> Chunks<'_> { - let start = range.start.to_offset_clamped(self); - let end = range.end.to_offset_clamped(self); + pub fn text_for_clamped_range(&self, range: Range) -> Chunks<'_> { + let start = range.start.to_offset_clipped(self); + let end = range.end.to_offset_clipped(self); self.visible_text.chunks_in_range(start..end) } @@ -1804,16 +1804,16 @@ impl BufferSnapshot { self.anchor_at(position, Bias::Left) } - pub fn clamped_anchor_before(&self, position: T) -> Anchor { - self.anchor_at_offset(position.to_offset_clamped(self), Bias::Left) + pub fn clamped_anchor_before(&self, position: T) -> Anchor { + self.anchor_at_offset(position.to_offset_clipped(self), Bias::Left) } pub fn anchor_after(&self, position: T) -> Anchor { self.anchor_at(position, Bias::Right) } - pub fn clamped_anchor_after(&self, position: T) -> Anchor { - self.anchor_at_offset(position.to_offset_clamped(self), Bias::Right) + pub fn clamped_anchor_after(&self, position: T) -> Anchor { + self.anchor_at_offset(position.to_offset_clipped(self), Bias::Right) } pub fn anchor_at(&self, position: T, bias: Bias) -> Anchor { @@ -2395,13 +2395,13 @@ impl<'a, T: ToOffset> ToOffset for &'a T { } } -pub trait ToOffsetClamped { - fn to_offset_clamped(&self, snapshot: &BufferSnapshot) -> usize; +pub trait ToOffsetClipped { + fn to_offset_clipped(&self, snapshot: &BufferSnapshot) -> usize; } -impl ToOffsetClamped for PointUtf16 { - fn to_offset_clamped<'a>(&self, snapshot: &BufferSnapshot) -> usize { - snapshot.point_utf16_to_offset_clamped(*self) +impl ToOffsetClipped for PointUtf16 { + fn to_offset_clipped<'a>(&self, snapshot: &BufferSnapshot) -> usize { + snapshot.point_utf16_to_offset_clipped(*self) } } From 1c84e77c3773dd5c07b2d8e77d3278a9b4ba0916 Mon Sep 17 00:00:00 2001 From: Julia Date: Wed, 16 Nov 2022 22:20:16 -0500 Subject: [PATCH 06/14] Start adding concept of `Unclipped` text coordinates Co-Authored-By: Max Brunsfeld --- crates/language/src/diagnostic_set.rs | 8 +-- crates/language/src/language.rs | 6 +- crates/project/src/lsp_command.rs | 40 ++++++------ crates/project/src/project.rs | 26 ++++---- crates/project/src/worktree.rs | 7 ++- crates/rope/src/rope.rs | 90 ++++++++++++++++++--------- crates/text/src/text.rs | 86 +++++++++++-------------- 7 files changed, 139 insertions(+), 124 deletions(-) diff --git a/crates/language/src/diagnostic_set.rs b/crates/language/src/diagnostic_set.rs index fe2e4c9c212a642c9f8b24e4d5347d5d46e994c2..a4d6dc12c731ec44a60f2fb8078e86355f283d18 100644 --- a/crates/language/src/diagnostic_set.rs +++ b/crates/language/src/diagnostic_set.rs @@ -6,7 +6,7 @@ use std::{ ops::Range, }; use sum_tree::{self, Bias, SumTree}; -use text::{Anchor, FromAnchor, PointUtf16, ToOffset}; +use text::{Anchor, FromAnchor, PointUtf16, ToOffset, Unclipped}; #[derive(Clone, Debug, Default)] pub struct DiagnosticSet { @@ -63,15 +63,15 @@ impl DiagnosticSet { pub fn new(iter: I, buffer: &text::BufferSnapshot) -> Self where - I: IntoIterator>, + I: IntoIterator>>, { let mut entries = iter.into_iter().collect::>(); entries.sort_unstable_by_key(|entry| (entry.range.start, Reverse(entry.range.end))); Self { diagnostics: SumTree::from_iter( entries.into_iter().map(|entry| DiagnosticEntry { - range: buffer.clamped_anchor_before(entry.range.start) - ..buffer.clamped_anchor_after(entry.range.end), + range: buffer.anchor_before(entry.range.start) + ..buffer.anchor_before(entry.range.end), diagnostic: entry.diagnostic, }), buffer, diff --git a/crates/language/src/language.rs b/crates/language/src/language.rs index 47d724866d7ff91c62ecaa55644d8d4cbf62337a..c3f2c3716b43c208408f67218f191354fcbe0c7b 100644 --- a/crates/language/src/language.rs +++ b/crates/language/src/language.rs @@ -1053,8 +1053,8 @@ pub fn point_to_lsp(point: PointUtf16) -> lsp::Position { lsp::Position::new(point.row, point.column) } -pub fn point_from_lsp(point: lsp::Position) -> PointUtf16 { - PointUtf16::new(point.line, point.character) +pub fn point_from_lsp(point: lsp::Position) -> Unclipped { + Unclipped(PointUtf16::new(point.line, point.character)) } pub fn range_to_lsp(range: Range) -> lsp::Range { @@ -1064,7 +1064,7 @@ pub fn range_to_lsp(range: Range) -> lsp::Range { } } -pub fn range_from_lsp(range: lsp::Range) -> Range { +pub fn range_from_lsp(range: lsp::Range) -> Range> { let mut start = point_from_lsp(range.start); let mut end = point_from_lsp(range.end); if start > end { diff --git a/crates/project/src/lsp_command.rs b/crates/project/src/lsp_command.rs index 78c6b50003605bf65c1e2b3f05b75d2d00ca9e89..ae6d18a9a90f6fc34cafc4979de42e7bec293213 100644 --- a/crates/project/src/lsp_command.rs +++ b/crates/project/src/lsp_command.rs @@ -128,11 +128,11 @@ impl LspCommand for PrepareRename { ) = message { let Range { start, end } = range_from_lsp(range); - if buffer.clip_point_utf16(start, Bias::Left) == start - && buffer.clip_point_utf16(end, Bias::Left) == end + if buffer.clip_point_utf16(start, Bias::Left) == start.0 + && buffer.clip_point_utf16(end, Bias::Left) == end.0 { return Ok(Some( - buffer.clamped_anchor_after(start)..buffer.clamped_anchor_before(end), + buffer.anchor_after(start)..buffer.anchor_before(end), )); } } @@ -145,7 +145,7 @@ impl LspCommand for PrepareRename { project_id, buffer_id: buffer.remote_id(), position: Some(language::proto::serialize_anchor( - &buffer.clamped_anchor_before(self.position), + &buffer.anchor_before(self.position), )), version: serialize_version(&buffer.version()), } @@ -264,7 +264,7 @@ impl LspCommand for PerformRename { project_id, buffer_id: buffer.remote_id(), position: Some(language::proto::serialize_anchor( - &buffer.clamped_anchor_before(self.position), + &buffer.anchor_before(self.position), )), new_name: self.new_name.clone(), version: serialize_version(&buffer.version()), @@ -362,7 +362,7 @@ impl LspCommand for GetDefinition { project_id, buffer_id: buffer.remote_id(), position: Some(language::proto::serialize_anchor( - &buffer.clamped_anchor_before(self.position), + &buffer.anchor_before(self.position), )), version: serialize_version(&buffer.version()), } @@ -448,7 +448,7 @@ impl LspCommand for GetTypeDefinition { project_id, buffer_id: buffer.remote_id(), position: Some(language::proto::serialize_anchor( - &buffer.clamped_anchor_before(self.position), + &buffer.anchor_before(self.position), )), version: serialize_version(&buffer.version()), } @@ -631,8 +631,8 @@ async fn location_links_from_lsp( origin_buffer.clip_point_utf16(point_from_lsp(origin_range.end), Bias::Left); Location { buffer: buffer.clone(), - range: origin_buffer.clamped_anchor_after(origin_start) - ..origin_buffer.clamped_anchor_before(origin_end), + range: origin_buffer.anchor_after(origin_start) + ..origin_buffer.anchor_before(origin_end), } }); @@ -643,8 +643,8 @@ async fn location_links_from_lsp( target_buffer.clip_point_utf16(point_from_lsp(target_range.end), Bias::Left); let target_location = Location { buffer: target_buffer_handle, - range: target_buffer.clamped_anchor_after(target_start) - ..target_buffer.clamped_anchor_before(target_end), + range: target_buffer.anchor_after(target_start) + ..target_buffer.anchor_before(target_end), }; definitions.push(LocationLink { @@ -743,8 +743,8 @@ impl LspCommand for GetReferences { .clip_point_utf16(point_from_lsp(lsp_location.range.end), Bias::Left); references.push(Location { buffer: target_buffer_handle, - range: target_buffer.clamped_anchor_after(target_start) - ..target_buffer.clamped_anchor_before(target_end), + range: target_buffer.anchor_after(target_start) + ..target_buffer.anchor_before(target_end), }); }); } @@ -758,7 +758,7 @@ impl LspCommand for GetReferences { project_id, buffer_id: buffer.remote_id(), position: Some(language::proto::serialize_anchor( - &buffer.clamped_anchor_before(self.position), + &buffer.anchor_before(self.position), )), version: serialize_version(&buffer.version()), } @@ -884,8 +884,8 @@ impl LspCommand for GetDocumentHighlights { let end = buffer .clip_point_utf16(point_from_lsp(lsp_highlight.range.end), Bias::Left); DocumentHighlight { - range: buffer.clamped_anchor_after(start) - ..buffer.clamped_anchor_before(end), + range: buffer.anchor_after(start) + ..buffer.anchor_before(end), kind: lsp_highlight .kind .unwrap_or(lsp::DocumentHighlightKind::READ), @@ -900,7 +900,7 @@ impl LspCommand for GetDocumentHighlights { project_id, buffer_id: buffer.remote_id(), position: Some(language::proto::serialize_anchor( - &buffer.clamped_anchor_before(self.position), + &buffer.anchor_before(self.position), )), version: serialize_version(&buffer.version()), } @@ -1020,8 +1020,8 @@ impl LspCommand for GetHover { let token_start = buffer.clip_point_utf16(point_from_lsp(range.start), Bias::Left); let token_end = buffer.clip_point_utf16(point_from_lsp(range.end), Bias::Left); - buffer.clamped_anchor_after(token_start) - ..buffer.clamped_anchor_before(token_end) + buffer.anchor_after(token_start) + ..buffer.anchor_before(token_end) }) }); @@ -1103,7 +1103,7 @@ impl LspCommand for GetHover { project_id, buffer_id: buffer.remote_id(), position: Some(language::proto::serialize_anchor( - &buffer.clamped_anchor_before(self.position), + &buffer.anchor_before(self.position), )), version: serialize_version(&buffer.version), } diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index e1339ba434c3a80d44da7b97c85693dc25249e23..e1e839db1fc851518604649dfd737723c1720a41 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -25,8 +25,8 @@ use language::{ range_from_lsp, range_to_lsp, Anchor, Bias, Buffer, CachedLspAdapter, CharKind, CodeAction, CodeLabel, Completion, Diagnostic, DiagnosticEntry, DiagnosticSet, Event as BufferEvent, File as _, Language, LanguageRegistry, LanguageServerName, LocalFile, OffsetRangeExt, - Operation, Patch, PointUtf16, TextBufferSnapshot, ToOffset, ToOffsetClipped, ToPointUtf16, - Transaction, + Operation, Patch, PointUtf16, TextBufferSnapshot, ToOffset, ToPointUtf16, + Transaction, Unclipped, }; use lsp::{ DiagnosticSeverity, DiagnosticTag, DocumentHighlightKind, LanguageServer, LanguageString, @@ -2598,7 +2598,7 @@ impl Project { language_server_id: usize, abs_path: PathBuf, version: Option, - diagnostics: Vec>, + diagnostics: Vec>>, cx: &mut ModelContext, ) -> Result<(), anyhow::Error> { let (worktree, relative_path) = self @@ -2636,7 +2636,7 @@ impl Project { fn update_buffer_diagnostics( &mut self, buffer: &ModelHandle, - mut diagnostics: Vec>, + mut diagnostics: Vec>>, version: Option, cx: &mut ModelContext, ) -> Result<()> { @@ -2677,16 +2677,14 @@ impl Project { end = entry.range.end; } - let mut range = snapshot.clip_point_utf16(start, Bias::Left) - ..snapshot.clip_point_utf16(end, Bias::Right); + let mut range = start..end; - // Expand empty ranges by one character + // Expand empty ranges by one codepoint if range.start == range.end { + // This will be go to the next boundary when being clipped range.end.column += 1; - range.end = snapshot.clip_point_utf16(range.end, Bias::Right); if range.start == range.end && range.end.column > 0 { range.start.column -= 1; - range.start = snapshot.clip_point_utf16(range.start, Bias::Left); } } @@ -3290,7 +3288,7 @@ impl Project { }; let position = position.to_point_utf16(source_buffer); - let anchor = source_buffer.clamped_anchor_after(position); + let anchor = source_buffer.anchor_after(position); if worktree.read(cx).as_local().is_some() { let buffer_abs_path = buffer_abs_path.unwrap(); @@ -3356,7 +3354,7 @@ impl Project { return None; } ( - snapshot.clamped_anchor_before(start)..snapshot.clamped_anchor_after(end), + snapshot.anchor_before(start)..snapshot.anchor_after(end), edit.new_text.clone(), ) } @@ -5779,11 +5777,11 @@ impl Project { } } } else if range.end == range.start { - let anchor = snapshot.clamped_anchor_after(range.start); + let anchor = snapshot.anchor_after(range.start); edits.push((anchor..anchor, new_text)); } else { - let edit_start = snapshot.clamped_anchor_after(range.start); - let edit_end = snapshot.clamped_anchor_before(range.end); + let edit_start = snapshot.anchor_after(range.start); + let edit_end = snapshot.anchor_before(range.end); edits.push((edit_start..edit_end, new_text)); } } diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 055aa0670631f04caf269eccb7c343b31114f92e..795143c3e09f747726a8d5df9a36223f9a662a52 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -20,6 +20,7 @@ use gpui::{ executor, AppContext, AsyncAppContext, Entity, ModelContext, ModelHandle, MutableAppContext, Task, }; +use language::Unclipped; use language::{ proto::{deserialize_version, serialize_line_ending, serialize_version}, Buffer, DiagnosticEntry, PointUtf16, Rope, @@ -65,7 +66,7 @@ pub struct LocalWorktree { _background_scanner_task: Option>, poll_task: Option>, share: Option, - diagnostics: HashMap, Vec>>, + diagnostics: HashMap, Vec>>>, diagnostic_summaries: TreeMap, client: Arc, fs: Arc, @@ -499,7 +500,7 @@ impl LocalWorktree { }) } - pub fn diagnostics_for_path(&self, path: &Path) -> Option>> { + pub fn diagnostics_for_path(&self, path: &Path) -> Option>>> { self.diagnostics.get(path).cloned() } @@ -507,7 +508,7 @@ impl LocalWorktree { &mut self, language_server_id: usize, worktree_path: Arc, - diagnostics: Vec>, + diagnostics: Vec>>, _: &mut ModelContext, ) -> Result { self.diagnostics.remove(&worktree_path); diff --git a/crates/rope/src/rope.rs b/crates/rope/src/rope.rs index 9b52815ae34db77934952c5717d8645194977f3f..27f0b8cdb414413b140067d26ec992977edaa79c 100644 --- a/crates/rope/src/rope.rs +++ b/crates/rope/src/rope.rs @@ -12,6 +12,9 @@ pub use offset_utf16::OffsetUtf16; pub use point::Point; pub use point_utf16::PointUtf16; +#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Unclipped(pub T); + #[cfg(test)] const CHUNK_BASE: usize = 6; @@ -259,7 +262,15 @@ impl Rope { .map_or(0, |chunk| chunk.point_to_offset(overshoot)) } - pub fn point_utf16_to_offset_clipped(&self, point: PointUtf16) -> usize { + pub fn point_utf16_to_offset(&self, point: PointUtf16) -> usize { + self.point_utf16_to_offset_impl(point, false) + } + + pub fn unclipped_point_utf16_to_offset(&self, point: Unclipped) -> usize { + self.point_utf16_to_offset_impl(point.0, true) + } + + fn point_utf16_to_offset_impl(&self, point: PointUtf16, clip: bool) -> usize { if point >= self.summary().lines_utf16() { return self.summary().len; } @@ -269,10 +280,10 @@ impl Rope { cursor.start().1 + cursor .item() - .map_or(0, |chunk| chunk.point_utf16_to_offset_clipped(overshoot)) + .map_or(0, |chunk| chunk.point_utf16_to_offset(overshoot, clip)) } - pub fn point_utf16_to_point(&self, point: PointUtf16) -> Point { + pub fn point_utf16_to_point_clipped(&self, point: PointUtf16) -> Point { if point >= self.summary().lines_utf16() { return self.summary().lines; } @@ -280,9 +291,9 @@ impl Rope { cursor.seek(&point, Bias::Left, &()); let overshoot = point - cursor.start().0; cursor.start().1 - + cursor - .item() - .map_or(Point::zero(), |chunk| chunk.point_utf16_to_point(overshoot)) + + cursor.item().map_or(Point::zero(), |chunk| { + chunk.point_utf16_to_point_clipped(overshoot) + }) } pub fn clip_offset(&self, mut offset: usize, bias: Bias) -> usize { @@ -330,11 +341,11 @@ impl Rope { } } - pub fn clip_point_utf16(&self, point: PointUtf16, bias: Bias) -> PointUtf16 { + pub fn clip_point_utf16(&self, point: Unclipped, bias: Bias) -> PointUtf16 { let mut cursor = self.chunks.cursor::(); - cursor.seek(&point, Bias::Right, &()); + cursor.seek(&point.0, Bias::Right, &()); if let Some(chunk) = cursor.item() { - let overshoot = point - cursor.start(); + let overshoot = Unclipped(point.0 - cursor.start()); *cursor.start() + chunk.clip_point_utf16(overshoot, bias) } else { self.summary().lines_utf16() @@ -711,7 +722,7 @@ impl Chunk { point_utf16 } - fn point_utf16_to_offset_clipped(&self, target: PointUtf16) -> usize { + fn point_utf16_to_offset(&self, target: PointUtf16, clip: bool) -> usize { let mut offset = 0; let mut point = PointUtf16::new(0, 0); @@ -723,14 +734,26 @@ impl Chunk { if ch == '\n' { point.row += 1; point.column = 0; + if point.row > target.row { + if clip { + // Return the offset of the newline + return offset; + } + panic!( + "point {:?} is beyond the end of a line with length {}", + target, point.column + ); + } } else { point.column += ch.len_utf16() as u32; } if point > target { - // If the point is past the end of a line or inside of a code point, - // return the last valid offset before the point. - return offset; + if clip { + // Return the offset of the codepoint which we have landed within, bias left + return offset; + } + panic!("point {:?} is inside of codepoint {:?}", target, ch); } offset += ch.len_utf8(); @@ -739,17 +762,21 @@ impl Chunk { offset } - fn point_utf16_to_point(&self, target: PointUtf16) -> Point { + fn point_utf16_to_point_clipped(&self, target: PointUtf16) -> Point { let mut point = Point::zero(); let mut point_utf16 = PointUtf16::zero(); + for ch in self.0.chars() { - if point_utf16 >= target { - if point_utf16 > target { - panic!("point {:?} is inside of character {:?}", target, ch); - } + if point_utf16 == target { break; } + if point_utf16 > target { + // If the point is past the end of a line or inside of a code point, + // return the last valid point before the target. + return point; + } + if ch == '\n' { point_utf16 += PointUtf16::new(1, 0); point += Point::new(1, 0); @@ -758,6 +785,7 @@ impl Chunk { point += Point::new(0, ch.len_utf8() as u32); } } + point } @@ -777,11 +805,11 @@ impl Chunk { unreachable!() } - fn clip_point_utf16(&self, target: PointUtf16, bias: Bias) -> PointUtf16 { + fn clip_point_utf16(&self, target: Unclipped, bias: Bias) -> PointUtf16 { for (row, line) in self.0.split('\n').enumerate() { - if row == target.row as usize { + if row == target.0.row as usize { let mut code_units = line.encode_utf16(); - let mut column = code_units.by_ref().take(target.column as usize).count(); + let mut column = code_units.by_ref().take(target.0.column as usize).count(); if char::decode_utf16(code_units).next().transpose().is_err() { match bias { Bias::Left => column -= 1, @@ -1114,15 +1142,15 @@ mod tests { ); assert_eq!( - rope.clip_point_utf16(PointUtf16::new(0, 1), Bias::Left), + rope.clip_point_utf16(Unclipped(PointUtf16::new(0, 1)), Bias::Left), PointUtf16::new(0, 0) ); assert_eq!( - rope.clip_point_utf16(PointUtf16::new(0, 1), Bias::Right), + rope.clip_point_utf16(Unclipped(PointUtf16::new(0, 1)), Bias::Right), PointUtf16::new(0, 2) ); assert_eq!( - rope.clip_point_utf16(PointUtf16::new(0, 3), Bias::Right), + rope.clip_point_utf16(Unclipped(PointUtf16::new(0, 3)), Bias::Right), PointUtf16::new(0, 2) ); @@ -1210,7 +1238,7 @@ mod tests { point ); assert_eq!( - actual.point_utf16_to_offset_clipped(point_utf16), + actual.point_utf16_to_offset(point_utf16), ix, "point_utf16_to_offset({:?})", point_utf16 @@ -1238,7 +1266,7 @@ mod tests { } let mut offset_utf16 = OffsetUtf16(0); - let mut point_utf16 = PointUtf16::zero(); + let mut point_utf16 = Unclipped(PointUtf16::zero()); for unit in expected.encode_utf16() { let left_offset = actual.clip_offset_utf16(offset_utf16, Bias::Left); let right_offset = actual.clip_offset_utf16(offset_utf16, Bias::Right); @@ -1250,15 +1278,15 @@ 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 UTF-16 points to offsets doesn't panic. - actual.point_utf16_to_offset_clipped(left_point); - actual.point_utf16_to_offset_clipped(right_point); + // Ensure translating valid UTF-16 points to offsets doesn't panic. + actual.point_utf16_to_offset(left_point); + actual.point_utf16_to_offset(right_point); offset_utf16.0 += 1; if unit == b'\n' as u16 { - point_utf16 += PointUtf16::new(1, 0); + point_utf16.0 += PointUtf16::new(1, 0); } else { - point_utf16 += PointUtf16::new(0, 1); + point_utf16.0 += PointUtf16::new(0, 1); } } diff --git a/crates/text/src/text.rs b/crates/text/src/text.rs index 8cdf087b53c538aabafa0581a2d31d63d4a56846..e4469ca141ff2300a58329ab7e4db072063665c9 100644 --- a/crates/text/src/text.rs +++ b/crates/text/src/text.rs @@ -1590,12 +1590,16 @@ impl BufferSnapshot { self.visible_text.point_to_offset(point) } - pub fn point_utf16_to_offset_clipped(&self, point: PointUtf16) -> usize { - self.visible_text.point_utf16_to_offset_clipped(point) + pub fn point_utf16_to_offset(&self, point: PointUtf16) -> usize { + self.visible_text.point_utf16_to_offset(point) } - pub fn point_utf16_to_point(&self, point: PointUtf16) -> Point { - self.visible_text.point_utf16_to_point(point) + pub fn unclipped_point_utf16_to_offset(&self, point: Unclipped) -> usize { + self.visible_text.unclipped_point_utf16_to_offset(point) + } + + pub fn point_utf16_to_point_clipped(&self, point: PointUtf16) -> Point { + self.visible_text.point_utf16_to_point_clipped(point) } pub fn offset_utf16_to_offset(&self, offset: OffsetUtf16) -> usize { @@ -1649,12 +1653,6 @@ impl BufferSnapshot { self.visible_text.chunks_in_range(start..end) } - pub fn text_for_clamped_range(&self, range: Range) -> Chunks<'_> { - let start = range.start.to_offset_clipped(self); - let end = range.end.to_offset_clipped(self); - self.visible_text.chunks_in_range(start..end) - } - pub fn line_len(&self, row: u32) -> u32 { let row_start_offset = Point::new(row, 0).to_offset(self); let row_end_offset = if row >= self.max_point().row { @@ -1804,18 +1802,10 @@ impl BufferSnapshot { self.anchor_at(position, Bias::Left) } - pub fn clamped_anchor_before(&self, position: T) -> Anchor { - self.anchor_at_offset(position.to_offset_clipped(self), Bias::Left) - } - pub fn anchor_after(&self, position: T) -> Anchor { self.anchor_at(position, Bias::Right) } - pub fn clamped_anchor_after(&self, position: T) -> Anchor { - self.anchor_at_offset(position.to_offset_clipped(self), Bias::Right) - } - pub fn anchor_at(&self, position: T, bias: Bias) -> Anchor { self.anchor_at_offset(position.to_offset(self), bias) } @@ -1857,7 +1847,7 @@ impl BufferSnapshot { self.visible_text.clip_offset_utf16(offset, bias) } - pub fn clip_point_utf16(&self, point: PointUtf16, bias: Bias) -> PointUtf16 { + pub fn clip_point_utf16(&self, point: Unclipped, bias: Bias) -> PointUtf16 { self.visible_text.clip_point_utf16(point, bias) } @@ -2395,13 +2385,15 @@ impl<'a, T: ToOffset> ToOffset for &'a T { } } -pub trait ToOffsetClipped { - fn to_offset_clipped(&self, snapshot: &BufferSnapshot) -> usize; +impl ToOffset for PointUtf16 { + fn to_offset(&self, snapshot: &BufferSnapshot) -> usize { + snapshot.point_utf16_to_offset(*self) + } } -impl ToOffsetClipped for PointUtf16 { - fn to_offset_clipped<'a>(&self, snapshot: &BufferSnapshot) -> usize { - snapshot.point_utf16_to_offset_clipped(*self) +impl ToOffset for Unclipped { + fn to_offset(&self, snapshot: &BufferSnapshot) -> usize { + snapshot.unclipped_point_utf16_to_offset(*self) } } @@ -2427,13 +2419,9 @@ impl ToPoint for Point { } } -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) +impl ToPoint for Unclipped { + fn to_point<'a>(&self, snapshot: &BufferSnapshot) -> Point { + snapshot.point_utf16_to_point_clipped(self.0) } } @@ -2487,27 +2475,27 @@ impl ToOffsetUtf16 for OffsetUtf16 { } } -pub trait Clip { - fn clip(&self, bias: Bias, snapshot: &BufferSnapshot) -> Self; -} +// pub trait Clip { +// fn clip(&self, bias: Bias, snapshot: &BufferSnapshot) -> Self; +// } -impl Clip for usize { - fn clip(&self, bias: Bias, snapshot: &BufferSnapshot) -> Self { - snapshot.clip_offset(*self, bias) - } -} +// impl Clip for usize { +// fn clip(&self, bias: Bias, snapshot: &BufferSnapshot) -> Self { +// snapshot.clip_offset(*self, bias) +// } +// } -impl Clip for Point { - fn clip(&self, bias: Bias, snapshot: &BufferSnapshot) -> Self { - snapshot.clip_point(*self, bias) - } -} +// impl Clip for Point { +// fn clip(&self, bias: Bias, snapshot: &BufferSnapshot) -> Self { +// snapshot.clip_point(*self, bias) +// } +// } -impl Clip for PointUtf16 { - fn clip(&self, bias: Bias, snapshot: &BufferSnapshot) -> Self { - snapshot.clip_point_utf16(*self, bias) - } -} +// impl Clip for Unclipped { +// fn clip(&self, bias: Bias, snapshot: &BufferSnapshot) -> Self { +// snapshot.clip_point_utf16(self.0, bias) +// } +// } pub trait FromAnchor { fn from_anchor(anchor: &Anchor, snapshot: &BufferSnapshot) -> Self; From 8c75df30cb41fdfcdc6450a03dc47fb1957c787f Mon Sep 17 00:00:00 2001 From: Julia Date: Mon, 21 Nov 2022 11:47:46 -0500 Subject: [PATCH 07/14] Wrap a bunch of traits for Unclipped --- crates/language/src/buffer_tests.rs | 1 + crates/project/src/lsp_command.rs | 10 ++-- crates/project/src/project.rs | 6 +-- crates/project/src/worktree.rs | 5 +- crates/rope/src/rope.rs | 72 ++++++++++++++++++++++++++++- 5 files changed, 81 insertions(+), 13 deletions(-) diff --git a/crates/language/src/buffer_tests.rs b/crates/language/src/buffer_tests.rs index 6043127dd526c2d44d0bb66050c83169c73efbe5..68fe8a294824a0be4a2853f1b41221a951352ae8 100644 --- a/crates/language/src/buffer_tests.rs +++ b/crates/language/src/buffer_tests.rs @@ -1337,6 +1337,7 @@ fn test_random_collaboration(cx: &mut MutableAppContext, mut rng: StdRng) { (0..entry_count).map(|_| { let range = buffer.random_byte_range(0, &mut rng); let range = range.to_point_utf16(buffer); + let range = Unclipped(range.start)..Unclipped(range.end); DiagnosticEntry { range, diagnostic: Diagnostic { diff --git a/crates/project/src/lsp_command.rs b/crates/project/src/lsp_command.rs index ae6d18a9a90f6fc34cafc4979de42e7bec293213..3ea12617351ecc1708741ad1a60aef6e73702740 100644 --- a/crates/project/src/lsp_command.rs +++ b/crates/project/src/lsp_command.rs @@ -131,9 +131,7 @@ impl LspCommand for PrepareRename { if buffer.clip_point_utf16(start, Bias::Left) == start.0 && buffer.clip_point_utf16(end, Bias::Left) == end.0 { - return Ok(Some( - buffer.anchor_after(start)..buffer.anchor_before(end), - )); + return Ok(Some(buffer.anchor_after(start)..buffer.anchor_before(end))); } } Ok(None) @@ -884,8 +882,7 @@ impl LspCommand for GetDocumentHighlights { let end = buffer .clip_point_utf16(point_from_lsp(lsp_highlight.range.end), Bias::Left); DocumentHighlight { - range: buffer.anchor_after(start) - ..buffer.anchor_before(end), + range: buffer.anchor_after(start)..buffer.anchor_before(end), kind: lsp_highlight .kind .unwrap_or(lsp::DocumentHighlightKind::READ), @@ -1020,8 +1017,7 @@ impl LspCommand for GetHover { let token_start = buffer.clip_point_utf16(point_from_lsp(range.start), Bias::Left); let token_end = buffer.clip_point_utf16(point_from_lsp(range.end), Bias::Left); - buffer.anchor_after(token_start) - ..buffer.anchor_before(token_end) + buffer.anchor_after(token_start)..buffer.anchor_before(token_end) }) }); diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index e1e839db1fc851518604649dfd737723c1720a41..cb8e01d562b39ca102037ad3b16ecf8653d58e7e 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -25,8 +25,8 @@ use language::{ range_from_lsp, range_to_lsp, Anchor, Bias, Buffer, CachedLspAdapter, CharKind, CodeAction, CodeLabel, Completion, Diagnostic, DiagnosticEntry, DiagnosticSet, Event as BufferEvent, File as _, Language, LanguageRegistry, LanguageServerName, LocalFile, OffsetRangeExt, - Operation, Patch, PointUtf16, TextBufferSnapshot, ToOffset, ToPointUtf16, - Transaction, Unclipped, + Operation, Patch, PointUtf16, TextBufferSnapshot, ToOffset, ToPointUtf16, Transaction, + Unclipped, }; use lsp::{ DiagnosticSeverity, DiagnosticTag, DocumentHighlightKind, LanguageServer, LanguageString, @@ -2660,7 +2660,7 @@ impl Project { let mut sanitized_diagnostics = Vec::new(); let edits_since_save = Patch::new( snapshot - .edits_since::(buffer.read(cx).saved_version()) + .edits_since::>(buffer.read(cx).saved_version()) .collect(), ); for entry in diagnostics { diff --git a/crates/project/src/worktree.rs b/crates/project/src/worktree.rs index 795143c3e09f747726a8d5df9a36223f9a662a52..3bab90d5e34f188b923337d84cadd6397e0e8017 100644 --- a/crates/project/src/worktree.rs +++ b/crates/project/src/worktree.rs @@ -500,7 +500,10 @@ impl LocalWorktree { }) } - pub fn diagnostics_for_path(&self, path: &Path) -> Option>>> { + pub fn diagnostics_for_path( + &self, + path: &Path, + ) -> Option>>> { self.diagnostics.get(path).cloned() } diff --git a/crates/rope/src/rope.rs b/crates/rope/src/rope.rs index 27f0b8cdb414413b140067d26ec992977edaa79c..af74b08743f935f1496936bcc8664442504a96f8 100644 --- a/crates/rope/src/rope.rs +++ b/crates/rope/src/rope.rs @@ -5,7 +5,11 @@ mod point_utf16; use arrayvec::ArrayString; use bromberg_sl2::{DigestString, HashMatrix}; use smallvec::SmallVec; -use std::{cmp, fmt, io, mem, ops::Range, str}; +use std::{ + cmp, fmt, io, mem, + ops::{Add, AddAssign, Range, Sub, SubAssign}, + str, +}; use sum_tree::{Bias, Dimension, SumTree}; pub use offset_utf16::OffsetUtf16; @@ -15,6 +19,70 @@ pub use point_utf16::PointUtf16; #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] pub struct Unclipped(pub T); +impl std::fmt::Debug for Unclipped { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_tuple("Unclipped").field(&self.0).finish() + } +} + +impl Default for Unclipped { + fn default() -> Self { + Unclipped(T::default()) + } +} + +impl From for Unclipped { + fn from(value: T) -> Self { + Unclipped(value) + } +} + +impl<'a, T: sum_tree::Dimension<'a, ChunkSummary>> sum_tree::Dimension<'a, ChunkSummary> + for Unclipped +{ + fn add_summary(&mut self, summary: &'a ChunkSummary, _: &()) { + self.0.add_summary(summary, &()); + } +} + +impl TextDimension for Unclipped { + fn from_text_summary(summary: &TextSummary) -> Self { + Unclipped(T::from_text_summary(summary)) + } + + fn add_assign(&mut self, other: &Self) { + TextDimension::add_assign(&mut self.0, &other.0); + } +} + +impl> Add> for Unclipped { + type Output = Unclipped; + + fn add(self, rhs: Unclipped) -> Self::Output { + Unclipped(self.0 + rhs.0) + } +} + +impl> Sub> for Unclipped { + type Output = Unclipped; + + fn sub(self, rhs: Unclipped) -> Self::Output { + Unclipped(self.0 - rhs.0) + } +} + +impl> AddAssign> for Unclipped { + fn add_assign(&mut self, rhs: Unclipped) { + self.0 += rhs.0; + } +} + +impl> SubAssign> for Unclipped { + fn sub_assign(&mut self, rhs: Unclipped) { + self.0 -= rhs.0; + } +} + #[cfg(test)] const CHUNK_BASE: usize = 6; @@ -945,7 +1013,7 @@ impl std::ops::Add for TextSummary { type Output = Self; fn add(mut self, rhs: Self) -> Self::Output { - self.add_assign(&rhs); + AddAssign::add_assign(&mut self, &rhs); self } } From e51cbf67ab4bf910963859b6a3eb7339153d929c Mon Sep 17 00:00:00 2001 From: Julia Date: Tue, 22 Nov 2022 02:49:47 -0500 Subject: [PATCH 08/14] Fixup compile errors --- crates/diagnostics/src/diagnostics.rs | 22 +++--- crates/editor/src/editor.rs | 2 +- crates/editor/src/multi_buffer.rs | 35 +++++---- crates/editor/src/selections_collection.rs | 13 ---- crates/language/src/proto.rs | 1 + crates/project/src/project.rs | 74 ++++++++++--------- crates/project/src/project_tests.rs | 4 +- crates/project_symbols/src/project_symbols.rs | 2 +- crates/rpc/proto/zed.proto | 9 ++- crates/rpc/src/rpc.rs | 2 +- crates/text/src/text.rs | 22 ------ 11 files changed, 83 insertions(+), 103 deletions(-) diff --git a/crates/diagnostics/src/diagnostics.rs b/crates/diagnostics/src/diagnostics.rs index b4fb6a503c1a58dfa036a2087ed21e55901477f7..bf237b9ad9ecd1d0eae0a13c50e06dfee6a219ac 100644 --- a/crates/diagnostics/src/diagnostics.rs +++ b/crates/diagnostics/src/diagnostics.rs @@ -738,7 +738,7 @@ mod tests { DisplayPoint, }; use gpui::TestAppContext; - use language::{Diagnostic, DiagnosticEntry, DiagnosticSeverity, PointUtf16}; + use language::{Diagnostic, DiagnosticEntry, DiagnosticSeverity, PointUtf16, Unclipped}; use serde_json::json; use unindent::Unindent as _; use workspace::AppState; @@ -788,7 +788,7 @@ mod tests { None, vec![ DiagnosticEntry { - range: PointUtf16::new(1, 8)..PointUtf16::new(1, 9), + range: Unclipped(PointUtf16::new(1, 8))..Unclipped(PointUtf16::new(1, 9)), diagnostic: Diagnostic { message: "move occurs because `x` has type `Vec`, which does not implement the `Copy` trait" @@ -801,7 +801,7 @@ mod tests { }, }, DiagnosticEntry { - range: PointUtf16::new(2, 8)..PointUtf16::new(2, 9), + range: Unclipped(PointUtf16::new(2, 8))..Unclipped(PointUtf16::new(2, 9)), diagnostic: Diagnostic { message: "move occurs because `y` has type `Vec`, which does not implement the `Copy` trait" @@ -814,7 +814,7 @@ mod tests { }, }, DiagnosticEntry { - range: PointUtf16::new(3, 6)..PointUtf16::new(3, 7), + range: Unclipped(PointUtf16::new(3, 6))..Unclipped(PointUtf16::new(3, 7)), diagnostic: Diagnostic { message: "value moved here".to_string(), severity: DiagnosticSeverity::INFORMATION, @@ -825,7 +825,7 @@ mod tests { }, }, DiagnosticEntry { - range: PointUtf16::new(4, 6)..PointUtf16::new(4, 7), + range: Unclipped(PointUtf16::new(4, 6))..Unclipped(PointUtf16::new(4, 7)), diagnostic: Diagnostic { message: "value moved here".to_string(), severity: DiagnosticSeverity::INFORMATION, @@ -836,7 +836,7 @@ mod tests { }, }, DiagnosticEntry { - range: PointUtf16::new(7, 6)..PointUtf16::new(7, 7), + range: Unclipped(PointUtf16::new(7, 6))..Unclipped(PointUtf16::new(7, 7)), diagnostic: Diagnostic { message: "use of moved value\nvalue used here after move".to_string(), severity: DiagnosticSeverity::ERROR, @@ -847,7 +847,7 @@ mod tests { }, }, DiagnosticEntry { - range: PointUtf16::new(8, 6)..PointUtf16::new(8, 7), + range: Unclipped(PointUtf16::new(8, 6))..Unclipped(PointUtf16::new(8, 7)), diagnostic: Diagnostic { message: "use of moved value\nvalue used here after move".to_string(), severity: DiagnosticSeverity::ERROR, @@ -939,7 +939,7 @@ mod tests { PathBuf::from("/test/consts.rs"), None, vec![DiagnosticEntry { - range: PointUtf16::new(0, 15)..PointUtf16::new(0, 15), + range: Unclipped(PointUtf16::new(0, 15))..Unclipped(PointUtf16::new(0, 15)), diagnostic: Diagnostic { message: "mismatched types\nexpected `usize`, found `char`".to_string(), severity: DiagnosticSeverity::ERROR, @@ -1040,7 +1040,8 @@ mod tests { None, vec![ DiagnosticEntry { - range: PointUtf16::new(0, 15)..PointUtf16::new(0, 15), + range: Unclipped(PointUtf16::new(0, 15)) + ..Unclipped(PointUtf16::new(0, 15)), diagnostic: Diagnostic { message: "mismatched types\nexpected `usize`, found `char`" .to_string(), @@ -1052,7 +1053,8 @@ mod tests { }, }, DiagnosticEntry { - range: PointUtf16::new(1, 15)..PointUtf16::new(1, 15), + range: Unclipped(PointUtf16::new(1, 15)) + ..Unclipped(PointUtf16::new(1, 15)), diagnostic: Diagnostic { message: "unresolved name `c`".to_string(), severity: DiagnosticSeverity::ERROR, diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 4575e9ce5e9c1e0bdf603bc0fae2429af56ffa93..dd5934f9794c4d65300db90bd3aedc7e69e9429f 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, - ToOffsetClipped, ToPoint, + 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 aa25b476803418f6c0ad8b5a2ae4bb04c96a4d3e..969a9702995fe0258e1e7bfeaa3a410b4dea6647 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -11,7 +11,7 @@ use language::{ char_kind, AutoindentMode, Buffer, BufferChunks, BufferSnapshot, CharKind, Chunk, CursorShape, DiagnosticEntry, Event, File, IndentSize, Language, OffsetRangeExt, OffsetUtf16, Outline, OutlineItem, Point, PointUtf16, Selection, TextDimension, ToOffset as _, ToOffsetUtf16 as _, - ToPoint as _, ToPointUtf16 as _, TransactionId, + ToPoint as _, ToPointUtf16 as _, TransactionId, Unclipped, }; use smallvec::SmallVec; use std::{ @@ -72,10 +72,6 @@ pub trait ToOffset: 'static + fmt::Debug { fn to_offset(&self, snapshot: &MultiBufferSnapshot) -> usize; } -pub trait ToOffsetClipped: 'static + fmt::Debug { - fn to_offset_clipped(&self, snapshot: &MultiBufferSnapshot) -> usize; -} - pub trait ToOffsetUtf16: 'static + fmt::Debug { fn to_offset_utf16(&self, snapshot: &MultiBufferSnapshot) -> OffsetUtf16; } @@ -1753,20 +1749,21 @@ impl MultiBufferSnapshot { *cursor.start() + overshoot } - pub fn clip_point_utf16(&self, point: PointUtf16, bias: Bias) -> PointUtf16 { + pub fn clip_point_utf16(&self, point: Unclipped, bias: Bias) -> PointUtf16 { if let Some((_, _, buffer)) = self.as_singleton() { return buffer.clip_point_utf16(point, bias); } let mut cursor = self.excerpts.cursor::(); - cursor.seek(&point, Bias::Right, &()); + //Cannot not panic if out of bounds as it will just not reach the target position + cursor.seek(&point.0, Bias::Right, &()); let overshoot = if let Some(excerpt) = cursor.item() { let excerpt_start = excerpt .buffer .offset_to_point_utf16(excerpt.range.context.start.to_offset(&excerpt.buffer)); let buffer_point = excerpt .buffer - .clip_point_utf16(excerpt_start + (point - cursor.start()), bias); + .clip_point_utf16(Unclipped(excerpt_start + (point.0 - cursor.start())), bias); buffer_point.saturating_sub(excerpt_start) } else { PointUtf16::zero() @@ -1949,9 +1946,9 @@ impl MultiBufferSnapshot { } } - pub fn point_utf16_to_offset_clipped(&self, point: PointUtf16) -> usize { + pub fn point_utf16_to_offset(&self, point: PointUtf16) -> usize { if let Some((_, _, buffer)) = self.as_singleton() { - return buffer.point_utf16_to_offset_clipped(point); + return buffer.point_utf16_to_offset(point); } let mut cursor = self.excerpts.cursor::<(PointUtf16, usize)>(); @@ -1965,7 +1962,7 @@ impl MultiBufferSnapshot { .offset_to_point_utf16(excerpt.range.context.start.to_offset(&excerpt.buffer)); let buffer_offset = excerpt .buffer - .point_utf16_to_offset_clipped(excerpt_start_point + overshoot); + .point_utf16_to_offset(excerpt_start_point + overshoot); *start_offset + (buffer_offset - excerpt_start_offset) } else { self.excerpts.summary().text.len @@ -3291,9 +3288,9 @@ impl ToOffset for OffsetUtf16 { } } -impl ToOffsetClipped for PointUtf16 { - fn to_offset_clipped<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize { - snapshot.point_utf16_to_offset_clipped(*self) +impl ToOffset for PointUtf16 { + fn to_offset<'a>(&self, snapshot: &MultiBufferSnapshot) -> usize { + snapshot.point_utf16_to_offset(*self) } } @@ -4162,12 +4159,14 @@ mod tests { } for _ in 0..ch.len_utf16() { - let left_point_utf16 = snapshot.clip_point_utf16(point_utf16, Bias::Left); - let right_point_utf16 = snapshot.clip_point_utf16(point_utf16, Bias::Right); + let left_point_utf16 = + snapshot.clip_point_utf16(Unclipped(point_utf16), Bias::Left); + let right_point_utf16 = + snapshot.clip_point_utf16(Unclipped(point_utf16), Bias::Right); let buffer_left_point_utf16 = - buffer.clip_point_utf16(buffer_point_utf16, Bias::Left); + buffer.clip_point_utf16(Unclipped(buffer_point_utf16), Bias::Left); let buffer_right_point_utf16 = - buffer.clip_point_utf16(buffer_point_utf16, Bias::Right); + buffer.clip_point_utf16(Unclipped(buffer_point_utf16), Bias::Right); assert_eq!( left_point_utf16, excerpt_start.lines_utf16() diff --git a/crates/editor/src/selections_collection.rs b/crates/editor/src/selections_collection.rs index 14026d9f4e86ce93ac3c40c710a614420d55ca37..facc1b04917cd289645b599cb8060f9ff8dd08d4 100644 --- a/crates/editor/src/selections_collection.rs +++ b/crates/editor/src/selections_collection.rs @@ -14,7 +14,6 @@ use util::post_inc; use crate::{ display_map::{DisplayMap, DisplaySnapshot, ToDisplayPoint}, Anchor, DisplayPoint, ExcerptId, MultiBuffer, MultiBufferSnapshot, SelectMode, ToOffset, - ToOffsetClipped, }; #[derive(Clone)] @@ -551,18 +550,6 @@ impl<'a> MutableSelectionsCollection<'a> { self.select_offset_ranges(ranges); } - pub fn select_clipped_ranges(&mut self, ranges: I) - where - I: IntoIterator>, - T: ToOffsetClipped, - { - let buffer = self.buffer.read(self.cx).snapshot(self.cx); - let ranges = ranges.into_iter().map(|range| { - range.start.to_offset_clipped(&buffer)..range.end.to_offset_clipped(&buffer) - }); - self.select_offset_ranges(ranges); - } - fn select_offset_ranges(&mut self, ranges: I) where I: IntoIterator>, diff --git a/crates/language/src/proto.rs b/crates/language/src/proto.rs index 674ce4f50eb5988098c32c3d7557852f9e2b912a..ca86b93bfd462067cb28722bcd58d1a801b05ea0 100644 --- a/crates/language/src/proto.rs +++ b/crates/language/src/proto.rs @@ -357,6 +357,7 @@ pub fn deserialize_diagnostics( .collect() } +//TODO: Deserialize anchors into `Unclipped`? pub fn deserialize_anchor(anchor: proto::Anchor) -> Option { Some(Anchor { timestamp: clock::Local { diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index cb8e01d562b39ca102037ad3b16ecf8653d58e7e..f4752270de6c19a3d286ade1a4efb81bf4f475c6 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -253,7 +253,7 @@ pub struct Symbol { pub label: CodeLabel, pub name: String, pub kind: lsp::SymbolKind, - pub range: Range, + pub range: Range>, pub signature: [u8; 32], } @@ -2682,9 +2682,9 @@ impl Project { // Expand empty ranges by one codepoint if range.start == range.end { // This will be go to the next boundary when being clipped - range.end.column += 1; - if range.start == range.end && range.end.column > 0 { - range.start.column -= 1; + range.end.0.column += 1; + if range.start == range.end && range.end.0.column > 0 { + range.start.0.column -= 1; } } @@ -3287,7 +3287,7 @@ impl Project { return Task::ready(Ok(Default::default())); }; - let position = position.to_point_utf16(source_buffer); + let position = Unclipped(position.to_point_utf16(source_buffer)); let anchor = source_buffer.anchor_after(position); if worktree.read(cx).as_local().is_some() { @@ -3306,7 +3306,7 @@ impl Project { lsp::TextDocumentIdentifier::new( lsp::Url::from_file_path(buffer_abs_path).unwrap(), ), - point_to_lsp(position), + point_to_lsp(position.0), ), context: Default::default(), work_done_progress_params: Default::default(), @@ -3349,7 +3349,7 @@ impl Project { let range = range_from_lsp(edit.range); let start = snapshot.clip_point_utf16(range.start, Bias::Left); let end = snapshot.clip_point_utf16(range.end, Bias::Left); - if start != range.start || end != range.end { + if start != range.start.0 || end != range.end.0 { log::info!("completion out of expected range"); return None; } @@ -3361,13 +3361,13 @@ impl Project { // If the language server does not provide a range, then infer // the range based on the syntax tree. None => { - if position != clipped_position { + if position.0 != clipped_position { log::info!("completion out of expected range"); return None; } let Range { start, end } = range_for_token .get_or_insert_with(|| { - let offset = position.to_offset_clipped(&snapshot); + let offset = position.to_offset(&snapshot); let (range, kind) = snapshot.surrounding_word(offset); if kind == Some(CharKind::Word) { range @@ -5116,22 +5116,30 @@ impl Project { _: Arc, mut cx: AsyncAppContext, ) -> Result { - let position = envelope - .payload - .position - .and_then(language::proto::deserialize_anchor) - .ok_or_else(|| anyhow!("invalid position"))?; - let version = deserialize_version(envelope.payload.version); let buffer = this.read_with(&cx, |this, cx| { this.opened_buffers .get(&envelope.payload.buffer_id) .and_then(|buffer| buffer.upgrade(cx)) .ok_or_else(|| anyhow!("unknown buffer id {}", envelope.payload.buffer_id)) })?; + + let position = envelope + .payload + .position + .and_then(language::proto::deserialize_anchor) + .map(|p| { + buffer.read_with(&cx, |buffer, _| { + buffer.clip_point_utf16(Unclipped(p.to_point_utf16(buffer)), Bias::Left) + }) + }) + .ok_or_else(|| anyhow!("invalid position"))?; + + let version = deserialize_version(envelope.payload.version); buffer .update(&mut cx, |buffer, _| buffer.wait_for_version(version)) .await; let version = buffer.read_with(&cx, |buffer, _| buffer.version()); + let completions = this .update(&mut cx, |this, cx| this.completions(&buffer, position, cx)) .await?; @@ -5618,8 +5626,8 @@ impl Project { }, name: serialized_symbol.name, - range: PointUtf16::new(start.row, start.column) - ..PointUtf16::new(end.row, end.column), + range: Unclipped(PointUtf16::new(start.row, start.column)) + ..Unclipped(PointUtf16::new(end.row, end.column)), kind, signature: serialized_symbol .signature @@ -5705,10 +5713,10 @@ impl Project { let mut lsp_edits = lsp_edits.into_iter().peekable(); let mut edits = Vec::new(); - while let Some((mut range, mut new_text)) = lsp_edits.next() { + while let Some((range, mut new_text)) = lsp_edits.next() { // Clip invalid ranges provided by the language server. - range.start = snapshot.clip_point_utf16(range.start, Bias::Left); - range.end = snapshot.clip_point_utf16(range.end, Bias::Left); + let mut range = snapshot.clip_point_utf16(range.start, Bias::Left) + ..snapshot.clip_point_utf16(range.end, Bias::Left); // Combine any LSP edits that are adjacent. // @@ -5720,11 +5728,11 @@ impl Project { // In order for the diffing logic below to work properly, any edits that // cancel each other out must be combined into one. while let Some((next_range, next_text)) = lsp_edits.peek() { - if next_range.start > range.end { - if next_range.start.row > range.end.row + 1 - || next_range.start.column > 0 + if next_range.start.0 > range.end { + if next_range.start.0.row > range.end.row + 1 + || next_range.start.0.column > 0 || snapshot.clip_point_utf16( - PointUtf16::new(range.end.row, u32::MAX), + Unclipped(PointUtf16::new(range.end.row, u32::MAX)), Bias::Left, ) > range.end { @@ -5732,7 +5740,7 @@ impl Project { } new_text.push('\n'); } - range.end = next_range.end; + range.end = snapshot.clip_point_utf16(next_range.end, Bias::Left); new_text.push_str(next_text); lsp_edits.next(); } @@ -5741,8 +5749,8 @@ impl Project { // we can identify the changes more precisely, preserving the locations // of any anchors positioned in the unchanged regions. if range.end.row > range.start.row { - let mut offset = range.start.to_offset_clipped(&snapshot); - let old_text = snapshot.text_for_clamped_range(range).collect::(); + let mut offset = range.start.to_offset(&snapshot); + let old_text = snapshot.text_for_range(range).collect::(); let diff = TextDiff::from_lines(old_text.as_str(), &new_text); let mut moved_since_edit = true; @@ -6053,13 +6061,13 @@ fn serialize_symbol(symbol: &Symbol) -> proto::Symbol { path: symbol.path.path.to_string_lossy().to_string(), name: symbol.name.clone(), kind: unsafe { mem::transmute(symbol.kind) }, - start: Some(proto::Point { - row: symbol.range.start.row, - column: symbol.range.start.column, + start: Some(proto::UnclippedPoint { + row: symbol.range.start.0.row, + column: symbol.range.start.0.column, }), - end: Some(proto::Point { - row: symbol.range.end.row, - column: symbol.range.end.column, + end: Some(proto::UnclippedPoint { + row: symbol.range.end.0.row, + column: symbol.range.end.0.column, }), signature: symbol.signature.to_vec(), } diff --git a/crates/project/src/project_tests.rs b/crates/project/src/project_tests.rs index ca274b18b8a37f74d9587470c2a9877d900505e8..dfb699fdbb55ece4e32e7c7a362709ff6ddf92ff 100644 --- a/crates/project/src/project_tests.rs +++ b/crates/project/src/project_tests.rs @@ -1239,7 +1239,7 @@ async fn test_empty_diagnostic_ranges(cx: &mut gpui::TestAppContext) { &buffer, vec![ DiagnosticEntry { - range: PointUtf16::new(0, 10)..PointUtf16::new(0, 10), + range: Unclipped(PointUtf16::new(0, 10))..Unclipped(PointUtf16::new(0, 10)), diagnostic: Diagnostic { severity: DiagnosticSeverity::ERROR, message: "syntax error 1".to_string(), @@ -1247,7 +1247,7 @@ async fn test_empty_diagnostic_ranges(cx: &mut gpui::TestAppContext) { }, }, DiagnosticEntry { - range: PointUtf16::new(1, 10)..PointUtf16::new(1, 10), + range: Unclipped(PointUtf16::new(1, 10))..Unclipped(PointUtf16::new(1, 10)), diagnostic: Diagnostic { severity: DiagnosticSeverity::ERROR, message: "syntax error 2".to_string(), diff --git a/crates/project_symbols/src/project_symbols.rs b/crates/project_symbols/src/project_symbols.rs index eb755d2d2f360a2691a447c4549c4d83de4150ba..273230fe26feb5f01cc1cdcbacd7c321d689a446 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_clipped_ranges([position..position]) + s.select_ranges([position..position]) }); }); }); diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index ded708370d3f64d00c478661911e34e37fa8dd98..b6516d235d8f81d18a879a50992f4547126a397b 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -412,8 +412,8 @@ message Symbol { string name = 4; int32 kind = 5; string path = 6; - Point start = 7; - Point end = 8; + UnclippedPoint start = 7; + UnclippedPoint end = 8; bytes signature = 9; } @@ -1047,6 +1047,11 @@ message Point { uint32 column = 2; } +message UnclippedPoint { + uint32 row = 1; + uint32 column = 2; +} + message Nonce { uint64 upper_half = 1; uint64 lower_half = 2; diff --git a/crates/rpc/src/rpc.rs b/crates/rpc/src/rpc.rs index b6aef64677b6f06716a6ea40d9b52a42017c3543..5ca5711d9ca8c43cd5f1979ee76ea11e61053bec 100644 --- a/crates/rpc/src/rpc.rs +++ b/crates/rpc/src/rpc.rs @@ -6,4 +6,4 @@ pub use conn::Connection; pub use peer::*; mod macros; -pub const PROTOCOL_VERSION: u32 = 39; +pub const PROTOCOL_VERSION: u32 = 40; diff --git a/crates/text/src/text.rs b/crates/text/src/text.rs index e4469ca141ff2300a58329ab7e4db072063665c9..aa4ef109cdb5b0909aa3f6d76717f3b7e7e3e9e5 100644 --- a/crates/text/src/text.rs +++ b/crates/text/src/text.rs @@ -2475,28 +2475,6 @@ impl ToOffsetUtf16 for OffsetUtf16 { } } -// pub trait Clip { -// fn clip(&self, bias: Bias, snapshot: &BufferSnapshot) -> Self; -// } - -// impl Clip for usize { -// fn clip(&self, bias: Bias, snapshot: &BufferSnapshot) -> Self { -// snapshot.clip_offset(*self, bias) -// } -// } - -// impl Clip for Point { -// fn clip(&self, bias: Bias, snapshot: &BufferSnapshot) -> Self { -// snapshot.clip_point(*self, bias) -// } -// } - -// impl Clip for Unclipped { -// fn clip(&self, bias: Bias, snapshot: &BufferSnapshot) -> Self { -// snapshot.clip_point_utf16(self.0, bias) -// } -// } - pub trait FromAnchor { fn from_anchor(anchor: &Anchor, snapshot: &BufferSnapshot) -> Self; } From b58ae8bdd7760d78ca9418ac4fd8618abbd1d630 Mon Sep 17 00:00:00 2001 From: Julia Date: Wed, 23 Nov 2022 13:20:47 -0500 Subject: [PATCH 09/14] Clip diagnostic range before and during empty range expansion Co-Authored-By: Max Brunsfeld --- crates/language/src/buffer_tests.rs | 2 +- crates/language/src/diagnostic_set.rs | 4 ++-- crates/project/src/project.rs | 11 +++++++---- 3 files changed, 10 insertions(+), 7 deletions(-) diff --git a/crates/language/src/buffer_tests.rs b/crates/language/src/buffer_tests.rs index 68fe8a294824a0be4a2853f1b41221a951352ae8..82641dbaa459b493d0a5ff36b6c0426f3a77857c 100644 --- a/crates/language/src/buffer_tests.rs +++ b/crates/language/src/buffer_tests.rs @@ -1337,7 +1337,7 @@ fn test_random_collaboration(cx: &mut MutableAppContext, mut rng: StdRng) { (0..entry_count).map(|_| { let range = buffer.random_byte_range(0, &mut rng); let range = range.to_point_utf16(buffer); - let range = Unclipped(range.start)..Unclipped(range.end); + let range = range.start..range.end; DiagnosticEntry { range, diagnostic: Diagnostic { diff --git a/crates/language/src/diagnostic_set.rs b/crates/language/src/diagnostic_set.rs index a4d6dc12c731ec44a60f2fb8078e86355f283d18..cde5a6fb2bab996dbfba9418ac72eac87652ac7b 100644 --- a/crates/language/src/diagnostic_set.rs +++ b/crates/language/src/diagnostic_set.rs @@ -6,7 +6,7 @@ use std::{ ops::Range, }; use sum_tree::{self, Bias, SumTree}; -use text::{Anchor, FromAnchor, PointUtf16, ToOffset, Unclipped}; +use text::{Anchor, FromAnchor, PointUtf16, ToOffset}; #[derive(Clone, Debug, Default)] pub struct DiagnosticSet { @@ -63,7 +63,7 @@ impl DiagnosticSet { pub fn new(iter: I, buffer: &text::BufferSnapshot) -> Self where - I: IntoIterator>>, + I: IntoIterator>, { let mut entries = iter.into_iter().collect::>(); entries.sort_unstable_by_key(|entry| (entry.range.start, Reverse(entry.range.end))); diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index f4752270de6c19a3d286ade1a4efb81bf4f475c6..432d13076ff3e7cfbc806a561013fd9c4034b1ea 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -2677,14 +2677,17 @@ impl Project { end = entry.range.end; } - let mut range = start..end; + let mut range = snapshot.clip_point_utf16(start, Bias::Left) + ..snapshot.clip_point_utf16(end, Bias::Right); // Expand empty ranges by one codepoint if range.start == range.end { // This will be go to the next boundary when being clipped - range.end.0.column += 1; - if range.start == range.end && range.end.0.column > 0 { - range.start.0.column -= 1; + range.end.column += 1; + range.end = snapshot.clip_point_utf16(Unclipped(range.end), Bias::Right); + if range.start == range.end && range.end.column > 0 { + range.start.column -= 1; + range.end = snapshot.clip_point_utf16(Unclipped(range.end), Bias::Left); } } From a666ca3e407cc0e2cbc2431cc5a6d3d650537dff Mon Sep 17 00:00:00 2001 From: Julia Date: Wed, 23 Nov 2022 13:28:44 -0500 Subject: [PATCH 10/14] Collapse proto Point into the one kind of use case, utf-16 coords Co-Authored-By: Max Brunsfeld --- crates/project/src/project.rs | 4 ++-- crates/rpc/proto/zed.proto | 13 +++++-------- 2 files changed, 7 insertions(+), 10 deletions(-) diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 432d13076ff3e7cfbc806a561013fd9c4034b1ea..08714d6cd315277480c6897e3742deeecb95a79a 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -6064,11 +6064,11 @@ fn serialize_symbol(symbol: &Symbol) -> proto::Symbol { path: symbol.path.path.to_string_lossy().to_string(), name: symbol.name.clone(), kind: unsafe { mem::transmute(symbol.kind) }, - start: Some(proto::UnclippedPoint { + start: Some(proto::PointUtf16 { row: symbol.range.start.0.row, column: symbol.range.start.0.column, }), - end: Some(proto::UnclippedPoint { + end: Some(proto::PointUtf16 { row: symbol.range.end.0.row, column: symbol.range.end.0.column, }), diff --git a/crates/rpc/proto/zed.proto b/crates/rpc/proto/zed.proto index b6516d235d8f81d18a879a50992f4547126a397b..6bfe7124c9ce936fdee3e11b697f3d3925a0cf22 100644 --- a/crates/rpc/proto/zed.proto +++ b/crates/rpc/proto/zed.proto @@ -412,8 +412,10 @@ message Symbol { string name = 4; int32 kind = 5; string path = 6; - UnclippedPoint start = 7; - UnclippedPoint end = 8; + // Cannot use generate anchors for unopend files, + // so we are forced to use point coords instead + PointUtf16 start = 7; + PointUtf16 end = 8; bytes signature = 9; } @@ -1042,12 +1044,7 @@ message Range { uint64 end = 2; } -message Point { - uint32 row = 1; - uint32 column = 2; -} - -message UnclippedPoint { +message PointUtf16 { uint32 row = 1; uint32 column = 2; } From 03cfd23ac563d74d43d29ec3651362fb8efa0df3 Mon Sep 17 00:00:00 2001 From: Julia Date: Wed, 23 Nov 2022 13:33:30 -0500 Subject: [PATCH 11/14] Bump protocol version back down as proto changes are non-breaking --- crates/editor/src/multi_buffer.rs | 1 - crates/language/src/proto.rs | 1 - crates/rpc/src/rpc.rs | 2 +- 3 files changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/editor/src/multi_buffer.rs b/crates/editor/src/multi_buffer.rs index 969a9702995fe0258e1e7bfeaa3a410b4dea6647..e3f12c18421832a1add4447c4249872e959971de 100644 --- a/crates/editor/src/multi_buffer.rs +++ b/crates/editor/src/multi_buffer.rs @@ -1755,7 +1755,6 @@ impl MultiBufferSnapshot { } let mut cursor = self.excerpts.cursor::(); - //Cannot not panic if out of bounds as it will just not reach the target position cursor.seek(&point.0, Bias::Right, &()); let overshoot = if let Some(excerpt) = cursor.item() { let excerpt_start = excerpt diff --git a/crates/language/src/proto.rs b/crates/language/src/proto.rs index ca86b93bfd462067cb28722bcd58d1a801b05ea0..674ce4f50eb5988098c32c3d7557852f9e2b912a 100644 --- a/crates/language/src/proto.rs +++ b/crates/language/src/proto.rs @@ -357,7 +357,6 @@ pub fn deserialize_diagnostics( .collect() } -//TODO: Deserialize anchors into `Unclipped`? pub fn deserialize_anchor(anchor: proto::Anchor) -> Option { Some(Anchor { timestamp: clock::Local { diff --git a/crates/rpc/src/rpc.rs b/crates/rpc/src/rpc.rs index 5ca5711d9ca8c43cd5f1979ee76ea11e61053bec..b6aef64677b6f06716a6ea40d9b52a42017c3543 100644 --- a/crates/rpc/src/rpc.rs +++ b/crates/rpc/src/rpc.rs @@ -6,4 +6,4 @@ pub use conn::Connection; pub use peer::*; mod macros; -pub const PROTOCOL_VERSION: u32 = 40; +pub const PROTOCOL_VERSION: u32 = 39; From 55ca085d7d55efd9c9d487a05d60baacbbccdef9 Mon Sep 17 00:00:00 2001 From: Julia Date: Wed, 23 Nov 2022 13:52:18 -0500 Subject: [PATCH 12/14] Consistency in prefix/suffix/signature of UTF16 point to point conversion Co-Authored-By: Max Brunsfeld --- crates/rope/src/rope.rs | 16 ++++++++-------- crates/text/src/text.rs | 6 +++--- 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/crates/rope/src/rope.rs b/crates/rope/src/rope.rs index af74b08743f935f1496936bcc8664442504a96f8..2a1268eab95f53e3e198fd7a889d33502cb05bde 100644 --- a/crates/rope/src/rope.rs +++ b/crates/rope/src/rope.rs @@ -351,16 +351,16 @@ impl Rope { .map_or(0, |chunk| chunk.point_utf16_to_offset(overshoot, clip)) } - pub fn point_utf16_to_point_clipped(&self, point: PointUtf16) -> Point { - if point >= self.summary().lines_utf16() { + pub fn unclipped_point_utf16_to_point(&self, point: Unclipped) -> Point { + if point.0 >= self.summary().lines_utf16() { return self.summary().lines; } let mut cursor = self.chunks.cursor::<(PointUtf16, Point)>(); - cursor.seek(&point, Bias::Left, &()); - let overshoot = point - cursor.start().0; + cursor.seek(&point.0, Bias::Left, &()); + let overshoot = Unclipped(point.0 - cursor.start().0); cursor.start().1 + cursor.item().map_or(Point::zero(), |chunk| { - chunk.point_utf16_to_point_clipped(overshoot) + chunk.unclipped_point_utf16_to_point(overshoot) }) } @@ -830,16 +830,16 @@ impl Chunk { offset } - fn point_utf16_to_point_clipped(&self, target: PointUtf16) -> Point { + fn unclipped_point_utf16_to_point(&self, target: Unclipped) -> Point { let mut point = Point::zero(); let mut point_utf16 = PointUtf16::zero(); for ch in self.0.chars() { - if point_utf16 == target { + if point_utf16 == target.0 { break; } - if point_utf16 > target { + if point_utf16 > target.0 { // If the point is past the end of a line or inside of a code point, // return the last valid point before the target. return point; diff --git a/crates/text/src/text.rs b/crates/text/src/text.rs index aa4ef109cdb5b0909aa3f6d76717f3b7e7e3e9e5..7e486d231e8bced733779ac1b04183a209a199b8 100644 --- a/crates/text/src/text.rs +++ b/crates/text/src/text.rs @@ -1598,8 +1598,8 @@ impl BufferSnapshot { self.visible_text.unclipped_point_utf16_to_offset(point) } - pub fn point_utf16_to_point_clipped(&self, point: PointUtf16) -> Point { - self.visible_text.point_utf16_to_point_clipped(point) + pub fn unclipped_point_utf16_to_point(&self, point: Unclipped) -> Point { + self.visible_text.unclipped_point_utf16_to_point(point) } pub fn offset_utf16_to_offset(&self, offset: OffsetUtf16) -> usize { @@ -2421,7 +2421,7 @@ impl ToPoint for Point { impl ToPoint for Unclipped { fn to_point<'a>(&self, snapshot: &BufferSnapshot) -> Point { - snapshot.point_utf16_to_point_clipped(self.0) + snapshot.unclipped_point_utf16_to_point(*self) } } From 525d84e5bf94cc20cf407049e5286171b61c18bd Mon Sep 17 00:00:00 2001 From: Julia Date: Wed, 23 Nov 2022 13:52:39 -0500 Subject: [PATCH 13/14] Remove spurious lifetimes Co-Authored-By: Max Brunsfeld --- crates/text/src/text.rs | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/crates/text/src/text.rs b/crates/text/src/text.rs index 7e486d231e8bced733779ac1b04183a209a199b8..0a260c08cee80b1dae15e251be66b45a8bd3e872 100644 --- a/crates/text/src/text.rs +++ b/crates/text/src/text.rs @@ -2361,20 +2361,20 @@ pub trait ToOffset { } impl ToOffset for Point { - fn to_offset<'a>(&self, snapshot: &BufferSnapshot) -> usize { + fn to_offset(&self, snapshot: &BufferSnapshot) -> usize { snapshot.point_to_offset(*self) } } impl ToOffset for usize { - fn to_offset<'a>(&self, snapshot: &BufferSnapshot) -> usize { + fn to_offset(&self, snapshot: &BufferSnapshot) -> usize { assert!(*self <= snapshot.len(), "offset {self} is out of range"); *self } } impl ToOffset for Anchor { - fn to_offset<'a>(&self, snapshot: &BufferSnapshot) -> usize { + fn to_offset(&self, snapshot: &BufferSnapshot) -> usize { snapshot.summary_for_anchor(self) } } @@ -2402,25 +2402,25 @@ pub trait ToPoint { } impl ToPoint for Anchor { - fn to_point<'a>(&self, snapshot: &BufferSnapshot) -> Point { + fn to_point(&self, snapshot: &BufferSnapshot) -> Point { snapshot.summary_for_anchor(self) } } impl ToPoint for usize { - fn to_point<'a>(&self, snapshot: &BufferSnapshot) -> Point { + fn to_point(&self, snapshot: &BufferSnapshot) -> Point { snapshot.offset_to_point(*self) } } impl ToPoint for Point { - fn to_point<'a>(&self, _: &BufferSnapshot) -> Point { + fn to_point(&self, _: &BufferSnapshot) -> Point { *self } } impl ToPoint for Unclipped { - fn to_point<'a>(&self, snapshot: &BufferSnapshot) -> Point { + fn to_point(&self, snapshot: &BufferSnapshot) -> Point { snapshot.unclipped_point_utf16_to_point(*self) } } @@ -2430,25 +2430,25 @@ pub trait ToPointUtf16 { } impl ToPointUtf16 for Anchor { - fn to_point_utf16<'a>(&self, snapshot: &BufferSnapshot) -> PointUtf16 { + fn to_point_utf16(&self, snapshot: &BufferSnapshot) -> PointUtf16 { snapshot.summary_for_anchor(self) } } impl ToPointUtf16 for usize { - fn to_point_utf16<'a>(&self, snapshot: &BufferSnapshot) -> PointUtf16 { + fn to_point_utf16(&self, snapshot: &BufferSnapshot) -> PointUtf16 { snapshot.offset_to_point_utf16(*self) } } impl ToPointUtf16 for PointUtf16 { - fn to_point_utf16<'a>(&self, _: &BufferSnapshot) -> PointUtf16 { + fn to_point_utf16(&self, _: &BufferSnapshot) -> PointUtf16 { *self } } impl ToPointUtf16 for Point { - fn to_point_utf16<'a>(&self, snapshot: &BufferSnapshot) -> PointUtf16 { + fn to_point_utf16(&self, snapshot: &BufferSnapshot) -> PointUtf16 { snapshot.point_to_point_utf16(*self) } } @@ -2458,19 +2458,19 @@ pub trait ToOffsetUtf16 { } impl ToOffsetUtf16 for Anchor { - fn to_offset_utf16<'a>(&self, snapshot: &BufferSnapshot) -> OffsetUtf16 { + fn to_offset_utf16(&self, snapshot: &BufferSnapshot) -> OffsetUtf16 { snapshot.summary_for_anchor(self) } } impl ToOffsetUtf16 for usize { - fn to_offset_utf16<'a>(&self, snapshot: &BufferSnapshot) -> OffsetUtf16 { + fn to_offset_utf16(&self, snapshot: &BufferSnapshot) -> OffsetUtf16 { snapshot.offset_to_offset_utf16(*self) } } impl ToOffsetUtf16 for OffsetUtf16 { - fn to_offset_utf16<'a>(&self, _snapshot: &BufferSnapshot) -> OffsetUtf16 { + fn to_offset_utf16(&self, _snapshot: &BufferSnapshot) -> OffsetUtf16 { *self } } From 09e6d4487370aeebd290e88d68530eb048c2083f Mon Sep 17 00:00:00 2001 From: Julia Date: Wed, 23 Nov 2022 14:02:11 -0500 Subject: [PATCH 14/14] Move Unclipped into separate file --- crates/rope/src/rope.rs | 71 ++---------------------------------- crates/rope/src/unclipped.rs | 57 +++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 68 deletions(-) create mode 100644 crates/rope/src/unclipped.rs diff --git a/crates/rope/src/rope.rs b/crates/rope/src/rope.rs index 2a1268eab95f53e3e198fd7a889d33502cb05bde..d4ee894310a103cd8fd183da3202c756f5b85605 100644 --- a/crates/rope/src/rope.rs +++ b/crates/rope/src/rope.rs @@ -1,13 +1,14 @@ mod offset_utf16; mod point; mod point_utf16; +mod unclipped; use arrayvec::ArrayString; use bromberg_sl2::{DigestString, HashMatrix}; use smallvec::SmallVec; use std::{ cmp, fmt, io, mem, - ops::{Add, AddAssign, Range, Sub, SubAssign}, + ops::{AddAssign, Range}, str, }; use sum_tree::{Bias, Dimension, SumTree}; @@ -15,73 +16,7 @@ use sum_tree::{Bias, Dimension, SumTree}; pub use offset_utf16::OffsetUtf16; pub use point::Point; pub use point_utf16::PointUtf16; - -#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] -pub struct Unclipped(pub T); - -impl std::fmt::Debug for Unclipped { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.debug_tuple("Unclipped").field(&self.0).finish() - } -} - -impl Default for Unclipped { - fn default() -> Self { - Unclipped(T::default()) - } -} - -impl From for Unclipped { - fn from(value: T) -> Self { - Unclipped(value) - } -} - -impl<'a, T: sum_tree::Dimension<'a, ChunkSummary>> sum_tree::Dimension<'a, ChunkSummary> - for Unclipped -{ - fn add_summary(&mut self, summary: &'a ChunkSummary, _: &()) { - self.0.add_summary(summary, &()); - } -} - -impl TextDimension for Unclipped { - fn from_text_summary(summary: &TextSummary) -> Self { - Unclipped(T::from_text_summary(summary)) - } - - fn add_assign(&mut self, other: &Self) { - TextDimension::add_assign(&mut self.0, &other.0); - } -} - -impl> Add> for Unclipped { - type Output = Unclipped; - - fn add(self, rhs: Unclipped) -> Self::Output { - Unclipped(self.0 + rhs.0) - } -} - -impl> Sub> for Unclipped { - type Output = Unclipped; - - fn sub(self, rhs: Unclipped) -> Self::Output { - Unclipped(self.0 - rhs.0) - } -} - -impl> AddAssign> for Unclipped { - fn add_assign(&mut self, rhs: Unclipped) { - self.0 += rhs.0; - } -} - -impl> SubAssign> for Unclipped { - fn sub_assign(&mut self, rhs: Unclipped) { - self.0 -= rhs.0; - } -} +pub use unclipped::Unclipped; #[cfg(test)] const CHUNK_BASE: usize = 6; diff --git a/crates/rope/src/unclipped.rs b/crates/rope/src/unclipped.rs new file mode 100644 index 0000000000000000000000000000000000000000..937cbca0534d0ee8da7059bee79c6625e7d4a329 --- /dev/null +++ b/crates/rope/src/unclipped.rs @@ -0,0 +1,57 @@ +use crate::{ChunkSummary, TextDimension, TextSummary}; +use std::ops::{Add, AddAssign, Sub, SubAssign}; + +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct Unclipped(pub T); + +impl From for Unclipped { + fn from(value: T) -> Self { + Unclipped(value) + } +} + +impl<'a, T: sum_tree::Dimension<'a, ChunkSummary>> sum_tree::Dimension<'a, ChunkSummary> + for Unclipped +{ + fn add_summary(&mut self, summary: &'a ChunkSummary, _: &()) { + self.0.add_summary(summary, &()); + } +} + +impl TextDimension for Unclipped { + fn from_text_summary(summary: &TextSummary) -> Self { + Unclipped(T::from_text_summary(summary)) + } + + fn add_assign(&mut self, other: &Self) { + TextDimension::add_assign(&mut self.0, &other.0); + } +} + +impl> Add> for Unclipped { + type Output = Unclipped; + + fn add(self, rhs: Unclipped) -> Self::Output { + Unclipped(self.0 + rhs.0) + } +} + +impl> Sub> for Unclipped { + type Output = Unclipped; + + fn sub(self, rhs: Unclipped) -> Self::Output { + Unclipped(self.0 - rhs.0) + } +} + +impl> AddAssign> for Unclipped { + fn add_assign(&mut self, rhs: Unclipped) { + self.0 += rhs.0; + } +} + +impl> SubAssign> for Unclipped { + fn sub_assign(&mut self, rhs: Unclipped) { + self.0 -= rhs.0; + } +}