Fix InlayMap bugs after the map order revers

Co-Authored-By: Antonio Scandurra <antonio@zed.dev>
This commit is contained in:
Kirill Bulatov 2023-06-16 12:02:48 +03:00
parent 05dc672c2a
commit 9ae611fa89

View file

@ -1,7 +1,7 @@
use crate::{ use crate::{
inlay_cache::{Inlay, InlayId, InlayProperties}, inlay_cache::{Inlay, InlayId, InlayProperties},
multi_buffer::{MultiBufferChunks, MultiBufferRows}, multi_buffer::{MultiBufferChunks, MultiBufferRows},
MultiBufferSnapshot, ToPoint, MultiBufferSnapshot, ToOffset,
}; };
use collections::{BTreeSet, HashMap}; use collections::{BTreeSet, HashMap};
use gpui::fonts::HighlightStyle; use gpui::fonts::HighlightStyle;
@ -16,11 +16,9 @@ use text::Patch;
use util::post_inc; use util::post_inc;
pub struct InlayMap { pub struct InlayMap {
buffer: Mutex<MultiBufferSnapshot>, snapshot: Mutex<InlaySnapshot>,
transforms: SumTree<Transform>,
inlays_by_id: HashMap<InlayId, Inlay>, inlays_by_id: HashMap<InlayId, Inlay>,
inlays: Vec<Inlay>, inlays: Vec<Inlay>,
version: usize,
} }
#[derive(Clone)] #[derive(Clone)]
@ -241,11 +239,9 @@ impl InlayMap {
( (
Self { Self {
buffer: Mutex::new(buffer), snapshot: Mutex::new(snapshot.clone()),
transforms: snapshot.transforms.clone(),
inlays_by_id: Default::default(), inlays_by_id: Default::default(),
inlays: Default::default(), inlays: Default::default(),
version,
}, },
snapshot, snapshot,
) )
@ -254,36 +250,40 @@ impl InlayMap {
pub fn sync( pub fn sync(
&mut self, &mut self,
buffer_snapshot: MultiBufferSnapshot, buffer_snapshot: MultiBufferSnapshot,
buffer_edits: Vec<text::Edit<usize>>, mut buffer_edits: Vec<text::Edit<usize>>,
) -> (InlaySnapshot, Vec<InlayEdit>) { ) -> (InlaySnapshot, Vec<InlayEdit>) {
let mut buffer = self.buffer.lock(); let mut snapshot = self.snapshot.lock();
if buffer_edits.is_empty() { if buffer_edits.is_empty() {
let new_version = if buffer.edit_count() != buffer_snapshot.edit_count() if snapshot.buffer.trailing_excerpt_update_count()
|| buffer.parse_count() != buffer_snapshot.parse_count() != buffer_snapshot.trailing_excerpt_update_count()
|| buffer.diagnostics_update_count() != buffer_snapshot.diagnostics_update_count() {
|| buffer.git_diff_update_count() != buffer_snapshot.git_diff_update_count() buffer_edits.push(Edit {
|| buffer.trailing_excerpt_update_count() old: snapshot.buffer.len()..snapshot.buffer.len(),
new: buffer_snapshot.len()..buffer_snapshot.len(),
});
}
}
if buffer_edits.is_empty() {
if snapshot.buffer.edit_count() != buffer_snapshot.edit_count()
|| snapshot.buffer.parse_count() != buffer_snapshot.parse_count()
|| snapshot.buffer.diagnostics_update_count()
!= buffer_snapshot.diagnostics_update_count()
|| snapshot.buffer.git_diff_update_count()
!= buffer_snapshot.git_diff_update_count()
|| snapshot.buffer.trailing_excerpt_update_count()
!= buffer_snapshot.trailing_excerpt_update_count() != buffer_snapshot.trailing_excerpt_update_count()
{ {
post_inc(&mut self.version) snapshot.version += 1;
} else { }
self.version
};
*buffer = buffer_snapshot.clone(); snapshot.buffer = buffer_snapshot;
( (snapshot.clone(), Vec::new())
InlaySnapshot {
buffer: buffer_snapshot,
transforms: SumTree::default(),
version: new_version,
},
Vec::new(),
)
} else { } else {
let mut inlay_edits = Patch::default(); let mut inlay_edits = Patch::default();
let mut new_transforms = SumTree::new(); let mut new_transforms = SumTree::new();
let transforms = &mut self.transforms; let mut cursor = snapshot.transforms.cursor::<(usize, InlayOffset)>();
let mut cursor = transforms.cursor::<(usize, InlayOffset)>();
let mut buffer_edits_iter = buffer_edits.iter().peekable(); let mut buffer_edits_iter = buffer_edits.iter().peekable();
while let Some(buffer_edit) = buffer_edits_iter.next() { while let Some(buffer_edit) = buffer_edits_iter.next() {
new_transforms new_transforms
@ -311,20 +311,18 @@ impl InlayMap {
); );
let new_start = InlayOffset(new_transforms.summary().output.len); let new_start = InlayOffset(new_transforms.summary().output.len);
let start_point = buffer_edit.new.start.to_point(&buffer_snapshot);
let start_ix = match self.inlays.binary_search_by(|probe| { let start_ix = match self.inlays.binary_search_by(|probe| {
probe probe
.position .position
.to_point(&buffer_snapshot) .to_offset(&buffer_snapshot)
.cmp(&start_point) .cmp(&buffer_edit.new.start)
.then(std::cmp::Ordering::Greater) .then(std::cmp::Ordering::Greater)
}) { }) {
Ok(ix) | Err(ix) => ix, Ok(ix) | Err(ix) => ix,
}; };
for inlay in &self.inlays[start_ix..] { for inlay in &self.inlays[start_ix..] {
let buffer_point = inlay.position.to_point(&buffer_snapshot); let buffer_offset = inlay.position.to_offset(&buffer_snapshot);
let buffer_offset = buffer_snapshot.point_to_offset(buffer_point);
if buffer_offset > buffer_edit.new.end { if buffer_offset > buffer_edit.new.end {
break; break;
} }
@ -375,17 +373,13 @@ impl InlayMap {
new_transforms.push(Transform::Isomorphic(Default::default()), &()); new_transforms.push(Transform::Isomorphic(Default::default()), &());
} }
let new_snapshot = InlaySnapshot {
buffer: buffer_snapshot.clone(),
transforms: new_transforms,
version: post_inc(&mut self.version),
};
new_snapshot.check_invariants();
drop(cursor); drop(cursor);
snapshot.transforms = new_transforms;
snapshot.version += 1;
snapshot.buffer = buffer_snapshot;
snapshot.check_invariants();
// TODO kb remove the 2nd buffer here, leave it in snapshot only? (snapshot.clone(), inlay_edits.into_inner())
*buffer = buffer_snapshot.clone();
(new_snapshot, inlay_edits.into_inner())
} }
} }
@ -394,15 +388,14 @@ impl InlayMap {
to_remove: Vec<InlayId>, to_remove: Vec<InlayId>,
to_insert: Vec<(InlayId, InlayProperties<T>)>, to_insert: Vec<(InlayId, InlayProperties<T>)>,
) -> (InlaySnapshot, Vec<InlayEdit>) { ) -> (InlaySnapshot, Vec<InlayEdit>) {
let mut buffer_snapshot = self.buffer.lock(); let snapshot = self.snapshot.lock();
let mut edits = BTreeSet::new(); let mut edits = BTreeSet::new();
self.inlays.retain(|inlay| !to_remove.contains(&inlay.id)); self.inlays.retain(|inlay| !to_remove.contains(&inlay.id));
for inlay_id in to_remove { for inlay_id in to_remove {
if let Some(inlay) = self.inlays_by_id.remove(&inlay_id) { if let Some(inlay) = self.inlays_by_id.remove(&inlay_id) {
let buffer_point = inlay.position.to_point(&buffer_snapshot); let offset = inlay.position.to_offset(&snapshot.buffer);
let buffer_offset = buffer_snapshot.point_to_offset(buffer_point); edits.insert(offset);
edits.insert(buffer_offset);
} }
} }
@ -415,16 +408,15 @@ impl InlayMap {
self.inlays_by_id.insert(inlay.id, inlay.clone()); self.inlays_by_id.insert(inlay.id, inlay.clone());
match self match self
.inlays .inlays
.binary_search_by(|probe| probe.position.cmp(&inlay.position, &buffer_snapshot)) .binary_search_by(|probe| probe.position.cmp(&inlay.position, &snapshot.buffer))
{ {
Ok(ix) | Err(ix) => { Ok(ix) | Err(ix) => {
self.inlays.insert(ix, inlay.clone()); self.inlays.insert(ix, inlay.clone());
} }
} }
let buffer_point = inlay.position.to_point(&buffer_snapshot); let offset = inlay.position.to_offset(&snapshot.buffer);
let buffer_offset = buffer_snapshot.point_to_offset(buffer_point); edits.insert(offset);
edits.insert(buffer_offset);
} }
let buffer_edits = edits let buffer_edits = edits
@ -434,10 +426,9 @@ impl InlayMap {
new: offset..offset, new: offset..offset,
}) })
.collect(); .collect();
// TODO kb fugly let buffer_snapshot = snapshot.buffer.clone();
let buffer_snapshot_to_sync = buffer_snapshot.clone(); drop(snapshot);
drop(buffer_snapshot); self.sync(buffer_snapshot, buffer_edits)
self.sync(buffer_snapshot_to_sync, buffer_edits)
} }
#[cfg(any(test, feature = "test-support"))] #[cfg(any(test, feature = "test-support"))]
@ -450,10 +441,10 @@ impl InlayMap {
let mut to_remove = Vec::new(); let mut to_remove = Vec::new();
let mut to_insert = Vec::new(); let mut to_insert = Vec::new();
let buffer_snapshot = self.buffer.lock(); let mut snapshot = self.snapshot.lock();
for _ in 0..rng.gen_range(1..=5) { for _ in 0..rng.gen_range(1..=5) {
if self.inlays.is_empty() || rng.gen() { if self.inlays.is_empty() || rng.gen() {
let position = buffer_snapshot.random_byte_range(0, rng).start; let position = snapshot.buffer.random_byte_range(0, rng).start;
let bias = if rng.gen() { Bias::Left } else { Bias::Right }; let bias = if rng.gen() { Bias::Left } else { Bias::Right };
let len = rng.gen_range(1..=5); let len = rng.gen_range(1..=5);
let text = util::RandomCharIter::new(&mut *rng) let text = util::RandomCharIter::new(&mut *rng)
@ -469,7 +460,7 @@ impl InlayMap {
to_insert.push(( to_insert.push((
InlayId(post_inc(next_inlay_id)), InlayId(post_inc(next_inlay_id)),
InlayProperties { InlayProperties {
position: buffer_snapshot.anchor_at(position, bias), position: snapshot.buffer.anchor_at(position, bias),
text, text,
}, },
)); ));
@ -479,7 +470,7 @@ impl InlayMap {
} }
log::info!("removing inlays: {:?}", to_remove); log::info!("removing inlays: {:?}", to_remove);
drop(buffer_snapshot); drop(snapshot);
self.splice(to_remove, to_insert) self.splice(to_remove, to_insert)
} }
} }
@ -569,15 +560,12 @@ impl InlaySnapshot {
} }
pub fn to_inlay_offset(&self, offset: usize) -> InlayOffset { pub fn to_inlay_offset(&self, offset: usize) -> InlayOffset {
let mut cursor = self.transforms.cursor::<(Point, InlayOffset)>(); let mut cursor = self.transforms.cursor::<(usize, InlayOffset)>();
// TODO kb is this right? cursor.seek(&offset, Bias::Left, &());
let buffer_point = self.buffer.offset_to_point(offset);
cursor.seek(&buffer_point, Bias::Left, &());
match cursor.item() { match cursor.item() {
Some(Transform::Isomorphic(_)) => { Some(Transform::Isomorphic(_)) => {
let overshoot = buffer_point - cursor.start().0; let overshoot = offset - cursor.start().0;
let overshoot_offset = self.to_inlay_offset(self.buffer.point_to_offset(overshoot)); InlayOffset(cursor.start().1 .0 + overshoot)
cursor.start().1 + overshoot_offset
} }
Some(Transform::Inlay(_)) => cursor.start().1, Some(Transform::Inlay(_)) => cursor.start().1,
None => self.len(), None => self.len(),
@ -922,7 +910,7 @@ mod tests {
} }
#[gpui::test] #[gpui::test]
fn test_buffer_rows(cx: &mut AppContext) { fn test_inlay_buffer_rows(cx: &mut AppContext) {
let buffer = MultiBuffer::build_simple("abc\ndef\nghi", cx); let buffer = MultiBuffer::build_simple("abc\ndef\nghi", cx);
let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer.read(cx).snapshot(cx)); let (mut inlay_map, inlay_snapshot) = InlayMap::new(buffer.read(cx).snapshot(cx));
assert_eq!(inlay_snapshot.text(), "abc\ndef\nghi"); assert_eq!(inlay_snapshot.text(), "abc\ndef\nghi");
@ -1018,9 +1006,8 @@ mod tests {
.iter() .iter()
.filter(|inlay| inlay.position.is_valid(&buffer_snapshot)) .filter(|inlay| inlay.position.is_valid(&buffer_snapshot))
.map(|inlay| { .map(|inlay| {
let buffer_point = inlay.position.to_point(&buffer_snapshot); let offset = inlay.position.to_offset(&buffer_snapshot);
let buffer_offset = buffer_snapshot.point_to_offset(buffer_point); (offset, inlay.clone())
(buffer_offset, inlay.clone())
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
let mut expected_text = Rope::from(buffer_snapshot.text().as_str()); let mut expected_text = Rope::from(buffer_snapshot.text().as_str());