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