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}