Support better inlay cache parallelization

This commit is contained in:
Kirill Bulatov 2023-06-24 01:08:20 +03:00
parent 890b164278
commit 83b3a914bc
2 changed files with 198 additions and 203 deletions

View file

@ -7891,11 +7891,11 @@ async fn test_mutual_editor_inlay_hint_cache_update(
editor_a.update(cx_a, |_, cx| cx.focus(&editor_a)); editor_a.update(cx_a, |_, cx| cx.focus(&editor_a));
cx_a.foreground().run_until_parked(); cx_a.foreground().run_until_parked();
editor_a.update(cx_a, |editor, _| { editor_a.update(cx_a, |editor, _| {
let inlay_cache = editor.inlay_hint_cache().snapshot();
assert!( assert!(
inlay_cache.hints.is_empty(), extract_hint_labels(editor).is_empty(),
"No inlays should be in the new cache" "No inlays should be in the new cache"
); );
let inlay_cache = editor.inlay_hint_cache();
assert_eq!( assert_eq!(
inlay_cache.allowed_hint_kinds, allowed_hint_kinds, inlay_cache.allowed_hint_kinds, allowed_hint_kinds,
"Cache should use editor settings to get the allowed hint kinds" "Cache should use editor settings to get the allowed hint kinds"
@ -7918,11 +7918,11 @@ async fn test_mutual_editor_inlay_hint_cache_update(
editor_b.update(cx_b, |_, cx| cx.focus(&editor_b)); editor_b.update(cx_b, |_, cx| cx.focus(&editor_b));
cx_b.foreground().run_until_parked(); cx_b.foreground().run_until_parked();
editor_b.update(cx_b, |editor, _| { editor_b.update(cx_b, |editor, _| {
let inlay_cache = editor.inlay_hint_cache().snapshot();
assert!( assert!(
inlay_cache.hints.is_empty(), extract_hint_labels(editor).is_empty(),
"No inlays should be in the new cache" "No inlays should be in the new cache"
); );
let inlay_cache = editor.inlay_hint_cache();
assert_eq!( assert_eq!(
inlay_cache.allowed_hint_kinds, allowed_hint_kinds, inlay_cache.allowed_hint_kinds, allowed_hint_kinds,
"Cache should use editor settings to get the allowed hint kinds" "Cache should use editor settings to get the allowed hint kinds"
@ -7978,32 +7978,27 @@ async fn test_mutual_editor_inlay_hint_cache_update(
cx_a.foreground().finish_waiting(); cx_a.foreground().finish_waiting();
cx_a.foreground().run_until_parked(); cx_a.foreground().run_until_parked();
fn extract_hint_labels(editor: &Editor) -> Vec<&str> { fn extract_hint_labels(editor: &Editor) -> Vec<String> {
editor let mut labels = Vec::new();
.inlay_hint_cache() for (_, excerpt_hints) in &editor.inlay_hint_cache().hints {
.snapshot() let excerpt_hints = excerpt_hints.read();
.hints for (_, inlay) in excerpt_hints.hints.iter() {
.iter() match &inlay.label {
.map(|(_, excerpt_hints)| { project::InlayHintLabel::String(s) => labels.push(s.to_string()),
excerpt_hints
.hints
.iter()
.map(|(_, inlay)| match &inlay.label {
project::InlayHintLabel::String(s) => s.as_str(),
_ => unreachable!(), _ => unreachable!(),
}) }
}) }
.flatten() }
.collect::<Vec<_>>() labels
} }
editor_a.update(cx_a, |editor, _| { editor_a.update(cx_a, |editor, _| {
assert_eq!( assert_eq!(
vec!["0"], vec!["0".to_string()],
extract_hint_labels(editor), extract_hint_labels(editor),
"Host should get hints from the 1st edit and 1st LSP query" "Host should get hints from the 1st edit and 1st LSP query"
); );
let inlay_cache = editor.inlay_hint_cache().snapshot(); let inlay_cache = editor.inlay_hint_cache();
assert_eq!(inlay_cache.allowed_hint_kinds, allowed_hint_kinds, "Inlay kinds settings never change during the test"); assert_eq!(inlay_cache.allowed_hint_kinds, allowed_hint_kinds, "Inlay kinds settings never change during the test");
assert_eq!( assert_eq!(
inlay_cache.version, edits_made, inlay_cache.version, edits_made,
@ -8012,11 +8007,11 @@ async fn test_mutual_editor_inlay_hint_cache_update(
}); });
editor_b.update(cx_b, |editor, _| { editor_b.update(cx_b, |editor, _| {
assert_eq!( assert_eq!(
vec!["0", "1"], vec!["0".to_string(), "1".to_string()],
extract_hint_labels(editor), extract_hint_labels(editor),
"Guest should get hints the 1st edit and 2nd LSP query" "Guest should get hints the 1st edit and 2nd LSP query"
); );
let inlay_cache = editor.inlay_hint_cache().snapshot(); let inlay_cache = editor.inlay_hint_cache();
assert_eq!(inlay_cache.allowed_hint_kinds, allowed_hint_kinds, "Inlay kinds settings never change during the test"); assert_eq!(inlay_cache.allowed_hint_kinds, allowed_hint_kinds, "Inlay kinds settings never change during the test");
assert_eq!( assert_eq!(
inlay_cache.version, edits_made, inlay_cache.version, edits_made,
@ -8034,12 +8029,12 @@ async fn test_mutual_editor_inlay_hint_cache_update(
cx_b.foreground().run_until_parked(); cx_b.foreground().run_until_parked();
editor_a.update(cx_a, |editor, _| { editor_a.update(cx_a, |editor, _| {
assert_eq!( assert_eq!(
vec!["0", "1", "2"], vec!["0".to_string(), "1".to_string(), "2".to_string()],
extract_hint_labels(editor), extract_hint_labels(editor),
"Host should get hints from 3rd edit, 5th LSP query: \ "Host should get hints from 3rd edit, 5th LSP query: \
4th query was made by guest (but not applied) due to cache invalidation logic" 4th query was made by guest (but not applied) due to cache invalidation logic"
); );
let inlay_cache = editor.inlay_hint_cache().snapshot(); let inlay_cache = editor.inlay_hint_cache();
assert_eq!(inlay_cache.allowed_hint_kinds, allowed_hint_kinds, "Inlay kinds settings never change during the test"); assert_eq!(inlay_cache.allowed_hint_kinds, allowed_hint_kinds, "Inlay kinds settings never change during the test");
assert_eq!( assert_eq!(
inlay_cache.version, edits_made, inlay_cache.version, edits_made,
@ -8048,11 +8043,16 @@ async fn test_mutual_editor_inlay_hint_cache_update(
}); });
editor_b.update(cx_b, |editor, _| { editor_b.update(cx_b, |editor, _| {
assert_eq!( assert_eq!(
vec!["0", "1", "2", "3"], vec![
"0".to_string(),
"1".to_string(),
"2".to_string(),
"3".to_string()
],
extract_hint_labels(editor), extract_hint_labels(editor),
"Guest should get hints from 3rd edit, 6th LSP query" "Guest should get hints from 3rd edit, 6th LSP query"
); );
let inlay_cache = editor.inlay_hint_cache().snapshot(); let inlay_cache = editor.inlay_hint_cache();
assert_eq!( assert_eq!(
inlay_cache.allowed_hint_kinds, allowed_hint_kinds, inlay_cache.allowed_hint_kinds, allowed_hint_kinds,
"Inlay kinds settings never change during the test" "Inlay kinds settings never change during the test"
@ -8072,11 +8072,17 @@ async fn test_mutual_editor_inlay_hint_cache_update(
cx_b.foreground().run_until_parked(); cx_b.foreground().run_until_parked();
editor_a.update(cx_a, |editor, _| { editor_a.update(cx_a, |editor, _| {
assert_eq!( assert_eq!(
vec!["0", "1", "2", "3", "4"], vec![
"0".to_string(),
"1".to_string(),
"2".to_string(),
"3".to_string(),
"4".to_string()
],
extract_hint_labels(editor), extract_hint_labels(editor),
"Host should react to /refresh LSP request and get new hints from 7th LSP query" "Host should react to /refresh LSP request and get new hints from 7th LSP query"
); );
let inlay_cache = editor.inlay_hint_cache().snapshot(); let inlay_cache = editor.inlay_hint_cache();
assert_eq!( assert_eq!(
inlay_cache.allowed_hint_kinds, allowed_hint_kinds, inlay_cache.allowed_hint_kinds, allowed_hint_kinds,
"Inlay kinds settings never change during the test" "Inlay kinds settings never change during the test"
@ -8088,11 +8094,11 @@ async fn test_mutual_editor_inlay_hint_cache_update(
}); });
editor_b.update(cx_b, |editor, _| { editor_b.update(cx_b, |editor, _| {
assert_eq!( assert_eq!(
vec!["0", "1", "2", "3", "4", "5"], vec!["0".to_string(), "1".to_string(), "2".to_string(), "3".to_string(), "4".to_string(), "5".to_string()],
extract_hint_labels(editor), extract_hint_labels(editor),
"Guest should get a /refresh LSP request propagated by host and get new hints from 8th LSP query" "Guest should get a /refresh LSP request propagated by host and get new hints from 8th LSP query"
); );
let inlay_cache = editor.inlay_hint_cache().snapshot(); let inlay_cache = editor.inlay_hint_cache();
assert_eq!( assert_eq!(
inlay_cache.allowed_hint_kinds, allowed_hint_kinds, inlay_cache.allowed_hint_kinds, allowed_hint_kinds,
"Inlay kinds settings never change during the test" "Inlay kinds settings never change during the test"

View file

@ -9,13 +9,16 @@ use clock::Global;
use gpui::{ModelHandle, Task, ViewContext}; use gpui::{ModelHandle, Task, ViewContext};
use language::{Buffer, BufferSnapshot}; use language::{Buffer, BufferSnapshot};
use log::error; use log::error;
use parking_lot::RwLock;
use project::{InlayHint, InlayHintKind}; use project::{InlayHint, InlayHintKind};
use collections::{hash_map, HashMap, HashSet}; use collections::{hash_map, HashMap, HashSet};
use util::post_inc; use util::post_inc;
pub struct InlayHintCache { pub struct InlayHintCache {
snapshot: CacheSnapshot, pub hints: HashMap<ExcerptId, Arc<RwLock<CachedExcerptHints>>>,
pub allowed_hint_kinds: HashSet<Option<InlayHintKind>>,
pub version: usize,
update_tasks: HashMap<ExcerptId, InlayHintUpdateTask>, update_tasks: HashMap<ExcerptId, InlayHintUpdateTask>,
} }
@ -24,13 +27,6 @@ struct InlayHintUpdateTask {
_task: Task<()>, _task: Task<()>,
} }
#[derive(Debug)]
pub struct CacheSnapshot {
pub hints: HashMap<ExcerptId, Arc<CachedExcerptHints>>,
pub allowed_hint_kinds: HashSet<Option<InlayHintKind>>,
pub version: usize,
}
#[derive(Debug)] #[derive(Debug)]
pub struct CachedExcerptHints { pub struct CachedExcerptHints {
version: usize, version: usize,
@ -84,19 +80,13 @@ struct ExcerptHintsUpdate {
impl InlayHintCache { impl InlayHintCache {
pub fn new(inlay_hint_settings: editor_settings::InlayHints) -> Self { pub fn new(inlay_hint_settings: editor_settings::InlayHints) -> Self {
Self { Self {
snapshot: CacheSnapshot {
allowed_hint_kinds: allowed_hint_types(inlay_hint_settings), allowed_hint_kinds: allowed_hint_types(inlay_hint_settings),
hints: HashMap::default(), hints: HashMap::default(),
version: 0,
},
update_tasks: HashMap::default(), update_tasks: HashMap::default(),
version: 0,
} }
} }
pub fn snapshot(&self) -> &CacheSnapshot {
&self.snapshot
}
pub fn update_settings( pub fn update_settings(
&mut self, &mut self,
multi_buffer: &ModelHandle<MultiBuffer>, multi_buffer: &ModelHandle<MultiBuffer>,
@ -106,38 +96,34 @@ impl InlayHintCache {
) -> Option<InlaySplice> { ) -> Option<InlaySplice> {
let new_allowed_hint_kinds = allowed_hint_types(inlay_hint_settings); let new_allowed_hint_kinds = allowed_hint_types(inlay_hint_settings);
if !inlay_hint_settings.enabled { if !inlay_hint_settings.enabled {
if self.snapshot.hints.is_empty() { if self.hints.is_empty() {
self.snapshot.allowed_hint_kinds = new_allowed_hint_kinds; self.allowed_hint_kinds = new_allowed_hint_kinds;
None
} else { } else {
self.clear(); self.clear();
self.snapshot.allowed_hint_kinds = new_allowed_hint_kinds; self.allowed_hint_kinds = new_allowed_hint_kinds;
return Some(InlaySplice { Some(InlaySplice {
to_remove: visible_hints.iter().map(|inlay| inlay.id).collect(), to_remove: visible_hints.iter().map(|inlay| inlay.id).collect(),
to_insert: Vec::new(), to_insert: Vec::new(),
}); })
} }
} else if new_allowed_hint_kinds == self.allowed_hint_kinds {
return None; None
} } else {
let new_splice = self.new_allowed_hint_kinds_splice(
if new_allowed_hint_kinds == self.snapshot.allowed_hint_kinds {
return None;
}
let new_splice = new_allowed_hint_kinds_splice(
&self.snapshot,
multi_buffer, multi_buffer,
&visible_hints, &visible_hints,
&new_allowed_hint_kinds, &new_allowed_hint_kinds,
cx, cx,
); );
if new_splice.is_some() { if new_splice.is_some() {
self.snapshot.version += 1; self.version += 1;
self.update_tasks.clear(); self.update_tasks.clear();
self.snapshot.allowed_hint_kinds = new_allowed_hint_kinds; self.allowed_hint_kinds = new_allowed_hint_kinds;
} }
new_splice new_splice
} }
}
pub fn spawn_hints_update( pub fn spawn_hints_update(
&mut self, &mut self,
@ -154,9 +140,10 @@ impl InlayHintCache {
update_tasks update_tasks
.retain(|task_excerpt_id, _| excerpts_to_query.contains_key(task_excerpt_id)); .retain(|task_excerpt_id, _| excerpts_to_query.contains_key(task_excerpt_id));
} }
let cache_version = self.version;
excerpts_to_query.retain(|visible_excerpt_id, _| { excerpts_to_query.retain(|visible_excerpt_id, _| {
match update_tasks.entry(*visible_excerpt_id) { match update_tasks.entry(*visible_excerpt_id) {
hash_map::Entry::Occupied(o) => match o.get().version.cmp(&self.snapshot.version) { hash_map::Entry::Occupied(o) => match o.get().version.cmp(&cache_version) {
cmp::Ordering::Less => true, cmp::Ordering::Less => true,
cmp::Ordering::Equal => invalidate_cache, cmp::Ordering::Equal => invalidate_cache,
cmp::Ordering::Greater => false, cmp::Ordering::Greater => false,
@ -169,7 +156,6 @@ impl InlayHintCache {
update_tasks update_tasks
.retain(|task_excerpt_id, _| excerpts_to_query.contains_key(task_excerpt_id)); .retain(|task_excerpt_id, _| excerpts_to_query.contains_key(task_excerpt_id));
} }
let cache_version = self.snapshot.version;
excerpts_to_query.retain(|visible_excerpt_id, _| { excerpts_to_query.retain(|visible_excerpt_id, _| {
match update_tasks.entry(*visible_excerpt_id) { match update_tasks.entry(*visible_excerpt_id) {
hash_map::Entry::Occupied(o) => match o.get().version.cmp(&cache_version) { hash_map::Entry::Occupied(o) => match o.get().version.cmp(&cache_version) {
@ -211,15 +197,12 @@ impl InlayHintCache {
cache_version, cache_version,
invalidate, invalidate,
}; };
let cached_excxerpt_hints = editor let cached_excxerpt_hints =
.inlay_hint_cache editor.inlay_hint_cache.hints.get(&excerpt_id).cloned();
.snapshot
.hints
.get(&excerpt_id)
.cloned();
if let Some(cached_excerpt_hints) = &cached_excxerpt_hints { if let Some(cached_excerpt_hints) = &cached_excxerpt_hints {
let new_task_buffer_version = buffer_snapshot.version(); let new_task_buffer_version = buffer_snapshot.version();
let cached_excerpt_hints = cached_excerpt_hints.read();
let cached_buffer_version = &cached_excerpt_hints.buffer_version; let cached_buffer_version = &cached_excerpt_hints.buffer_version;
if cached_buffer_version.changed_since(new_task_buffer_version) { if cached_buffer_version.changed_since(new_task_buffer_version) {
return; return;
@ -250,132 +233,14 @@ impl InlayHintCache {
.detach(); .detach();
} }
fn clear(&mut self) {
self.snapshot.version += 1;
self.update_tasks.clear();
self.snapshot.hints.clear();
self.snapshot.allowed_hint_kinds.clear();
}
}
fn new_update_task(
query: ExcerptQuery,
multi_buffer_snapshot: MultiBufferSnapshot,
buffer_snapshot: BufferSnapshot,
visible_hints: Arc<Vec<Inlay>>,
cached_excerpt_hints: Option<Arc<CachedExcerptHints>>,
cx: &mut ViewContext<'_, '_, Editor>,
) -> InlayHintUpdateTask {
let hints_fetch_task = hints_fetch_task(query, cx);
InlayHintUpdateTask {
version: query.cache_version,
_task: cx.spawn(|editor, mut cx| async move {
match hints_fetch_task.await {
Ok(Some(new_hints)) => {
let task_buffer_snapshot = buffer_snapshot.clone();
if let Some(new_update) = cx
.background()
.spawn(async move {
new_excerpt_hints_update_result(
query,
new_hints,
&task_buffer_snapshot,
cached_excerpt_hints,
&visible_hints,
)
})
.await
{
editor
.update(&mut cx, |editor, cx| {
let cached_excerpt_hints = editor
.inlay_hint_cache
.snapshot
.hints
.entry(new_update.excerpt_id)
.or_insert_with(|| {
Arc::new(CachedExcerptHints {
version: new_update.cache_version,
buffer_version: buffer_snapshot.version().clone(),
hints: Vec::new(),
})
});
let cached_excerpt_hints = Arc::get_mut(cached_excerpt_hints)
.expect("Cached excerpt hints were dropped with the task");
match new_update.cache_version.cmp(&cached_excerpt_hints.version) {
cmp::Ordering::Less => return,
cmp::Ordering::Greater | cmp::Ordering::Equal => {
cached_excerpt_hints.version = new_update.cache_version;
}
}
cached_excerpt_hints.hints.retain(|(hint_id, _)| {
!new_update.remove_from_cache.contains(hint_id)
});
cached_excerpt_hints.buffer_version =
buffer_snapshot.version().clone();
editor.inlay_hint_cache.snapshot.version += 1;
let mut splice = InlaySplice {
to_remove: new_update.remove_from_visible,
to_insert: Vec::new(),
};
for new_hint in new_update.add_to_cache {
let new_hint_position = multi_buffer_snapshot
.anchor_in_excerpt(query.excerpt_id, new_hint.position);
let new_inlay_id = InlayId(post_inc(&mut editor.next_inlay_id));
if editor
.inlay_hint_cache
.snapshot
.allowed_hint_kinds
.contains(&new_hint.kind)
{
splice.to_insert.push((
new_hint_position,
new_inlay_id,
new_hint.clone(),
));
}
cached_excerpt_hints.hints.push((new_inlay_id, new_hint));
}
cached_excerpt_hints
.hints
.sort_by(|(_, hint_a), (_, hint_b)| {
hint_a.position.cmp(&hint_b.position, &buffer_snapshot)
});
let InlaySplice {
to_remove,
to_insert,
} = splice;
if !to_remove.is_empty() || !to_insert.is_empty() {
editor.splice_inlay_hints(to_remove, to_insert, cx)
}
})
.ok();
}
}
Ok(None) => {}
Err(e) => error!(
"Failed to fecth hints for excerpt {:?} in buffer {} : {}",
query.excerpt_id, query.buffer_id, e
),
}
}),
}
}
fn new_allowed_hint_kinds_splice( fn new_allowed_hint_kinds_splice(
cache: &CacheSnapshot, &self,
multi_buffer: &ModelHandle<MultiBuffer>, multi_buffer: &ModelHandle<MultiBuffer>,
visible_hints: &[Inlay], visible_hints: &[Inlay],
new_kinds: &HashSet<Option<InlayHintKind>>, new_kinds: &HashSet<Option<InlayHintKind>>,
cx: &mut ViewContext<Editor>, cx: &mut ViewContext<Editor>,
) -> Option<InlaySplice> { ) -> Option<InlaySplice> {
let old_kinds = &cache.allowed_hint_kinds; let old_kinds = &self.allowed_hint_kinds;
if new_kinds == old_kinds { if new_kinds == old_kinds {
return None; return None;
} }
@ -396,8 +261,10 @@ fn new_allowed_hint_kinds_splice(
let multi_buffer = multi_buffer.read(cx); let multi_buffer = multi_buffer.read(cx);
let multi_buffer_snapshot = multi_buffer.snapshot(cx); let multi_buffer_snapshot = multi_buffer.snapshot(cx);
for (excerpt_id, excerpt_cached_hints) in &cache.hints { for (excerpt_id, excerpt_cached_hints) in &self.hints {
let shown_excerpt_hints_to_remove = shown_hints_to_remove.entry(*excerpt_id).or_default(); let shown_excerpt_hints_to_remove =
shown_hints_to_remove.entry(*excerpt_id).or_default();
let excerpt_cached_hints = excerpt_cached_hints.read();
let mut excerpt_cache = excerpt_cached_hints.hints.iter().fuse().peekable(); let mut excerpt_cache = excerpt_cached_hints.hints.iter().fuse().peekable();
shown_excerpt_hints_to_remove.retain(|(shown_anchor, shown_hint_id)| { shown_excerpt_hints_to_remove.retain(|(shown_anchor, shown_hint_id)| {
let Some(buffer) = shown_anchor let Some(buffer) = shown_anchor
@ -421,8 +288,10 @@ fn new_allowed_hint_kinds_splice(
&& new_kinds.contains(&cached_hint.kind) && new_kinds.contains(&cached_hint.kind)
{ {
to_insert.push(( to_insert.push((
multi_buffer_snapshot multi_buffer_snapshot.anchor_in_excerpt(
.anchor_in_excerpt(*excerpt_id, cached_hint.position), *excerpt_id,
cached_hint.position,
),
*cached_hint_id, *cached_hint_id,
cached_hint.clone(), cached_hint.clone(),
)); ));
@ -466,11 +335,126 @@ fn new_allowed_hint_kinds_splice(
} }
} }
fn clear(&mut self) {
self.version += 1;
self.update_tasks.clear();
self.hints.clear();
self.allowed_hint_kinds.clear();
}
}
fn new_update_task(
query: ExcerptQuery,
multi_buffer_snapshot: MultiBufferSnapshot,
buffer_snapshot: BufferSnapshot,
visible_hints: Arc<Vec<Inlay>>,
cached_excerpt_hints: Option<Arc<RwLock<CachedExcerptHints>>>,
cx: &mut ViewContext<'_, '_, Editor>,
) -> InlayHintUpdateTask {
let hints_fetch_task = hints_fetch_task(query, cx);
InlayHintUpdateTask {
version: query.cache_version,
_task: cx.spawn(|editor, mut cx| async move {
match hints_fetch_task.await {
Ok(Some(new_hints)) => {
let task_buffer_snapshot = buffer_snapshot.clone();
if let Some(new_update) = cx
.background()
.spawn(async move {
new_excerpt_hints_update_result(
query,
new_hints,
&task_buffer_snapshot,
cached_excerpt_hints,
&visible_hints,
)
})
.await
{
editor
.update(&mut cx, |editor, cx| {
let cached_excerpt_hints = editor
.inlay_hint_cache
.hints
.entry(new_update.excerpt_id)
.or_insert_with(|| {
Arc::new(RwLock::new(CachedExcerptHints {
version: new_update.cache_version,
buffer_version: buffer_snapshot.version().clone(),
hints: Vec::new(),
}))
});
let mut cached_excerpt_hints = cached_excerpt_hints.write();
match new_update.cache_version.cmp(&cached_excerpt_hints.version) {
cmp::Ordering::Less => return,
cmp::Ordering::Greater | cmp::Ordering::Equal => {
cached_excerpt_hints.version = new_update.cache_version;
}
}
cached_excerpt_hints.hints.retain(|(hint_id, _)| {
!new_update.remove_from_cache.contains(hint_id)
});
cached_excerpt_hints.buffer_version =
buffer_snapshot.version().clone();
editor.inlay_hint_cache.version += 1;
let mut splice = InlaySplice {
to_remove: new_update.remove_from_visible,
to_insert: Vec::new(),
};
for new_hint in new_update.add_to_cache {
let new_hint_position = multi_buffer_snapshot
.anchor_in_excerpt(query.excerpt_id, new_hint.position);
let new_inlay_id = InlayId(post_inc(&mut editor.next_inlay_id));
if editor
.inlay_hint_cache
.allowed_hint_kinds
.contains(&new_hint.kind)
{
splice.to_insert.push((
new_hint_position,
new_inlay_id,
new_hint.clone(),
));
}
cached_excerpt_hints.hints.push((new_inlay_id, new_hint));
}
cached_excerpt_hints
.hints
.sort_by(|(_, hint_a), (_, hint_b)| {
hint_a.position.cmp(&hint_b.position, &buffer_snapshot)
});
drop(cached_excerpt_hints);
let InlaySplice {
to_remove,
to_insert,
} = splice;
if !to_remove.is_empty() || !to_insert.is_empty() {
editor.splice_inlay_hints(to_remove, to_insert, cx)
}
})
.ok();
}
}
Ok(None) => {}
Err(e) => error!(
"Failed to fecth hints for excerpt {:?} in buffer {} : {}",
query.excerpt_id, query.buffer_id, e
),
}
}),
}
}
fn new_excerpt_hints_update_result( fn new_excerpt_hints_update_result(
query: ExcerptQuery, query: ExcerptQuery,
new_excerpt_hints: Vec<InlayHint>, new_excerpt_hints: Vec<InlayHint>,
buffer_snapshot: &BufferSnapshot, buffer_snapshot: &BufferSnapshot,
cached_excerpt_hints: Option<Arc<CachedExcerptHints>>, cached_excerpt_hints: Option<Arc<RwLock<CachedExcerptHints>>>,
visible_hints: &[Inlay], visible_hints: &[Inlay],
) -> Option<ExcerptHintsUpdate> { ) -> Option<ExcerptHintsUpdate> {
let mut add_to_cache: Vec<InlayHint> = Vec::new(); let mut add_to_cache: Vec<InlayHint> = Vec::new();
@ -482,6 +466,7 @@ fn new_excerpt_hints_update_result(
} }
let missing_from_cache = match &cached_excerpt_hints { let missing_from_cache = match &cached_excerpt_hints {
Some(cached_excerpt_hints) => { Some(cached_excerpt_hints) => {
let cached_excerpt_hints = cached_excerpt_hints.read();
match cached_excerpt_hints.hints.binary_search_by(|probe| { match cached_excerpt_hints.hints.binary_search_by(|probe| {
probe.1.position.cmp(&new_hint.position, buffer_snapshot) probe.1.position.cmp(&new_hint.position, buffer_snapshot)
}) { }) {
@ -518,16 +503,20 @@ fn new_excerpt_hints_update_result(
.map(|inlay_hint| inlay_hint.id) .map(|inlay_hint| inlay_hint.id)
.filter(|hint_id| !excerpt_hints_to_persist.contains_key(hint_id)), .filter(|hint_id| !excerpt_hints_to_persist.contains_key(hint_id)),
); );
if let Some(cached_excerpt_hints) = &cached_excerpt_hints {
let cached_excerpt_hints = cached_excerpt_hints.read();
remove_from_cache.extend( remove_from_cache.extend(
cached_excerpt_hints cached_excerpt_hints
.hints
.iter() .iter()
.flat_map(|excerpt_hints| excerpt_hints.hints.iter())
.filter(|(cached_inlay_id, _)| { .filter(|(cached_inlay_id, _)| {
!excerpt_hints_to_persist.contains_key(cached_inlay_id) !excerpt_hints_to_persist.contains_key(cached_inlay_id)
}) })
.map(|(cached_inlay_id, _)| *cached_inlay_id), .map(|(cached_inlay_id, _)| *cached_inlay_id),
); );
} }
}
if remove_from_visible.is_empty() && remove_from_cache.is_empty() && add_to_cache.is_empty() { if remove_from_visible.is_empty() && remove_from_cache.is_empty() && add_to_cache.is_empty() {
None None