1use std::cmp;
2
3use crate::{
4 display_map::Inlay, editor_settings, Anchor, Editor, ExcerptId, InlayId, MultiBuffer,
5 MultiBufferSnapshot,
6};
7use anyhow::Context;
8use clock::Global;
9use gpui::{ModelHandle, Task, ViewContext};
10use log::error;
11use project::{InlayHint, InlayHintKind};
12
13use collections::{hash_map, HashMap, HashSet};
14use util::post_inc;
15
16#[derive(Debug)]
17pub struct InlayHintCache {
18 inlay_hints: HashMap<InlayId, InlayHint>,
19 hints_in_buffers: HashMap<u64, BufferHints<(Anchor, InlayId)>>,
20 allowed_hint_kinds: HashSet<Option<InlayHintKind>>,
21 hint_updates_tx: smol::channel::Sender<HintsUpdate>,
22}
23
24#[derive(Clone, Debug)]
25struct BufferHints<H> {
26 buffer_version: Global,
27 hints_per_excerpt: HashMap<ExcerptId, Vec<H>>,
28}
29
30#[derive(Debug)]
31pub struct InlayHintQuery {
32 pub buffer_id: u64,
33 pub buffer_version: Global,
34 pub excerpt_id: ExcerptId,
35}
36
37impl<H> BufferHints<H> {
38 fn new(buffer_version: Global) -> Self {
39 Self {
40 buffer_version,
41 hints_per_excerpt: HashMap::default(),
42 }
43 }
44}
45
46impl InlayHintCache {
47 pub fn new(
48 inlay_hint_settings: editor_settings::InlayHints,
49 cx: &mut ViewContext<Editor>,
50 ) -> Self {
51 let (hint_updates_tx, hint_updates_rx) = smol::channel::unbounded();
52 spawn_hints_update_loop(hint_updates_rx, cx);
53 Self {
54 allowed_hint_kinds: allowed_hint_types(inlay_hint_settings),
55 hints_in_buffers: HashMap::default(),
56 inlay_hints: HashMap::default(),
57 hint_updates_tx,
58 }
59 }
60
61 pub fn spawn_settings_update(
62 &mut self,
63 multi_buffer: ModelHandle<MultiBuffer>,
64 inlay_hint_settings: editor_settings::InlayHints,
65 current_inlays: Vec<Inlay>,
66 ) {
67 if !inlay_hint_settings.enabled {
68 self.allowed_hint_kinds = allowed_hint_types(inlay_hint_settings);
69 if self.inlay_hints.is_empty() {
70 return;
71 } else {
72 self.hint_updates_tx
73 .send_blocking(HintsUpdate {
74 multi_buffer,
75 current_inlays,
76 kind: HintsUpdateKind::Clean,
77 })
78 .ok();
79 return;
80 }
81 }
82
83 let new_allowed_hint_kinds = allowed_hint_types(inlay_hint_settings);
84 if new_allowed_hint_kinds == self.allowed_hint_kinds {
85 return;
86 }
87
88 self.hint_updates_tx
89 .send_blocking(HintsUpdate {
90 multi_buffer,
91 current_inlays,
92 kind: HintsUpdateKind::AllowedHintKindsChanged {
93 old: self.allowed_hint_kinds.clone(),
94 new: new_allowed_hint_kinds,
95 },
96 })
97 .ok();
98 }
99
100 pub fn spawn_hints_update(
101 &mut self,
102 multi_buffer: ModelHandle<MultiBuffer>,
103 queries: Vec<InlayHintQuery>,
104 current_inlays: Vec<Inlay>,
105 conflicts_invalidate_cache: bool,
106 cx: &mut ViewContext<Editor>,
107 ) {
108 let conflicts_with_cache = conflicts_invalidate_cache
109 && queries.iter().any(|update_query| {
110 let Some(cached_buffer_hints) = self.hints_in_buffers.get(&update_query.buffer_id)
111 else { return false };
112 if cached_buffer_hints
113 .buffer_version
114 .changed_since(&update_query.buffer_version)
115 {
116 false
117 } else if update_query
118 .buffer_version
119 .changed_since(&cached_buffer_hints.buffer_version)
120 {
121 true
122 } else {
123 cached_buffer_hints
124 .hints_per_excerpt
125 .contains_key(&update_query.excerpt_id)
126 }
127 });
128
129 let queries_per_buffer = queries
130 .into_iter()
131 .filter_map(|query| {
132 let Some(cached_buffer_hints) = self.hints_in_buffers.get(&query.buffer_id)
133 else { return Some(query) };
134 if cached_buffer_hints
135 .buffer_version
136 .changed_since(&query.buffer_version)
137 {
138 return None;
139 }
140 if conflicts_with_cache
141 || !cached_buffer_hints
142 .hints_per_excerpt
143 .contains_key(&query.excerpt_id)
144 {
145 Some(query)
146 } else {
147 None
148 }
149 })
150 .fold(
151 HashMap::<u64, (Global, Vec<ExcerptId>)>::default(),
152 |mut queries_per_buffer, new_query| {
153 let (current_verison, excerpts_to_query) =
154 queries_per_buffer.entry(new_query.buffer_id).or_default();
155
156 if new_query.buffer_version.changed_since(current_verison) {
157 *current_verison = new_query.buffer_version;
158 *excerpts_to_query = vec![new_query.excerpt_id];
159 } else if !current_verison.changed_since(&new_query.buffer_version) {
160 excerpts_to_query.push(new_query.excerpt_id);
161 }
162
163 queries_per_buffer
164 },
165 );
166
167 for (queried_buffer, (buffer_version, excerpts)) in queries_per_buffer {
168 self.hint_updates_tx
169 .send_blocking(HintsUpdate {
170 multi_buffer,
171 current_inlays,
172 kind: HintsUpdateKind::BufferUpdate {
173 invalidate_cache: conflicts_with_cache,
174 buffer_id: queried_buffer,
175 buffer_version,
176 excerpts,
177 },
178 })
179 .ok();
180 }
181 }
182}
183
184#[derive(Debug, Default)]
185struct InlaySplice {
186 to_remove: Vec<InlayId>,
187 to_insert: Vec<(InlayId, Anchor, InlayHint)>,
188}
189
190struct HintsUpdate {
191 multi_buffer: ModelHandle<MultiBuffer>,
192 current_inlays: Vec<Inlay>,
193 kind: HintsUpdateKind,
194}
195
196enum HintsUpdateKind {
197 Clean,
198 AllowedHintKindsChanged {
199 old: HashSet<Option<InlayHintKind>>,
200 new: HashSet<Option<InlayHintKind>>,
201 },
202 BufferUpdate {
203 buffer_id: u64,
204 buffer_version: Global,
205 excerpts: Vec<ExcerptId>,
206 invalidate_cache: bool,
207 },
208}
209
210struct UpdateTaskHandle {
211 multi_buffer: ModelHandle<MultiBuffer>,
212 cancellation_tx: smol::channel::Sender<()>,
213 task_finish_rx: smol::channel::Receiver<UpdateTaskResult>,
214}
215
216struct UpdateTaskResult {
217 multi_buffer: ModelHandle<MultiBuffer>,
218 splice: InlaySplice,
219 new_allowed_hint_kinds: Option<HashSet<Option<InlayHintKind>>>,
220 remove_from_cache: HashSet<InlayId>,
221 add_to_cache: HashMap<u64, BufferHints<(Anchor, InlayHint, InlayId)>>,
222}
223
224impl HintsUpdate {
225 fn merge(&mut self, mut other: Self) -> Result<(), Self> {
226 match (&mut self.kind, &mut other.kind) {
227 (HintsUpdateKind::Clean, HintsUpdateKind::Clean) => return Ok(()),
228 (
229 HintsUpdateKind::AllowedHintKindsChanged { .. },
230 HintsUpdateKind::AllowedHintKindsChanged { .. },
231 ) => {
232 *self = other;
233 return Ok(());
234 }
235 (
236 HintsUpdateKind::BufferUpdate {
237 buffer_id: old_buffer_id,
238 buffer_version: old_buffer_version,
239 excerpts: old_excerpts,
240 invalidate_cache: old_invalidate_cache,
241 },
242 HintsUpdateKind::BufferUpdate {
243 buffer_id: new_buffer_id,
244 buffer_version: new_buffer_version,
245 excerpts: new_excerpts,
246 invalidate_cache: new_invalidate_cache,
247 },
248 ) => {
249 if old_buffer_id == new_buffer_id {
250 if new_buffer_version.changed_since(old_buffer_version) {
251 *self = other;
252 return Ok(());
253 } else if old_buffer_version.changed_since(new_buffer_version) {
254 return Ok(());
255 } else if *new_invalidate_cache {
256 *self = other;
257 return Ok(());
258 } else {
259 let old_inlays = self
260 .current_inlays
261 .iter()
262 .map(|inlay| inlay.id)
263 .collect::<Vec<_>>();
264 let new_inlays = other
265 .current_inlays
266 .iter()
267 .map(|inlay| inlay.id)
268 .collect::<Vec<_>>();
269 if old_inlays == new_inlays {
270 old_excerpts.extend(new_excerpts.drain(..));
271 old_excerpts.dedup();
272 return Ok(());
273 }
274 }
275 }
276 }
277 _ => {}
278 }
279
280 Err(other)
281 }
282
283 fn spawn(self, cx: &mut ViewContext<'_, '_, Editor>) -> UpdateTaskHandle {
284 let (task_finish_tx, task_finish_rx) = smol::channel::unbounded();
285 let (cancellation_tx, cancellation_rx) = smol::channel::bounded(1);
286
287 match self.kind {
288 HintsUpdateKind::Clean => cx
289 .spawn(|editor, mut cx| async move {
290 if let Some(splice) = editor.update(&mut cx, |editor, cx| {
291 clean_cache(editor, self.current_inlays)
292 })? {
293 task_finish_tx
294 .send(UpdateTaskResult {
295 multi_buffer: self.multi_buffer.clone(),
296 splice,
297 new_allowed_hint_kinds: None,
298 remove_from_cache: HashSet::default(),
299 add_to_cache: HashMap::default(),
300 })
301 .await
302 .ok();
303 }
304 anyhow::Ok(())
305 })
306 .detach_and_log_err(cx),
307 HintsUpdateKind::AllowedHintKindsChanged { old, new } => cx
308 .spawn(|editor, mut cx| async move {
309 if let Some(splice) = editor.update(&mut cx, |editor, cx| {
310 update_allowed_hint_kinds(
311 &self.multi_buffer.read(cx).snapshot(cx),
312 self.current_inlays,
313 old,
314 new,
315 editor,
316 )
317 })? {
318 task_finish_tx
319 .send(UpdateTaskResult {
320 multi_buffer: self.multi_buffer.clone(),
321 splice,
322 new_allowed_hint_kinds: None,
323 remove_from_cache: HashSet::default(),
324 add_to_cache: HashMap::default(),
325 })
326 .await
327 .ok();
328 }
329 anyhow::Ok(())
330 })
331 .detach_and_log_err(cx),
332 HintsUpdateKind::BufferUpdate {
333 buffer_id,
334 buffer_version,
335 excerpts,
336 invalidate_cache,
337 } => todo!("TODO kb"),
338 }
339
340 UpdateTaskHandle {
341 multi_buffer: self.multi_buffer.clone(),
342 cancellation_tx,
343 task_finish_rx,
344 }
345 }
346}
347
348fn spawn_hints_update_loop(
349 hint_updates_rx: smol::channel::Receiver<HintsUpdate>,
350 cx: &mut ViewContext<'_, '_, Editor>,
351) {
352 cx.spawn(|editor, mut cx| async move {
353 let mut update = None::<HintsUpdate>;
354 let mut next_update = None::<HintsUpdate>;
355 loop {
356 if update.is_none() {
357 match hint_updates_rx.recv().await {
358 Ok(first_task) => update = Some(first_task),
359 Err(smol::channel::RecvError) => return,
360 }
361 }
362
363 let mut updates_limit = 10;
364 'update_merge: loop {
365 match hint_updates_rx.try_recv() {
366 Ok(new_update) => {
367 match update.as_mut() {
368 Some(update) => match update.merge(new_update) {
369 Ok(()) => {}
370 Err(new_update) => {
371 next_update = Some(new_update);
372 break 'update_merge;
373 }
374 },
375 None => update = Some(new_update),
376 };
377
378 if updates_limit == 0 {
379 break 'update_merge;
380 }
381 updates_limit -= 1;
382 }
383 Err(smol::channel::TryRecvError::Empty) => break 'update_merge,
384 Err(smol::channel::TryRecvError::Closed) => return,
385 }
386 }
387
388 if let Some(update) = update.take() {
389 let Ok(task_handle) = editor.update(&mut cx, |_, cx| update.spawn(cx)) else { return; };
390 while let Ok(update_task_result) = task_handle.task_finish_rx.recv().await {
391 let Ok(()) = editor.update(&mut cx, |editor, cx| {
392 let multi_buffer_snapshot = update_task_result.multi_buffer.read(cx).snapshot(cx);
393 let inlay_hint_cache = &mut editor.inlay_hint_cache;
394
395 if let Some(new_allowed_hint_kinds) = update_task_result.new_allowed_hint_kinds {
396 inlay_hint_cache.allowed_hint_kinds = new_allowed_hint_kinds;
397 }
398
399 inlay_hint_cache.hints_in_buffers.retain(|_, buffer_hints| {
400 buffer_hints.hints_per_excerpt.retain(|_, excerpt_hints| {
401 excerpt_hints.retain(|(_, hint_id)| !update_task_result.remove_from_cache.contains(hint_id));
402 !excerpt_hints.is_empty()
403 });
404 !buffer_hints.hints_per_excerpt.is_empty()
405 });
406 inlay_hint_cache.inlay_hints.retain(|hint_id, _| !update_task_result.remove_from_cache.contains(hint_id));
407
408 for (new_buffer_id, new_buffer_inlays) in update_task_result.add_to_cache {
409 let cached_buffer_hints = inlay_hint_cache.hints_in_buffers.entry(new_buffer_id).or_insert_with(|| BufferHints::new(new_buffer_inlays.buffer_version));
410 if cached_buffer_hints.buffer_version.changed_since(&new_buffer_inlays.buffer_version) {
411 continue;
412 }
413 for (excerpt_id, new_excerpt_inlays) in new_buffer_inlays.hints_per_excerpt {
414 let cached_excerpt_hints = cached_buffer_hints.hints_per_excerpt.entry(excerpt_id).or_default();
415 for (new_hint_position, new_hint, new_inlay_id) in new_excerpt_inlays {
416 if let hash_map::Entry::Vacant(v) = inlay_hint_cache.inlay_hints.entry(new_inlay_id) {
417 v.insert(new_hint);
418 match cached_excerpt_hints.binary_search_by(|probe| {
419 new_hint_position.cmp(&probe.0, &multi_buffer_snapshot)
420 }) {
421 Ok(ix) | Err(ix) => cached_excerpt_hints.insert(ix, (new_hint_position, new_inlay_id)),
422 }
423 }
424 }
425 }
426 }
427
428 let InlaySplice {
429 to_remove,
430 to_insert,
431 } = update_task_result.splice;
432 editor.splice_inlay_hints(to_remove, to_insert, cx)
433 }) else { return; };
434 }
435 }
436 update = next_update.take();
437 }
438 })
439 .detach()
440}
441
442fn update_allowed_hint_kinds(
443 multi_buffer_snapshot: &MultiBufferSnapshot,
444 current_inlays: Vec<Inlay>,
445 old_kinds: HashSet<Option<InlayHintKind>>,
446 new_kinds: HashSet<Option<InlayHintKind>>,
447 editor: &mut Editor,
448) -> Option<InlaySplice> {
449 if old_kinds == new_kinds {
450 return None;
451 }
452
453 let mut to_remove = Vec::new();
454 let mut to_insert = Vec::new();
455 let mut shown_hints_to_remove = group_inlays(&multi_buffer_snapshot, current_inlays);
456 let hints_cache = &editor.inlay_hint_cache;
457
458 for (buffer_id, cached_buffer_hints) in &hints_cache.hints_in_buffers {
459 let shown_buffer_hints_to_remove = shown_hints_to_remove.entry(*buffer_id).or_default();
460 for (excerpt_id, cached_excerpt_hints) in &cached_buffer_hints.hints_per_excerpt {
461 let shown_excerpt_hints_to_remove =
462 shown_buffer_hints_to_remove.entry(*excerpt_id).or_default();
463 let mut cached_hints = cached_excerpt_hints.iter().fuse().peekable();
464 shown_excerpt_hints_to_remove.retain(|(shown_anchor, shown_hint_id)| {
465 loop {
466 match cached_hints.peek() {
467 Some((cached_anchor, cached_hint_id)) => {
468 if cached_hint_id == shown_hint_id {
469 return !new_kinds.contains(
470 &hints_cache.inlay_hints.get(&cached_hint_id).unwrap().kind,
471 );
472 }
473
474 match cached_anchor.cmp(shown_anchor, &multi_buffer_snapshot) {
475 cmp::Ordering::Less | cmp::Ordering::Equal => {
476 let maybe_missed_cached_hint =
477 hints_cache.inlay_hints.get(&cached_hint_id).unwrap();
478 let cached_hint_kind = maybe_missed_cached_hint.kind;
479 if !old_kinds.contains(&cached_hint_kind)
480 && new_kinds.contains(&cached_hint_kind)
481 {
482 to_insert.push((
483 *cached_hint_id,
484 *cached_anchor,
485 maybe_missed_cached_hint.clone(),
486 ));
487 }
488 cached_hints.next();
489 }
490 cmp::Ordering::Greater => break,
491 }
492 }
493 None => return true,
494 }
495 }
496
497 match hints_cache.inlay_hints.get(&shown_hint_id) {
498 Some(shown_hint) => !new_kinds.contains(&shown_hint.kind),
499 None => true,
500 }
501 });
502
503 for (cached_anchor, cached_hint_id) in cached_hints {
504 let maybe_missed_cached_hint =
505 hints_cache.inlay_hints.get(&cached_hint_id).unwrap();
506 let cached_hint_kind = maybe_missed_cached_hint.kind;
507 if !old_kinds.contains(&cached_hint_kind) && new_kinds.contains(&cached_hint_kind) {
508 to_insert.push((
509 *cached_hint_id,
510 *cached_anchor,
511 maybe_missed_cached_hint.clone(),
512 ));
513 }
514 }
515 }
516 }
517
518 to_remove.extend(
519 shown_hints_to_remove
520 .into_iter()
521 .flat_map(|(_, hints_by_excerpt)| hints_by_excerpt)
522 .flat_map(|(_, excerpt_hints)| excerpt_hints)
523 .map(|(_, hint_id)| hint_id),
524 );
525 Some(InlaySplice {
526 to_remove,
527 to_insert,
528 })
529}
530
531fn clean_cache(editor: &mut Editor, current_inlays: Vec<Inlay>) -> Option<InlaySplice> {
532 let hints_cache = &mut editor.inlay_hint_cache;
533 if hints_cache.inlay_hints.is_empty() {
534 None
535 } else {
536 let splice = InlaySplice {
537 to_remove: current_inlays
538 .iter()
539 .filter(|inlay| {
540 editor
541 .copilot_state
542 .suggestion
543 .as_ref()
544 .map(|inlay| inlay.id)
545 != Some(inlay.id)
546 })
547 .map(|inlay| inlay.id)
548 .collect(),
549 to_insert: Vec::new(),
550 };
551 hints_cache.inlay_hints.clear();
552 hints_cache.hints_in_buffers.clear();
553 Some(splice)
554 }
555}
556
557fn allowed_hint_types(
558 inlay_hint_settings: editor_settings::InlayHints,
559) -> HashSet<Option<InlayHintKind>> {
560 let mut new_allowed_hint_types = HashSet::default();
561 if inlay_hint_settings.show_type_hints {
562 new_allowed_hint_types.insert(Some(InlayHintKind::Type));
563 }
564 if inlay_hint_settings.show_parameter_hints {
565 new_allowed_hint_types.insert(Some(InlayHintKind::Parameter));
566 }
567 if inlay_hint_settings.show_other_hints {
568 new_allowed_hint_types.insert(None);
569 }
570 new_allowed_hint_types
571}
572
573// TODO kb wrong, query and update the editor separately
574fn fetch_queries(
575 multi_buffer: ModelHandle<MultiBuffer>,
576 queries: impl Iterator<Item = InlayHintQuery>,
577 cx: &mut ViewContext<'_, '_, Editor>,
578) -> Task<anyhow::Result<HashMap<u64, BufferHints<InlayHint>>>> {
579 let mut inlay_fetch_tasks = Vec::new();
580 for query in queries {
581 let task_multi_buffer = multi_buffer.clone();
582 let task = cx.spawn(|editor, mut cx| async move {
583 let Some(buffer_handle) = cx.read(|cx| task_multi_buffer.read(cx).buffer(query.buffer_id))
584 else { return anyhow::Ok((query, Some(Vec::new()))) };
585 let task = editor
586 .update(&mut cx, |editor, cx| {
587 if let Some((_, excerpt_range)) = task_multi_buffer.read(cx)
588 .excerpts_for_buffer(&buffer_handle, cx)
589 .into_iter()
590 .find(|(excerpt_id, _)| excerpt_id == &query.excerpt_id)
591 {
592 editor.project.as_ref().map(|project| {
593 project.update(cx, |project, cx| {
594 project.query_inlay_hints_for_buffer(
595 buffer_handle,
596 excerpt_range.context,
597 cx,
598 )
599 })
600 })
601 } else {
602 None
603 }
604 })
605 .context("inlays fetch task spawn")?;
606 Ok((
607 query,
608 match task {
609 Some(task) => task.await.context("inlays for buffer task")?,
610 None => Some(Vec::new()),
611 },
612 ))
613 });
614
615 inlay_fetch_tasks.push(task);
616 }
617
618 cx.spawn(|editor, cx| async move {
619 let mut inlay_updates: HashMap<u64, BufferHints<InlayHint>> = HashMap::default();
620 for task_result in futures::future::join_all(inlay_fetch_tasks).await {
621 match task_result {
622 Ok((query, Some(response_hints))) => {
623 let Some(buffer_snapshot) = editor.read_with(&cx, |editor, cx| {
624 editor.buffer().read(cx).buffer(query.buffer_id).map(|buffer| buffer.read(cx).snapshot())
625 })? else { continue; };
626 let buffer_hints = inlay_updates
627 .entry(query.buffer_id)
628 .or_insert_with(|| BufferHints::new(query.buffer_version.clone()));
629 if buffer_snapshot.version().changed_since(&buffer_hints.buffer_version) {
630 continue;
631 }
632 let cached_excerpt_hints = buffer_hints
633 .hints_per_excerpt
634 .entry(query.excerpt_id)
635 .or_default();
636 for inlay in response_hints {
637 match cached_excerpt_hints.binary_search_by(|probe| {
638 inlay.position.cmp(&probe.position, &buffer_snapshot)
639 }) {
640 Ok(ix) | Err(ix) => cached_excerpt_hints.insert(ix, inlay),
641 }
642 }
643 }
644 Ok((_, None)) => {}
645 Err(e) => error!("Failed to update inlays for buffer: {e:#}"),
646 }
647 }
648 Ok(inlay_updates)
649 })
650}
651
652fn group_inlays(
653 multi_buffer_snapshot: &MultiBufferSnapshot,
654 inlays: Vec<Inlay>,
655) -> HashMap<u64, HashMap<ExcerptId, Vec<(Anchor, InlayId)>>> {
656 inlays.into_iter().fold(
657 HashMap::<u64, HashMap<ExcerptId, Vec<(Anchor, InlayId)>>>::default(),
658 |mut current_hints, inlay| {
659 if let Some(buffer_id) = inlay.position.buffer_id {
660 current_hints
661 .entry(buffer_id)
662 .or_default()
663 .entry(inlay.position.excerpt_id)
664 .or_default()
665 .push((inlay.position, inlay.id));
666 }
667 current_hints
668 },
669 )
670}
671
672async fn update_hints(
673 multi_buffer: ModelHandle<MultiBuffer>,
674 queries: Vec<InlayHintQuery>,
675 current_inlays: Vec<Inlay>,
676 invalidate_cache: bool,
677 cx: &mut ViewContext<'_, '_, Editor>,
678) -> Option<InlaySplice> {
679 let fetch_queries_task = fetch_queries(multi_buffer, queries.into_iter(), cx);
680 let new_hints = fetch_queries_task.await.context("inlay hints fetch")?;
681
682 let mut to_remove = Vec::new();
683 let mut to_insert = Vec::new();
684 let mut cache_hints_to_persist: HashMap<u64, (Global, HashMap<ExcerptId, HashSet<InlayId>>)> =
685 HashMap::default();
686
687 editor.update(&mut cx, |editor, cx| {
688 let multi_buffer_snapshot = task_multi_buffer.read(cx).snapshot(cx);
689 for (new_buffer_id, new_hints_per_buffer) in new_hints {
690 let cached_buffer_hints = editor
691 .inlay_hint_cache
692 .hints_in_buffers
693 .entry(new_buffer_id)
694 .or_insert_with(|| {
695 BufferHints::new(new_hints_per_buffer.buffer_version.clone())
696 });
697
698 let buffer_cache_hints_to_persist =
699 cache_hints_to_persist.entry(new_buffer_id).or_insert_with(|| (new_hints_per_buffer.buffer_version.clone(), HashMap::default()));
700 if cached_buffer_hints
701 .buffer_version
702 .changed_since(&new_hints_per_buffer.buffer_version)
703 {
704 buffer_cache_hints_to_persist.0 = new_hints_per_buffer.buffer_version;
705 buffer_cache_hints_to_persist.1.extend(
706 cached_buffer_hints.hints_per_excerpt.iter().map(
707 |(excerpt_id, excerpt_hints)| {
708 (
709 *excerpt_id,
710 excerpt_hints.iter().map(|(_, id)| *id).collect(),
711 )
712 },
713 ),
714 );
715 continue;
716 }
717
718 let shown_buffer_hints = currently_shown_hints.get(&new_buffer_id);
719 for (new_excerpt_id, new_hints_per_excerpt) in
720 new_hints_per_buffer.hints_per_excerpt
721 {
722 let excerpt_cache_hints_to_persist = buffer_cache_hints_to_persist.1
723 .entry(new_excerpt_id)
724 .or_default();
725 let cached_excerpt_hints = cached_buffer_hints
726 .hints_per_excerpt
727 .entry(new_excerpt_id)
728 .or_default();
729 let empty_shown_excerpt_hints = Vec::new();
730 let shown_excerpt_hints = shown_buffer_hints.and_then(|hints| hints.get(&new_excerpt_id)).unwrap_or(&empty_shown_excerpt_hints);
731 for new_hint in new_hints_per_excerpt {
732 let new_hint_anchor = multi_buffer_snapshot
733 .anchor_in_excerpt(new_excerpt_id, new_hint.position);
734 let cache_insert_ix = match cached_excerpt_hints.binary_search_by(|probe| {
735 new_hint_anchor.cmp(&probe.0, &multi_buffer_snapshot)
736 }) {
737 Ok(ix) => {
738 let (_, cached_inlay_id) = cached_excerpt_hints[ix];
739 let cache_hit = editor
740 .inlay_hint_cache
741 .inlay_hints
742 .get(&cached_inlay_id)
743 .filter(|cached_hint| cached_hint == &&new_hint)
744 .is_some();
745 if cache_hit {
746 excerpt_cache_hints_to_persist
747 .insert(cached_inlay_id);
748 None
749 } else {
750 Some(ix)
751 }
752 }
753 Err(ix) => Some(ix),
754 };
755
756 let shown_inlay_id = match shown_excerpt_hints.binary_search_by(|probe| {
757 probe.0.cmp(&new_hint_anchor, &multi_buffer_snapshot)
758 }) {
759 Ok(ix) => {{
760 let (_, shown_inlay_id) = shown_excerpt_hints[ix];
761 let shown_hint_found = editor.inlay_hint_cache.inlay_hints.get(&shown_inlay_id)
762 .filter(|cached_hint| cached_hint == &&new_hint).is_some();
763 if shown_hint_found {
764 Some(shown_inlay_id)
765 } else {
766 None
767 }
768 }},
769 Err(_) => None,
770 };
771
772 if let Some(insert_ix) = cache_insert_ix {
773 let hint_id = match shown_inlay_id {
774 Some(shown_inlay_id) => shown_inlay_id,
775 None => {
776 let new_hint_id = InlayId(post_inc(&mut editor.next_inlay_id));
777 if editor.inlay_hint_cache.allowed_hint_kinds.contains(&new_hint.kind)
778 {
779 to_insert.push((new_hint_id, new_hint_anchor, new_hint.clone()));
780 }
781 new_hint_id
782 }
783 };
784 excerpt_cache_hints_to_persist.insert(hint_id);
785 cached_excerpt_hints.insert(insert_ix, (new_hint_anchor, hint_id));
786 editor
787 .inlay_hint_cache
788 .inlay_hints
789 .insert(hint_id, new_hint);
790 }
791 }
792 }
793 }
794
795 if conflicts_with_cache {
796 for (shown_buffer_id, mut shown_hints_to_clean) in currently_shown_hints {
797 match cache_hints_to_persist.get(&shown_buffer_id) {
798 Some(cached_buffer_hints) => {
799 for (persisted_id, cached_hints) in &cached_buffer_hints.1 {
800 shown_hints_to_clean.entry(*persisted_id).or_default()
801 .retain(|(_, shown_id)| !cached_hints.contains(shown_id));
802 }
803 },
804 None => {},
805 }
806 to_remove.extend(shown_hints_to_clean.into_iter()
807 .flat_map(|(_, excerpt_hints)| excerpt_hints.into_iter().map(|(_, hint_id)| hint_id)));
808 }
809
810 editor.inlay_hint_cache.hints_in_buffers.retain(|buffer_id, buffer_hints| {
811 let Some(mut buffer_hints_to_persist) = cache_hints_to_persist.remove(buffer_id) else { return false; };
812 buffer_hints.buffer_version = buffer_hints_to_persist.0;
813 buffer_hints.hints_per_excerpt.retain(|excerpt_id, excerpt_hints| {
814 let Some(excerpt_hints_to_persist) = buffer_hints_to_persist.1.remove(&excerpt_id) else { return false; };
815 excerpt_hints.retain(|(_, hint_id)| {
816 let retain = excerpt_hints_to_persist.contains(hint_id);
817 if !retain {
818 editor
819 .inlay_hint_cache
820 .inlay_hints
821 .remove(hint_id);
822 }
823 retain
824 });
825 !excerpt_hints.is_empty()
826 });
827 !buffer_hints.hints_per_excerpt.is_empty()
828 });
829 }
830
831 Some(InlaySplice {
832 to_remove,
833 to_insert,
834 })
835 })
836}