diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index ed76ee3b297162492bc808cc230f8c3224cba940..c684821649675aafc9199e66dc0fdb85d6915502 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -2700,7 +2700,7 @@ impl Editor { .update(&mut cx, |editor, cx| { editor.inlay_hint_cache.replace_hints( multi_buffer_handle, - replacement_queries.into_iter(), + replacement_queries, currently_shown_inlay_hints, cx, ) diff --git a/crates/editor/src/inlay_hint_cache.rs b/crates/editor/src/inlay_hint_cache.rs index 85ca62840d5ae0a43d717684a2be4da246f48231..72eb241e5a63581c3a8b4438a86954dc9c5ede82 100644 --- a/crates/editor/src/inlay_hint_cache.rs +++ b/crates/editor/src/inlay_hint_cache.rs @@ -1,4 +1,4 @@ -use std::ops::Range; +use std::{cmp, ops::Range}; use crate::{ editor_settings, scroll::ScrollAnchor, Anchor, Editor, ExcerptId, InlayId, MultiBuffer, @@ -63,6 +63,7 @@ pub struct InlaySplice { pub to_insert: Vec<(InlayId, Anchor, InlayHint)>, } +#[derive(Debug)] pub struct InlayHintQuery { pub buffer_id: u64, pub buffer_version: Global, @@ -186,6 +187,7 @@ impl InlayHintCache { ids_to_remove } + // TODO kb deduplicate into replace_hints? pub fn append_hints( &mut self, multi_buffer: ModelHandle, @@ -230,29 +232,44 @@ impl InlayHintCache { ) } for new_hint in new_excerpt_hints.hints { - let hint_anchor = multi_buffer_snapshot + let new_hint_anchor = multi_buffer_snapshot .anchor_in_excerpt(new_excerpt_id, new_hint.position); let insert_ix = match cached_excerpt_hints.hints.binary_search_by(|probe| { - hint_anchor.cmp(&probe.0, &multi_buffer_snapshot) + new_hint_anchor.cmp(&probe.0, &multi_buffer_snapshot) }) { - Ok(ix) | Err(ix) => ix, + Ok(ix) => { + let (_, cached_inlay_id) = cached_excerpt_hints.hints[ix]; + let cached_hint = editor + .inlay_hint_cache + .inlay_hints + .get(&cached_inlay_id) + .unwrap(); + if cached_hint == &new_hint { + None + } else { + Some(ix) + } + } + Err(ix) => Some(ix), }; - let new_hint_id = InlayId(post_inc(&mut editor.next_inlay_id)); - cached_excerpt_hints - .hints - .insert(insert_ix, (hint_anchor, new_hint_id)); - editor - .inlay_hint_cache - .inlay_hints - .insert(new_hint_id, new_hint.clone()); - if editor - .inlay_hint_cache - .allowed_hint_kinds - .contains(&new_hint.kind) - { - to_insert.push((new_hint_id, hint_anchor, new_hint)); + if let Some(insert_ix) = insert_ix { + let new_hint_id = InlayId(post_inc(&mut editor.next_inlay_id)); + cached_excerpt_hints + .hints + .insert(insert_ix, (new_hint_anchor, new_hint_id)); + editor + .inlay_hint_cache + .inlay_hints + .insert(new_hint_id, new_hint.clone()); + if editor + .inlay_hint_cache + .allowed_hint_kinds + .contains(&new_hint.kind) + { + to_insert.push((new_hint_id, new_hint_anchor, new_hint)); + } } } } @@ -269,11 +286,11 @@ impl InlayHintCache { pub fn replace_hints( &mut self, multi_buffer: ModelHandle, - mut range_updates: impl Iterator, + range_updates: Vec, mut currently_shown_hints: HashMap>>, cx: &mut ViewContext, ) -> Task> { - let conflicts_with_cache = range_updates.any(|update_query| { + let conflicts_with_cache = range_updates.iter().any(|update_query| { let Some(cached_buffer_hints) = self.hints_in_buffers.get(&update_query.buffer_id) else { return false }; if cached_buffer_hints @@ -293,7 +310,11 @@ impl InlayHintCache { } }); - let queries = filter_queries(range_updates, &self.hints_in_buffers, conflicts_with_cache); + let queries = filter_queries( + range_updates.into_iter(), + &self.hints_in_buffers, + conflicts_with_cache, + ); let task_multi_buffer = multi_buffer.clone(); let fetch_queries_task = fetch_queries(multi_buffer, queries.into_iter(), cx); let mut to_remove = Vec::new(); @@ -329,37 +350,115 @@ impl InlayHintCache { .hints_per_excerpt .entry(new_excerpt_id) .or_default(); - let shown_excerpt_hints = shown_buffer_hints + let mut shown_excerpt_hints = shown_buffer_hints .remove(&new_excerpt_id) - .unwrap_or_default(); - + .unwrap_or_default() + .into_iter() + .fuse() + .peekable(); if conflicts_with_cache { cached_excerpt_hints.cached_excerpt_offsets.clear(); - // TODO kb need to add such into to_delete and do not cause extra changes - // cached_excerpt_hints.hints.clear(); - // editor.inlay_hint_cache.inlay_hints.clear(); - todo!("TODO kb") - } else { - for new_range in new_hints_per_excerpt.cached_excerpt_offsets { - insert_and_merge_ranges( - &mut cached_excerpt_hints.cached_excerpt_offsets, - &new_range, - ) - } - for new_hint in new_hints_per_excerpt.hints { - let hint_anchor = multi_buffer_snapshot - .anchor_in_excerpt(new_excerpt_id, new_hint.position); + cached_excerpt_hints.hints.clear(); + } + + for new_hint in new_hints_per_excerpt.hints { + let new_hint_anchor = multi_buffer_snapshot + .anchor_in_excerpt(new_excerpt_id, new_hint.position); + + let insert_ix = if conflicts_with_cache { + let mut no_matching_inlay_displayed = true; + loop { + match shown_excerpt_hints.peek() { + Some((shown_anchor, shown_id)) => { + match shown_anchor + .cmp(&new_hint_anchor, &multi_buffer_snapshot) + { + cmp::Ordering::Less => { + editor + .inlay_hint_cache + .inlay_hints + .remove(shown_id); + to_remove.push(*shown_id); + shown_excerpt_hints.next(); + } + cmp::Ordering::Equal => { + match editor + .inlay_hint_cache + .inlay_hints + .get(shown_id) + { + Some(cached_hint) + if cached_hint == &new_hint => + { + no_matching_inlay_displayed = false; + } + _ => to_remove.push(*shown_id), + } + shown_excerpt_hints.next(); + break; + } + cmp::Ordering::Greater => break, + } + } + None => break, + } + } + + if no_matching_inlay_displayed { + let insert_ix = + match cached_excerpt_hints.hints.binary_search_by(|probe| { + new_hint_anchor.cmp(&probe.0, &multi_buffer_snapshot) + }) { + Ok(ix) => { + let (_, cached_inlay_id) = + cached_excerpt_hints.hints[ix]; + let cached_hint = editor + .inlay_hint_cache + .inlay_hints + .get(&cached_inlay_id) + .unwrap(); + if cached_hint == &new_hint { + None + } else { + Some(ix) + } + } + Err(ix) => Some(ix), + }; + insert_ix + } else { + None + } + } else { let insert_ix = match cached_excerpt_hints.hints.binary_search_by(|probe| { - hint_anchor.cmp(&probe.0, &multi_buffer_snapshot) + new_hint_anchor.cmp(&probe.0, &multi_buffer_snapshot) }) { - Ok(ix) | Err(ix) => ix, + Ok(ix) => { + let (_, cached_inlay_id) = + cached_excerpt_hints.hints[ix]; + let cached_hint = editor + .inlay_hint_cache + .inlay_hints + .get(&cached_inlay_id) + .unwrap(); + if cached_hint == &new_hint { + None + } else { + Some(ix) + } + } + Err(ix) => Some(ix), }; + insert_ix + }; + + if let Some(insert_ix) = insert_ix { let new_hint_id = InlayId(post_inc(&mut editor.next_inlay_id)); cached_excerpt_hints .hints - .insert(insert_ix, (hint_anchor, new_hint_id)); + .insert(insert_ix, (new_hint_anchor, new_hint_id)); editor .inlay_hint_cache .inlay_hints @@ -369,11 +468,18 @@ impl InlayHintCache { .allowed_hint_kinds .contains(&new_hint.kind) { - to_insert.push((new_hint_id, hint_anchor, new_hint)); + to_insert.push((new_hint_id, new_hint_anchor, new_hint)); } } } + for new_range in new_hints_per_excerpt.cached_excerpt_offsets { + insert_and_merge_ranges( + &mut cached_excerpt_hints.cached_excerpt_offsets, + &new_range, + ) + } + if cached_excerpt_hints.hints.is_empty() { cached_buffer_hints .hints_per_excerpt @@ -521,7 +627,7 @@ fn insert_and_merge_ranges(cache: &mut Vec>, new_range: &Range 0 && cache[index - 1].end >= new_range.start { // Merge with the previous range - cache[index - 1].end = std::cmp::max(cache[index - 1].end, new_range.end); + cache[index - 1].end = cmp::max(cache[index - 1].end, new_range.end); } else { // Insert the new range, as it doesn't overlap with the previous range cache.insert(index, new_range.clone()); @@ -530,7 +636,7 @@ fn insert_and_merge_ranges(cache: &mut Vec>, new_range: &Range= cache[i + 1].start { - cache[i].end = std::cmp::max(cache[i].end, cache[i + 1].end); + cache[i].end = cmp::max(cache[i].end, cache[i + 1].end); cache.remove(i + 1); i += 1; }