inlay_cache.rs

  1use std::ops::Range;
  2
  3use crate::{
  4    display_map::InlayId, editor_settings, scroll::ScrollAnchor, Anchor, Editor, ExcerptId,
  5    MultiBuffer,
  6};
  7use clock::Global;
  8use gpui::{ModelHandle, Task, ViewContext};
  9use language::Buffer;
 10use project::{InlayHint, InlayHintKind};
 11
 12use collections::{HashMap, HashSet};
 13
 14#[derive(Debug, Copy, Clone)]
 15pub enum InlayRefreshReason {
 16    SettingsChange(editor_settings::InlayHints),
 17    Scroll(ScrollAnchor),
 18    VisibleExcerptsChange,
 19}
 20
 21#[derive(Debug, Clone, Default)]
 22pub struct InlayCache {
 23    inlay_hints: HashMap<InlayId, InlayHint>,
 24    inlays_in_buffers: HashMap<u64, BufferInlays>,
 25    allowed_hint_kinds: HashSet<Option<InlayHintKind>>,
 26}
 27
 28#[derive(Clone, Debug, Default)]
 29struct BufferInlays {
 30    buffer_version: Global,
 31    ordered_by_anchor_inlays: Vec<(Anchor, InlayId)>,
 32}
 33
 34#[derive(Debug, Default)]
 35pub struct InlaySplice {
 36    pub to_remove: Vec<InlayId>,
 37    pub to_insert: Vec<(Option<InlayId>, Anchor, InlayHint)>,
 38}
 39
 40pub struct InlayHintQuery {
 41    pub buffer_id: u64,
 42    pub buffer_version: Global,
 43    pub excerpt_id: ExcerptId,
 44    pub excerpt_offset_query_range: Range<usize>,
 45}
 46
 47impl InlayCache {
 48    pub fn new(inlay_hint_settings: editor_settings::InlayHints) -> Self {
 49        Self {
 50            allowed_hint_kinds: allowed_inlay_hint_types(inlay_hint_settings),
 51            inlays_in_buffers: HashMap::default(),
 52            inlay_hints: HashMap::default(),
 53        }
 54    }
 55
 56    pub fn apply_settings(
 57        &mut self,
 58        multi_buffer: ModelHandle<MultiBuffer>,
 59        inlay_hint_settings: editor_settings::InlayHints,
 60        currently_visible_ranges: Vec<(ModelHandle<Buffer>, Range<usize>, ExcerptId)>,
 61        currently_shown_inlays: Vec<(Anchor, InlayId)>,
 62        cx: &mut ViewContext<Editor>,
 63    ) -> Option<InlaySplice> {
 64        let new_allowed_hint_kinds = allowed_inlay_hint_types(inlay_hint_settings);
 65        if new_allowed_hint_kinds == self.allowed_hint_kinds {
 66            None
 67        } else {
 68            self.allowed_hint_kinds = new_allowed_hint_kinds;
 69            let mut to_remove = Vec::new();
 70            let mut to_insert = Vec::new();
 71
 72            let mut considered_inlay_ids = HashSet::default();
 73            for (_, shown_inlay_id) in currently_shown_inlays {
 74                if let Some(inlay_hint) = self.inlay_hints.get(&shown_inlay_id) {
 75                    if !self.allowed_hint_kinds.contains(&inlay_hint.kind) {
 76                        to_remove.push(shown_inlay_id);
 77                    }
 78                    considered_inlay_ids.insert(shown_inlay_id);
 79                }
 80            }
 81
 82            let multi_buffer_snapshot = multi_buffer.read(cx).snapshot(cx);
 83            for (inlay_id, inlay_hint) in &self.inlay_hints {
 84                if self.allowed_hint_kinds.contains(&inlay_hint.kind)
 85                    && !considered_inlay_ids.contains(inlay_id)
 86                {
 87                    if let Some(hint_to_readd) = currently_visible_ranges.iter()
 88                        .filter(|(_, visible_range, _)| visible_range.contains(&inlay_hint.position.offset))
 89                        .find_map(|(_, _, excerpt_id)| {
 90                            let Some(anchor) = multi_buffer_snapshot
 91                                .find_anchor_in_excerpt(*excerpt_id, inlay_hint.position) else { return None; };
 92                            Some((Some(*inlay_id), anchor, inlay_hint.clone()))
 93                        },
 94                    ) {
 95                        to_insert.push(hint_to_readd);
 96                    }
 97                }
 98            }
 99
100            Some(InlaySplice {
101                to_remove,
102                to_insert,
103            })
104        }
105    }
106
107    pub fn clear(&mut self) -> Vec<InlayId> {
108        let ids_to_remove = self.inlay_hints.drain().map(|(id, _)| id).collect();
109        self.inlays_in_buffers.clear();
110        ids_to_remove
111    }
112
113    pub fn append_inlays(
114        &mut self,
115        multi_buffer: ModelHandle<MultiBuffer>,
116        ranges_to_add: impl Iterator<Item = InlayHintQuery>,
117        currently_shown_inlays: Vec<(Anchor, InlayId)>,
118        cx: &mut ViewContext<Editor>,
119    ) -> Task<anyhow::Result<InlaySplice>> {
120        self.fetch_inlays(
121            multi_buffer,
122            ranges_to_add,
123            currently_shown_inlays,
124            false,
125            cx,
126        )
127    }
128
129    pub fn replace_inlays(
130        &mut self,
131        multi_buffer: ModelHandle<MultiBuffer>,
132        new_ranges: impl Iterator<Item = InlayHintQuery>,
133        currently_shown_inlays: Vec<(Anchor, InlayId)>,
134        cx: &mut ViewContext<Editor>,
135    ) -> Task<anyhow::Result<InlaySplice>> {
136        self.fetch_inlays(multi_buffer, new_ranges, currently_shown_inlays, true, cx)
137    }
138
139    fn fetch_inlays(
140        &mut self,
141        multi_buffer: ModelHandle<MultiBuffer>,
142        inlay_fetch_ranges: impl Iterator<Item = InlayHintQuery>,
143        currently_shown_inlays: Vec<(Anchor, InlayId)>,
144        replace_old: bool,
145        cx: &mut ViewContext<Editor>,
146    ) -> Task<anyhow::Result<InlaySplice>> {
147        // TODO kb
148        todo!("TODO kb")
149    }
150
151    // fn fetch_inlays(
152    //     &mut self,
153    //     multi_buffer: ModelHandle<MultiBuffer>,
154    //     inlay_fetch_ranges: impl Iterator<Item = InlayHintQuery>,
155    //     replace_old: bool,
156    //     cx: &mut ViewContext<Editor>,
157    // ) -> Task<anyhow::Result<InlaySplice>> {
158    //     let mut inlay_fetch_tasks = Vec::new();
159    //     for inlay_fetch_range in inlay_fetch_ranges {
160    //         let inlays_up_to_date = self.inlays_up_to_date(
161    //             &inlay_fetch_range.buffer_path,
162    //             &inlay_fetch_range.buffer_version,
163    //             inlay_fetch_range.excerpt_id,
164    //         );
165    //         let task_multi_buffer = multi_buffer.clone();
166    //         let task = cx.spawn(|editor, mut cx| async move {
167    //             if inlays_up_to_date {
168    //                 anyhow::Ok((inlay_fetch_range, None))
169    //             } else {
170    //                 let Some(buffer_handle) = cx.read(|cx| task_multi_buffer.read(cx).buffer(inlay_fetch_range.buffer_id))
171    //                     else { return Ok((inlay_fetch_range, Some(Vec::new()))) };
172    //                 let task = editor
173    //                     .update(&mut cx, |editor, cx| {
174    //                         editor.project.as_ref().map(|project| {
175    //                             project.update(cx, |project, cx| {
176    //                                 project.query_inlay_hints_for_buffer(
177    //                                     buffer_handle,
178    //                                     inlay_fetch_range.excerpt_offset_query_range.clone(),
179    //                                     cx,
180    //                                 )
181    //                             })
182    //                         })
183    //                     })
184    //                     .context("inlays fecth task spawn")?;
185
186    //                 Ok((inlay_fetch_range, match task {
187    //                     Some(task) => task.await.context("inlays for buffer task")?,
188    //                     None => Some(Vec::new()),
189    //                 }))
190    //             }
191    //         });
192    //         inlay_fetch_tasks.push(task);
193    //     }
194
195    //     let final_task = cx.spawn(|editor, mut cx| async move {
196    //         let mut inlay_updates: HashMap<
197    //             PathBuf,
198    //             (
199    //                 Global,
200    //                 HashMap<ExcerptId, Option<(Range<usize>, OrderedByAnchorOffset<InlayHint>)>>,
201    //             ),
202    //         > = HashMap::default();
203    //         let multi_buffer_snapshot =
204    //             editor.read_with(&cx, |editor, cx| editor.buffer().read(cx).snapshot(cx))?;
205
206    //         for task_result in futures::future::join_all(inlay_fetch_tasks).await {
207    //             match task_result {
208    //                 Ok((inlay_fetch_range, response_inlays)) => {
209    //                     // TODO kb different caching now
210    //                     let inlays_per_excerpt = HashMap::from_iter([(
211    //                         inlay_fetch_range.excerpt_id,
212    //                         response_inlays
213    //                             .map(|excerpt_inlays| {
214    //                                 excerpt_inlays.into_iter().fold(
215    //                                     OrderedByAnchorOffset::default(),
216    //                                     |mut ordered_inlays, inlay| {
217    //                                         let anchor = multi_buffer_snapshot.anchor_in_excerpt(
218    //                                             inlay_fetch_range.excerpt_id,
219    //                                             inlay.position,
220    //                                         );
221    //                                         ordered_inlays.add(anchor, inlay);
222    //                                         ordered_inlays
223    //                                     },
224    //                                 )
225    //                             })
226    //                             .map(|inlays| {
227    //                                 (inlay_fetch_range.excerpt_offset_query_range, inlays)
228    //                             }),
229    //                     )]);
230    //                     match inlay_updates.entry(inlay_fetch_range.buffer_path) {
231    //                         hash_map::Entry::Occupied(mut o) => {
232    //                             o.get_mut().1.extend(inlays_per_excerpt);
233    //                         }
234    //                         hash_map::Entry::Vacant(v) => {
235    //                             v.insert((inlay_fetch_range.buffer_version, inlays_per_excerpt));
236    //                         }
237    //                     }
238    //                 }
239    //                 Err(e) => error!("Failed to update inlays for buffer: {e:#}"),
240    //             }
241    //         }
242
243    //         let updates = if !inlay_updates.is_empty() {
244    //             let inlays_update = editor.update(&mut cx, |editor, _| {
245    //                 editor.inlay_cache.apply_fetch_inlays(inlay_updates)
246    //             })?;
247    //             inlays_update
248    //         } else {
249    //             InlaySplice::default()
250    //         };
251
252    //         anyhow::Ok(updates)
253    //     });
254
255    //     final_task
256    // }
257
258    // fn inlays_up_to_date(
259    //     &self,
260    //     buffer_path: &Path,
261    //     buffer_version: &Global,
262    //     excerpt_id: ExcerptId,
263    // ) -> bool {
264    //     let Some(buffer_inlays) = self.inlays_per_buffer.get(buffer_path) else { return false };
265    //     let buffer_up_to_date = buffer_version == &buffer_inlays.buffer_version
266    //         || buffer_inlays.buffer_version.changed_since(&buffer_version);
267    //     buffer_up_to_date && buffer_inlays.inlays_per_excerpts.contains_key(&excerpt_id)
268    // }
269
270    // fn apply_fetch_inlays(
271    //     &mut self,
272    //     fetched_inlays: HashMap<
273    //         PathBuf,
274    //         (
275    //             Global,
276    //             HashMap<ExcerptId, Option<(Range<usize>, OrderedByAnchorOffset<InlayHint>)>>,
277    //         ),
278    //     >,
279    // ) -> InlaySplice {
280    //     let mut old_inlays = self.inlays_per_buffer.clone();
281    //     let mut to_remove = Vec::new();
282    //     let mut to_insert = Vec::new();
283
284    //     for (buffer_path, (buffer_version, new_buffer_inlays)) in fetched_inlays {
285    //         match old_inlays.remove(&buffer_path) {
286    //             Some(mut old_buffer_inlays) => {
287    //                 for (excerpt_id, new_excerpt_inlays) in new_buffer_inlays {
288    //                     let (_, mut new_excerpt_inlays) = match new_excerpt_inlays {
289    //                         Some((excerpt_offset_range, new_inlays)) => (
290    //                             excerpt_offset_range,
291    //                             new_inlays.into_ordered_elements().fuse().peekable(),
292    //                         ),
293    //                         None => continue,
294    //                     };
295    //                     if self.inlays_up_to_date(&buffer_path, &buffer_version, excerpt_id) {
296    //                         continue;
297    //                     }
298
299    //                     let self_inlays_per_buffer = self
300    //                         .inlays_per_buffer
301    //                         .get_mut(&buffer_path)
302    //                         .expect("element expected: `old_inlays.remove` returned `Some`");
303
304    //                     if old_buffer_inlays
305    //                         .inlays_per_excerpts
306    //                         .remove(&excerpt_id)
307    //                         .is_some()
308    //                     {
309    //                         let self_excerpt_inlays = self_inlays_per_buffer
310    //                             .inlays_per_excerpts
311    //                             .get_mut(&excerpt_id)
312    //                             .expect("element expected: `old_excerpt_inlays` is `Some`");
313    //                         let mut hints_to_add = Vec::<(Anchor, (InlayId, InlayHint))>::new();
314    //                         // TODO kb update inner buffer_id and version with the new data?
315    //                         self_excerpt_inlays.0.retain(
316    //                             |_, (old_anchor, (old_inlay_id, old_inlay))| {
317    //                                 let mut retain = false;
318
319    //                                 while let Some(new_offset) = new_excerpt_inlays
320    //                                     .peek()
321    //                                     .map(|(new_anchor, _)| new_anchor.text_anchor.offset)
322    //                                 {
323    //                                     let old_offset = old_anchor.text_anchor.offset;
324    //                                     match new_offset.cmp(&old_offset) {
325    //                                         cmp::Ordering::Less => {
326    //                                             let (new_anchor, new_inlay) =
327    //                                                 new_excerpt_inlays.next().expect(
328    //                                                     "element expected: `peek` returned `Some`",
329    //                                                 );
330    //                                             hints_to_add.push((
331    //                                                 new_anchor,
332    //                                                 (
333    //                                                     InlayId(post_inc(&mut self.next_inlay_id)),
334    //                                                     new_inlay,
335    //                                                 ),
336    //                                             ));
337    //                                         }
338    //                                         cmp::Ordering::Equal => {
339    //                                             let (new_anchor, new_inlay) =
340    //                                                 new_excerpt_inlays.next().expect(
341    //                                                     "element expected: `peek` returned `Some`",
342    //                                                 );
343    //                                             if &new_inlay == old_inlay {
344    //                                                 retain = true;
345    //                                             } else {
346    //                                                 hints_to_add.push((
347    //                                                     new_anchor,
348    //                                                     (
349    //                                                         InlayId(post_inc(
350    //                                                             &mut self.next_inlay_id,
351    //                                                         )),
352    //                                                         new_inlay,
353    //                                                     ),
354    //                                                 ));
355    //                                             }
356    //                                         }
357    //                                         cmp::Ordering::Greater => break,
358    //                                     }
359    //                                 }
360
361    //                                 if !retain {
362    //                                     to_remove.push(*old_inlay_id);
363    //                                 }
364    //                                 retain
365    //                             },
366    //                         );
367
368    //                         for (new_anchor, (id, new_inlay)) in hints_to_add {
369    //                             self_excerpt_inlays.add(new_anchor, (id, new_inlay.clone()));
370    //                             to_insert.push((id, new_anchor, new_inlay));
371    //                         }
372    //                     }
373
374    //                     for (new_anchor, new_inlay) in new_excerpt_inlays {
375    //                         let id = InlayId(post_inc(&mut self.next_inlay_id));
376    //                         self_inlays_per_buffer
377    //                             .inlays_per_excerpts
378    //                             .entry(excerpt_id)
379    //                             .or_default()
380    //                             .add(new_anchor, (id, new_inlay.clone()));
381    //                         to_insert.push((id, new_anchor, new_inlay));
382    //                     }
383    //                 }
384    //             }
385    //             None => {
386    //                 let mut inlays_per_excerpts: HashMap<
387    //                     ExcerptId,
388    //                     OrderedByAnchorOffset<(InlayId, InlayHint)>,
389    //                 > = HashMap::default();
390    //                 for (new_excerpt_id, new_ordered_inlays) in new_buffer_inlays {
391    //                     if let Some((_, new_ordered_inlays)) = new_ordered_inlays {
392    //                         for (new_anchor, new_inlay) in
393    //                             new_ordered_inlays.into_ordered_elements()
394    //                         {
395    //                             let id = InlayId(post_inc(&mut self.next_inlay_id));
396    //                             inlays_per_excerpts
397    //                                 .entry(new_excerpt_id)
398    //                                 .or_default()
399    //                                 .add(new_anchor, (id, new_inlay.clone()));
400    //                             to_insert.push((id, new_anchor, new_inlay));
401    //                         }
402    //                     }
403    //                 }
404    //                 self.inlays_per_buffer.insert(
405    //                     buffer_path,
406    //                     BufferInlays {
407    //                         buffer_version,
408    //                         inlays_per_excerpts,
409    //                     },
410    //                 );
411    //             }
412    //         }
413    //     }
414
415    //     for (_, old_buffer_inlays) in old_inlays {
416    //         for (_, old_excerpt_inlays) in old_buffer_inlays.inlays_per_excerpts {
417    //             for (_, (id_to_remove, _)) in old_excerpt_inlays.into_ordered_elements() {
418    //                 to_remove.push(id_to_remove);
419    //             }
420    //         }
421    //     }
422
423    //     to_insert.retain(|(_, _, new_hint)| self.allowed_hint_kinds.contains(&new_hint.kind));
424
425    //     InlaySplice {
426    //         to_remove,
427    //         to_insert,
428    //     }
429    // }
430}
431
432fn allowed_inlay_hint_types(
433    inlay_hint_settings: editor_settings::InlayHints,
434) -> HashSet<Option<InlayHintKind>> {
435    let mut new_allowed_inlay_hint_types = HashSet::default();
436    if inlay_hint_settings.show_type_hints {
437        new_allowed_inlay_hint_types.insert(Some(InlayHintKind::Type));
438    }
439    if inlay_hint_settings.show_parameter_hints {
440        new_allowed_inlay_hint_types.insert(Some(InlayHintKind::Parameter));
441    }
442    if inlay_hint_settings.show_other_hints {
443        new_allowed_inlay_hint_types.insert(None);
444    }
445    new_allowed_inlay_hint_types
446}