1use std::{
2 cmp,
3 ops::{ControlFlow, Range},
4 sync::Arc,
5 time::Duration,
6};
7
8use crate::{
9 display_map::Inlay, Anchor, Editor, ExcerptId, InlayId, MultiBuffer, MultiBufferSnapshot,
10};
11use anyhow::Context;
12use clock::Global;
13use futures::future;
14use gpui::{Model, ModelContext, Task, ViewContext};
15use language::{language_settings::InlayHintKind, Buffer, BufferSnapshot};
16use parking_lot::RwLock;
17use project::{InlayHint, ResolveState};
18
19use collections::{hash_map, HashMap, HashSet};
20use language::language_settings::InlayHintSettings;
21use smol::lock::Semaphore;
22use sum_tree::Bias;
23use text::{ToOffset, ToPoint};
24use util::post_inc;
25
26pub struct InlayHintCache {
27 hints: HashMap<ExcerptId, Arc<RwLock<CachedExcerptHints>>>,
28 allowed_hint_kinds: HashSet<Option<InlayHintKind>>,
29 version: usize,
30 pub(super) enabled: bool,
31 update_tasks: HashMap<ExcerptId, TasksForRanges>,
32 lsp_request_limiter: Arc<Semaphore>,
33}
34
35#[derive(Debug)]
36struct TasksForRanges {
37 tasks: Vec<Task<()>>,
38 sorted_ranges: Vec<Range<language::Anchor>>,
39}
40
41#[derive(Debug)]
42pub struct CachedExcerptHints {
43 version: usize,
44 buffer_version: Global,
45 buffer_id: u64,
46 ordered_hints: Vec<InlayId>,
47 hints_by_id: HashMap<InlayId, InlayHint>,
48}
49
50#[derive(Debug, Clone, Copy)]
51pub enum InvalidationStrategy {
52 RefreshRequested,
53 BufferEdited,
54 None,
55}
56
57#[derive(Debug, Default)]
58pub struct InlaySplice {
59 pub to_remove: Vec<InlayId>,
60 pub to_insert: Vec<Inlay>,
61}
62
63#[derive(Debug)]
64struct ExcerptHintsUpdate {
65 excerpt_id: ExcerptId,
66 remove_from_visible: Vec<InlayId>,
67 remove_from_cache: HashSet<InlayId>,
68 add_to_cache: Vec<InlayHint>,
69}
70
71#[derive(Debug, Clone, Copy)]
72struct ExcerptQuery {
73 buffer_id: u64,
74 excerpt_id: ExcerptId,
75 cache_version: usize,
76 invalidate: InvalidationStrategy,
77 reason: &'static str,
78}
79
80impl InvalidationStrategy {
81 fn should_invalidate(&self) -> bool {
82 matches!(
83 self,
84 InvalidationStrategy::RefreshRequested | InvalidationStrategy::BufferEdited
85 )
86 }
87}
88
89impl TasksForRanges {
90 fn new(query_ranges: QueryRanges, task: Task<()>) -> Self {
91 let mut sorted_ranges = Vec::new();
92 sorted_ranges.extend(query_ranges.before_visible);
93 sorted_ranges.extend(query_ranges.visible);
94 sorted_ranges.extend(query_ranges.after_visible);
95 Self {
96 tasks: vec![task],
97 sorted_ranges,
98 }
99 }
100
101 fn update_cached_tasks(
102 &mut self,
103 buffer_snapshot: &BufferSnapshot,
104 query_ranges: QueryRanges,
105 invalidate: InvalidationStrategy,
106 spawn_task: impl FnOnce(QueryRanges) -> Task<()>,
107 ) {
108 let query_ranges = if invalidate.should_invalidate() {
109 self.tasks.clear();
110 self.sorted_ranges.clear();
111 query_ranges
112 } else {
113 let mut non_cached_query_ranges = query_ranges;
114 non_cached_query_ranges.before_visible = non_cached_query_ranges
115 .before_visible
116 .into_iter()
117 .flat_map(|query_range| {
118 self.remove_cached_ranges_from_query(buffer_snapshot, query_range)
119 })
120 .collect();
121 non_cached_query_ranges.visible = non_cached_query_ranges
122 .visible
123 .into_iter()
124 .flat_map(|query_range| {
125 self.remove_cached_ranges_from_query(buffer_snapshot, query_range)
126 })
127 .collect();
128 non_cached_query_ranges.after_visible = non_cached_query_ranges
129 .after_visible
130 .into_iter()
131 .flat_map(|query_range| {
132 self.remove_cached_ranges_from_query(buffer_snapshot, query_range)
133 })
134 .collect();
135 non_cached_query_ranges
136 };
137
138 if !query_ranges.is_empty() {
139 self.tasks.push(spawn_task(query_ranges));
140 }
141 }
142
143 fn remove_cached_ranges_from_query(
144 &mut self,
145 buffer_snapshot: &BufferSnapshot,
146 query_range: Range<language::Anchor>,
147 ) -> Vec<Range<language::Anchor>> {
148 let mut ranges_to_query = Vec::new();
149 let mut latest_cached_range = None::<&mut Range<language::Anchor>>;
150 for cached_range in self
151 .sorted_ranges
152 .iter_mut()
153 .skip_while(|cached_range| {
154 cached_range
155 .end
156 .cmp(&query_range.start, buffer_snapshot)
157 .is_lt()
158 })
159 .take_while(|cached_range| {
160 cached_range
161 .start
162 .cmp(&query_range.end, buffer_snapshot)
163 .is_le()
164 })
165 {
166 match latest_cached_range {
167 Some(latest_cached_range) => {
168 if latest_cached_range.end.offset.saturating_add(1) < cached_range.start.offset
169 {
170 ranges_to_query.push(latest_cached_range.end..cached_range.start);
171 cached_range.start = latest_cached_range.end;
172 }
173 }
174 None => {
175 if query_range
176 .start
177 .cmp(&cached_range.start, buffer_snapshot)
178 .is_lt()
179 {
180 ranges_to_query.push(query_range.start..cached_range.start);
181 cached_range.start = query_range.start;
182 }
183 }
184 }
185 latest_cached_range = Some(cached_range);
186 }
187
188 match latest_cached_range {
189 Some(latest_cached_range) => {
190 if latest_cached_range.end.offset.saturating_add(1) < query_range.end.offset {
191 ranges_to_query.push(latest_cached_range.end..query_range.end);
192 latest_cached_range.end = query_range.end;
193 }
194 }
195 None => {
196 ranges_to_query.push(query_range.clone());
197 self.sorted_ranges.push(query_range);
198 self.sorted_ranges
199 .sort_by(|range_a, range_b| range_a.start.cmp(&range_b.start, buffer_snapshot));
200 }
201 }
202
203 ranges_to_query
204 }
205
206 fn invalidate_range(&mut self, buffer: &BufferSnapshot, range: &Range<language::Anchor>) {
207 self.sorted_ranges = self
208 .sorted_ranges
209 .drain(..)
210 .filter_map(|mut cached_range| {
211 if cached_range.start.cmp(&range.end, buffer).is_gt()
212 || cached_range.end.cmp(&range.start, buffer).is_lt()
213 {
214 Some(vec![cached_range])
215 } else if cached_range.start.cmp(&range.start, buffer).is_ge()
216 && cached_range.end.cmp(&range.end, buffer).is_le()
217 {
218 None
219 } else if range.start.cmp(&cached_range.start, buffer).is_ge()
220 && range.end.cmp(&cached_range.end, buffer).is_le()
221 {
222 Some(vec![
223 cached_range.start..range.start,
224 range.end..cached_range.end,
225 ])
226 } else if cached_range.start.cmp(&range.start, buffer).is_ge() {
227 cached_range.start = range.end;
228 Some(vec![cached_range])
229 } else {
230 cached_range.end = range.start;
231 Some(vec![cached_range])
232 }
233 })
234 .flatten()
235 .collect();
236 }
237}
238
239impl InlayHintCache {
240 pub fn new(inlay_hint_settings: InlayHintSettings) -> Self {
241 Self {
242 allowed_hint_kinds: inlay_hint_settings.enabled_inlay_hint_kinds(),
243 enabled: inlay_hint_settings.enabled,
244 hints: HashMap::default(),
245 update_tasks: HashMap::default(),
246 version: 0,
247 lsp_request_limiter: Arc::new(Semaphore::new(MAX_CONCURRENT_LSP_REQUESTS)),
248 }
249 }
250
251 pub fn update_settings(
252 &mut self,
253 multi_buffer: &Model<MultiBuffer>,
254 new_hint_settings: InlayHintSettings,
255 visible_hints: Vec<Inlay>,
256 cx: &mut ViewContext<Editor>,
257 ) -> ControlFlow<Option<InlaySplice>> {
258 let new_allowed_hint_kinds = new_hint_settings.enabled_inlay_hint_kinds();
259 match (self.enabled, new_hint_settings.enabled) {
260 (false, false) => {
261 self.allowed_hint_kinds = new_allowed_hint_kinds;
262 ControlFlow::Break(None)
263 }
264 (true, true) => {
265 if new_allowed_hint_kinds == self.allowed_hint_kinds {
266 ControlFlow::Break(None)
267 } else {
268 let new_splice = self.new_allowed_hint_kinds_splice(
269 multi_buffer,
270 &visible_hints,
271 &new_allowed_hint_kinds,
272 cx,
273 );
274 if new_splice.is_some() {
275 self.version += 1;
276 self.allowed_hint_kinds = new_allowed_hint_kinds;
277 }
278 ControlFlow::Break(new_splice)
279 }
280 }
281 (true, false) => {
282 self.enabled = new_hint_settings.enabled;
283 self.allowed_hint_kinds = new_allowed_hint_kinds;
284 if self.hints.is_empty() {
285 ControlFlow::Break(None)
286 } else {
287 self.clear();
288 ControlFlow::Break(Some(InlaySplice {
289 to_remove: visible_hints.iter().map(|inlay| inlay.id).collect(),
290 to_insert: Vec::new(),
291 }))
292 }
293 }
294 (false, true) => {
295 self.enabled = new_hint_settings.enabled;
296 self.allowed_hint_kinds = new_allowed_hint_kinds;
297 ControlFlow::Continue(())
298 }
299 }
300 }
301
302 pub fn spawn_hint_refresh(
303 &mut self,
304 reason: &'static str,
305 excerpts_to_query: HashMap<ExcerptId, (Model<Buffer>, Global, Range<usize>)>,
306 invalidate: InvalidationStrategy,
307 cx: &mut ViewContext<Editor>,
308 ) -> Option<InlaySplice> {
309 if !self.enabled {
310 return None;
311 }
312
313 let mut invalidated_hints = Vec::new();
314 if invalidate.should_invalidate() {
315 self.update_tasks
316 .retain(|task_excerpt_id, _| excerpts_to_query.contains_key(task_excerpt_id));
317 self.hints.retain(|cached_excerpt, cached_hints| {
318 let retain = excerpts_to_query.contains_key(cached_excerpt);
319 if !retain {
320 invalidated_hints.extend(cached_hints.read().ordered_hints.iter().copied());
321 }
322 retain
323 });
324 }
325 if excerpts_to_query.is_empty() && invalidated_hints.is_empty() {
326 return None;
327 }
328
329 let cache_version = self.version + 1;
330 cx.spawn(|editor, mut cx| async move {
331 editor
332 .update(&mut cx, |editor, cx| {
333 spawn_new_update_tasks(
334 editor,
335 reason,
336 excerpts_to_query,
337 invalidate,
338 cache_version,
339 cx,
340 )
341 })
342 .ok();
343 })
344 .detach();
345
346 if invalidated_hints.is_empty() {
347 None
348 } else {
349 Some(InlaySplice {
350 to_remove: invalidated_hints,
351 to_insert: Vec::new(),
352 })
353 }
354 }
355
356 fn new_allowed_hint_kinds_splice(
357 &self,
358 multi_buffer: &Model<MultiBuffer>,
359 visible_hints: &[Inlay],
360 new_kinds: &HashSet<Option<InlayHintKind>>,
361 cx: &mut ViewContext<Editor>,
362 ) -> Option<InlaySplice> {
363 let old_kinds = &self.allowed_hint_kinds;
364 if new_kinds == old_kinds {
365 return None;
366 }
367
368 let mut to_remove = Vec::new();
369 let mut to_insert = Vec::new();
370 let mut shown_hints_to_remove = visible_hints.iter().fold(
371 HashMap::<ExcerptId, Vec<(Anchor, InlayId)>>::default(),
372 |mut current_hints, inlay| {
373 current_hints
374 .entry(inlay.position.excerpt_id)
375 .or_default()
376 .push((inlay.position, inlay.id));
377 current_hints
378 },
379 );
380
381 let multi_buffer = multi_buffer.read(cx);
382 let multi_buffer_snapshot = multi_buffer.snapshot(cx);
383
384 for (excerpt_id, excerpt_cached_hints) in &self.hints {
385 let shown_excerpt_hints_to_remove =
386 shown_hints_to_remove.entry(*excerpt_id).or_default();
387 let excerpt_cached_hints = excerpt_cached_hints.read();
388 let mut excerpt_cache = excerpt_cached_hints.ordered_hints.iter().fuse().peekable();
389 shown_excerpt_hints_to_remove.retain(|(shown_anchor, shown_hint_id)| {
390 let Some(buffer) = shown_anchor
391 .buffer_id
392 .and_then(|buffer_id| multi_buffer.buffer(buffer_id))
393 else {
394 return false;
395 };
396 let buffer_snapshot = buffer.read(cx).snapshot();
397 loop {
398 match excerpt_cache.peek() {
399 Some(&cached_hint_id) => {
400 let cached_hint = &excerpt_cached_hints.hints_by_id[cached_hint_id];
401 if cached_hint_id == shown_hint_id {
402 excerpt_cache.next();
403 return !new_kinds.contains(&cached_hint.kind);
404 }
405
406 match cached_hint
407 .position
408 .cmp(&shown_anchor.text_anchor, &buffer_snapshot)
409 {
410 cmp::Ordering::Less | cmp::Ordering::Equal => {
411 if !old_kinds.contains(&cached_hint.kind)
412 && new_kinds.contains(&cached_hint.kind)
413 {
414 to_insert.push(Inlay::hint(
415 cached_hint_id.id(),
416 multi_buffer_snapshot.anchor_in_excerpt(
417 *excerpt_id,
418 cached_hint.position,
419 ),
420 &cached_hint,
421 ));
422 }
423 excerpt_cache.next();
424 }
425 cmp::Ordering::Greater => return true,
426 }
427 }
428 None => return true,
429 }
430 }
431 });
432
433 for cached_hint_id in excerpt_cache {
434 let maybe_missed_cached_hint = &excerpt_cached_hints.hints_by_id[cached_hint_id];
435 let cached_hint_kind = maybe_missed_cached_hint.kind;
436 if !old_kinds.contains(&cached_hint_kind) && new_kinds.contains(&cached_hint_kind) {
437 to_insert.push(Inlay::hint(
438 cached_hint_id.id(),
439 multi_buffer_snapshot
440 .anchor_in_excerpt(*excerpt_id, maybe_missed_cached_hint.position),
441 &maybe_missed_cached_hint,
442 ));
443 }
444 }
445 }
446
447 to_remove.extend(
448 shown_hints_to_remove
449 .into_values()
450 .flatten()
451 .map(|(_, hint_id)| hint_id),
452 );
453 if to_remove.is_empty() && to_insert.is_empty() {
454 None
455 } else {
456 Some(InlaySplice {
457 to_remove,
458 to_insert,
459 })
460 }
461 }
462
463 pub fn remove_excerpts(&mut self, excerpts_removed: Vec<ExcerptId>) -> Option<InlaySplice> {
464 let mut to_remove = Vec::new();
465 for excerpt_to_remove in excerpts_removed {
466 self.update_tasks.remove(&excerpt_to_remove);
467 if let Some(cached_hints) = self.hints.remove(&excerpt_to_remove) {
468 let cached_hints = cached_hints.read();
469 to_remove.extend(cached_hints.ordered_hints.iter().copied());
470 }
471 }
472 if to_remove.is_empty() {
473 None
474 } else {
475 self.version += 1;
476 Some(InlaySplice {
477 to_remove,
478 to_insert: Vec::new(),
479 })
480 }
481 }
482
483 pub fn clear(&mut self) {
484 if !self.update_tasks.is_empty() || !self.hints.is_empty() {
485 self.version += 1;
486 }
487 self.update_tasks.clear();
488 self.hints.clear();
489 }
490
491 pub fn hint_by_id(&self, excerpt_id: ExcerptId, hint_id: InlayId) -> Option<InlayHint> {
492 self.hints
493 .get(&excerpt_id)?
494 .read()
495 .hints_by_id
496 .get(&hint_id)
497 .cloned()
498 }
499
500 pub fn hints(&self) -> Vec<InlayHint> {
501 let mut hints = Vec::new();
502 for excerpt_hints in self.hints.values() {
503 let excerpt_hints = excerpt_hints.read();
504 hints.extend(
505 excerpt_hints
506 .ordered_hints
507 .iter()
508 .map(|id| &excerpt_hints.hints_by_id[id])
509 .cloned(),
510 );
511 }
512 hints
513 }
514
515 pub fn version(&self) -> usize {
516 self.version
517 }
518
519 pub fn spawn_hint_resolve(
520 &self,
521 buffer_id: u64,
522 excerpt_id: ExcerptId,
523 id: InlayId,
524 cx: &mut ViewContext<'_, Editor>,
525 ) {
526 if let Some(excerpt_hints) = self.hints.get(&excerpt_id) {
527 let mut guard = excerpt_hints.write();
528 if let Some(cached_hint) = guard.hints_by_id.get_mut(&id) {
529 if let ResolveState::CanResolve(server_id, _) = &cached_hint.resolve_state {
530 let hint_to_resolve = cached_hint.clone();
531 let server_id = *server_id;
532 cached_hint.resolve_state = ResolveState::Resolving;
533 drop(guard);
534 cx.spawn(|editor, mut cx| async move {
535 let resolved_hint_task = editor.update(&mut cx, |editor, cx| {
536 editor
537 .buffer()
538 .read(cx)
539 .buffer(buffer_id)
540 .and_then(|buffer| {
541 let project = editor.project.as_ref()?;
542 Some(project.update(cx, |project, cx| {
543 project.resolve_inlay_hint(
544 hint_to_resolve,
545 buffer,
546 server_id,
547 cx,
548 )
549 }))
550 })
551 })?;
552 if let Some(resolved_hint_task) = resolved_hint_task {
553 let mut resolved_hint =
554 resolved_hint_task.await.context("hint resolve task")?;
555 editor.update(&mut cx, |editor, _| {
556 todo!()
557 // if let Some(excerpt_hints) =
558 // editor.inlay_hint_cache.hints.get(&excerpt_id)
559 // {
560 // let mut guard = excerpt_hints.write();
561 // if let Some(cached_hint) = guard.hints_by_id.get_mut(&id) {
562 // if cached_hint.resolve_state == ResolveState::Resolving {
563 // resolved_hint.resolve_state = ResolveState::Resolved;
564 // *cached_hint = resolved_hint;
565 // }
566 // }
567 // }
568 })?;
569 }
570
571 anyhow::Ok(())
572 })
573 .detach_and_log_err(cx);
574 }
575 }
576 }
577 }
578}
579
580fn spawn_new_update_tasks(
581 editor: &mut Editor,
582 reason: &'static str,
583 excerpts_to_query: HashMap<ExcerptId, (Model<Buffer>, Global, Range<usize>)>,
584 invalidate: InvalidationStrategy,
585 update_cache_version: usize,
586 cx: &mut ViewContext<'_, Editor>,
587) {
588 todo!("old version below");
589}
590// let visible_hints = Arc::new(editor.visible_inlay_hints(cx));
591// for (excerpt_id, (excerpt_buffer, new_task_buffer_version, excerpt_visible_range)) in
592// excerpts_to_query
593// {
594// if excerpt_visible_range.is_empty() {
595// continue;
596// }
597// let buffer = excerpt_buffer.read(cx);
598// let buffer_id = buffer.remote_id();
599// let buffer_snapshot = buffer.snapshot();
600// if buffer_snapshot
601// .version()
602// .changed_since(&new_task_buffer_version)
603// {
604// continue;
605// }
606
607// let cached_excerpt_hints = editor.inlay_hint_cache.hints.get(&excerpt_id).cloned();
608// if let Some(cached_excerpt_hints) = &cached_excerpt_hints {
609// let cached_excerpt_hints = cached_excerpt_hints.read();
610// let cached_buffer_version = &cached_excerpt_hints.buffer_version;
611// if cached_excerpt_hints.version > update_cache_version
612// || cached_buffer_version.changed_since(&new_task_buffer_version)
613// {
614// continue;
615// }
616// };
617
618// let (multi_buffer_snapshot, Some(query_ranges)) =
619// editor.buffer.update(cx, |multi_buffer, cx| {
620// (
621// multi_buffer.snapshot(cx),
622// determine_query_ranges(
623// multi_buffer,
624// excerpt_id,
625// &excerpt_buffer,
626// excerpt_visible_range,
627// cx,
628// ),
629// )
630// })
631// else {
632// return;
633// };
634// let query = ExcerptQuery {
635// buffer_id,
636// excerpt_id,
637// cache_version: update_cache_version,
638// invalidate,
639// reason,
640// };
641
642// let new_update_task = |query_ranges| {
643// new_update_task(
644// query,
645// query_ranges,
646// multi_buffer_snapshot,
647// buffer_snapshot.clone(),
648// Arc::clone(&visible_hints),
649// cached_excerpt_hints,
650// Arc::clone(&editor.inlay_hint_cache.lsp_request_limiter),
651// cx,
652// )
653// };
654
655// match editor.inlay_hint_cache.update_tasks.entry(excerpt_id) {
656// hash_map::Entry::Occupied(mut o) => {
657// o.get_mut().update_cached_tasks(
658// &buffer_snapshot,
659// query_ranges,
660// invalidate,
661// new_update_task,
662// );
663// }
664// hash_map::Entry::Vacant(v) => {
665// v.insert(TasksForRanges::new(
666// query_ranges.clone(),
667// new_update_task(query_ranges),
668// ));
669// }
670// }
671// }
672// }
673
674#[derive(Debug, Clone)]
675struct QueryRanges {
676 before_visible: Vec<Range<language::Anchor>>,
677 visible: Vec<Range<language::Anchor>>,
678 after_visible: Vec<Range<language::Anchor>>,
679}
680
681impl QueryRanges {
682 fn is_empty(&self) -> bool {
683 self.before_visible.is_empty() && self.visible.is_empty() && self.after_visible.is_empty()
684 }
685}
686
687fn determine_query_ranges(
688 multi_buffer: &mut MultiBuffer,
689 excerpt_id: ExcerptId,
690 excerpt_buffer: &Model<Buffer>,
691 excerpt_visible_range: Range<usize>,
692 cx: &mut ModelContext<'_, MultiBuffer>,
693) -> Option<QueryRanges> {
694 let full_excerpt_range = multi_buffer
695 .excerpts_for_buffer(excerpt_buffer, cx)
696 .into_iter()
697 .find(|(id, _)| id == &excerpt_id)
698 .map(|(_, range)| range.context)?;
699 let buffer = excerpt_buffer.read(cx);
700 let snapshot = buffer.snapshot();
701 let excerpt_visible_len = excerpt_visible_range.end - excerpt_visible_range.start;
702
703 let visible_range = if excerpt_visible_range.start == excerpt_visible_range.end {
704 return None;
705 } else {
706 vec![
707 buffer.anchor_before(snapshot.clip_offset(excerpt_visible_range.start, Bias::Left))
708 ..buffer.anchor_after(snapshot.clip_offset(excerpt_visible_range.end, Bias::Right)),
709 ]
710 };
711
712 let full_excerpt_range_end_offset = full_excerpt_range.end.to_offset(&snapshot);
713 let after_visible_range_start = excerpt_visible_range
714 .end
715 .saturating_add(1)
716 .min(full_excerpt_range_end_offset)
717 .min(buffer.len());
718 let after_visible_range = if after_visible_range_start == full_excerpt_range_end_offset {
719 Vec::new()
720 } else {
721 let after_range_end_offset = after_visible_range_start
722 .saturating_add(excerpt_visible_len)
723 .min(full_excerpt_range_end_offset)
724 .min(buffer.len());
725 vec![
726 buffer.anchor_before(snapshot.clip_offset(after_visible_range_start, Bias::Left))
727 ..buffer.anchor_after(snapshot.clip_offset(after_range_end_offset, Bias::Right)),
728 ]
729 };
730
731 let full_excerpt_range_start_offset = full_excerpt_range.start.to_offset(&snapshot);
732 let before_visible_range_end = excerpt_visible_range
733 .start
734 .saturating_sub(1)
735 .max(full_excerpt_range_start_offset);
736 let before_visible_range = if before_visible_range_end == full_excerpt_range_start_offset {
737 Vec::new()
738 } else {
739 let before_range_start_offset = before_visible_range_end
740 .saturating_sub(excerpt_visible_len)
741 .max(full_excerpt_range_start_offset);
742 vec![
743 buffer.anchor_before(snapshot.clip_offset(before_range_start_offset, Bias::Left))
744 ..buffer.anchor_after(snapshot.clip_offset(before_visible_range_end, Bias::Right)),
745 ]
746 };
747
748 Some(QueryRanges {
749 before_visible: before_visible_range,
750 visible: visible_range,
751 after_visible: after_visible_range,
752 })
753}
754
755const MAX_CONCURRENT_LSP_REQUESTS: usize = 5;
756const INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS: u64 = 400;
757
758fn new_update_task(
759 query: ExcerptQuery,
760 query_ranges: QueryRanges,
761 multi_buffer_snapshot: MultiBufferSnapshot,
762 buffer_snapshot: BufferSnapshot,
763 visible_hints: Arc<Vec<Inlay>>,
764 cached_excerpt_hints: Option<Arc<RwLock<CachedExcerptHints>>>,
765 lsp_request_limiter: Arc<Semaphore>,
766 cx: &mut ViewContext<'_, Editor>,
767) -> Task<()> {
768 todo!()
769 // cx.spawn(|editor, mut cx| async move {
770 // let closure_cx = cx.clone();
771 // let fetch_and_update_hints = |invalidate, range| {
772 // fetch_and_update_hints(
773 // editor.clone(),
774 // multi_buffer_snapshot.clone(),
775 // buffer_snapshot.clone(),
776 // Arc::clone(&visible_hints),
777 // cached_excerpt_hints.as_ref().map(Arc::clone),
778 // query,
779 // invalidate,
780 // range,
781 // Arc::clone(&lsp_request_limiter),
782 // closure_cx.clone(),
783 // )
784 // };
785 // let visible_range_update_results = future::join_all(query_ranges.visible.into_iter().map(
786 // |visible_range| async move {
787 // (
788 // visible_range.clone(),
789 // fetch_and_update_hints(query.invalidate.should_invalidate(), visible_range)
790 // .await,
791 // )
792 // },
793 // ))
794 // .await;
795
796 // let hint_delay = cx.background().timer(Duration::from_millis(
797 // INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS,
798 // ));
799
800 // let mut query_range_failed = |range: &Range<language::Anchor>, e: anyhow::Error| {
801 // log::error!("inlay hint update task for range {range:?} failed: {e:#}");
802 // editor
803 // .update(&mut cx, |editor, _| {
804 // if let Some(task_ranges) = editor
805 // .inlay_hint_cache
806 // .update_tasks
807 // .get_mut(&query.excerpt_id)
808 // {
809 // task_ranges.invalidate_range(&buffer_snapshot, &range);
810 // }
811 // })
812 // .ok()
813 // };
814
815 // for (range, result) in visible_range_update_results {
816 // if let Err(e) = result {
817 // query_range_failed(&range, e);
818 // }
819 // }
820
821 // hint_delay.await;
822 // let invisible_range_update_results = future::join_all(
823 // query_ranges
824 // .before_visible
825 // .into_iter()
826 // .chain(query_ranges.after_visible.into_iter())
827 // .map(|invisible_range| async move {
828 // (
829 // invisible_range.clone(),
830 // fetch_and_update_hints(false, invisible_range).await,
831 // )
832 // }),
833 // )
834 // .await;
835 // for (range, result) in invisible_range_update_results {
836 // if let Err(e) = result {
837 // query_range_failed(&range, e);
838 // }
839 // }
840 // })
841}
842
843// async fn fetch_and_update_hints(
844// editor: gpui::WeakView<Editor>,
845// multi_buffer_snapshot: MultiBufferSnapshot,
846// buffer_snapshot: BufferSnapshot,
847// visible_hints: Arc<Vec<Inlay>>,
848// cached_excerpt_hints: Option<Arc<RwLock<CachedExcerptHints>>>,
849// query: ExcerptQuery,
850// invalidate: bool,
851// fetch_range: Range<language::Anchor>,
852// lsp_request_limiter: Arc<Semaphore>,
853// mut cx: gpui::AsyncAppContext,
854// ) -> anyhow::Result<()> {
855// let (lsp_request_guard, got_throttled) = if query.invalidate.should_invalidate() {
856// (None, false)
857// } else {
858// match lsp_request_limiter.try_acquire() {
859// Some(guard) => (Some(guard), false),
860// None => (Some(lsp_request_limiter.acquire().await), true),
861// }
862// };
863// let fetch_range_to_log =
864// fetch_range.start.to_point(&buffer_snapshot)..fetch_range.end.to_point(&buffer_snapshot);
865// let inlay_hints_fetch_task = editor
866// .update(&mut cx, |editor, cx| {
867// if got_throttled {
868// let query_not_around_visible_range = match editor.excerpt_visible_offsets(None, cx).remove(&query.excerpt_id) {
869// Some((_, _, current_visible_range)) => {
870// let visible_offset_length = current_visible_range.len();
871// let double_visible_range = current_visible_range
872// .start
873// .saturating_sub(visible_offset_length)
874// ..current_visible_range
875// .end
876// .saturating_add(visible_offset_length)
877// .min(buffer_snapshot.len());
878// !double_visible_range
879// .contains(&fetch_range.start.to_offset(&buffer_snapshot))
880// && !double_visible_range
881// .contains(&fetch_range.end.to_offset(&buffer_snapshot))
882// },
883// None => true,
884// };
885// if query_not_around_visible_range {
886// log::trace!("Fetching inlay hints for range {fetch_range_to_log:?} got throttled and fell off the current visible range, skipping.");
887// if let Some(task_ranges) = editor
888// .inlay_hint_cache
889// .update_tasks
890// .get_mut(&query.excerpt_id)
891// {
892// task_ranges.invalidate_range(&buffer_snapshot, &fetch_range);
893// }
894// return None;
895// }
896// }
897// editor
898// .buffer()
899// .read(cx)
900// .buffer(query.buffer_id)
901// .and_then(|buffer| {
902// let project = editor.project.as_ref()?;
903// Some(project.update(cx, |project, cx| {
904// project.inlay_hints(buffer, fetch_range.clone(), cx)
905// }))
906// })
907// })
908// .ok()
909// .flatten();
910// let new_hints = match inlay_hints_fetch_task {
911// Some(fetch_task) => {
912// log::debug!(
913// "Fetching inlay hints for range {fetch_range_to_log:?}, reason: {query_reason}, invalidate: {invalidate}",
914// query_reason = query.reason,
915// );
916// log::trace!(
917// "Currently visible hints: {visible_hints:?}, cached hints present: {}",
918// cached_excerpt_hints.is_some(),
919// );
920// fetch_task.await.context("inlay hint fetch task")?
921// }
922// None => return Ok(()),
923// };
924// drop(lsp_request_guard);
925// log::debug!(
926// "Fetched {} hints for range {fetch_range_to_log:?}",
927// new_hints.len()
928// );
929// log::trace!("Fetched hints: {new_hints:?}");
930
931// let background_task_buffer_snapshot = buffer_snapshot.clone();
932// let backround_fetch_range = fetch_range.clone();
933// let new_update = cx
934// .background()
935// .spawn(async move {
936// calculate_hint_updates(
937// query.excerpt_id,
938// invalidate,
939// backround_fetch_range,
940// new_hints,
941// &background_task_buffer_snapshot,
942// cached_excerpt_hints,
943// &visible_hints,
944// )
945// })
946// .await;
947// if let Some(new_update) = new_update {
948// log::debug!(
949// "Applying update for range {fetch_range_to_log:?}: remove from editor: {}, remove from cache: {}, add to cache: {}",
950// new_update.remove_from_visible.len(),
951// new_update.remove_from_cache.len(),
952// new_update.add_to_cache.len()
953// );
954// log::trace!("New update: {new_update:?}");
955// editor
956// .update(&mut cx, |editor, cx| {
957// apply_hint_update(
958// editor,
959// new_update,
960// query,
961// invalidate,
962// buffer_snapshot,
963// multi_buffer_snapshot,
964// cx,
965// );
966// })
967// .ok();
968// }
969// Ok(())
970// }
971
972fn calculate_hint_updates(
973 excerpt_id: ExcerptId,
974 invalidate: bool,
975 fetch_range: Range<language::Anchor>,
976 new_excerpt_hints: Vec<InlayHint>,
977 buffer_snapshot: &BufferSnapshot,
978 cached_excerpt_hints: Option<Arc<RwLock<CachedExcerptHints>>>,
979 visible_hints: &[Inlay],
980) -> Option<ExcerptHintsUpdate> {
981 let mut add_to_cache = Vec::<InlayHint>::new();
982 let mut excerpt_hints_to_persist = HashMap::default();
983 for new_hint in new_excerpt_hints {
984 if !contains_position(&fetch_range, new_hint.position, buffer_snapshot) {
985 continue;
986 }
987 let missing_from_cache = match &cached_excerpt_hints {
988 Some(cached_excerpt_hints) => {
989 let cached_excerpt_hints = cached_excerpt_hints.read();
990 match cached_excerpt_hints
991 .ordered_hints
992 .binary_search_by(|probe| {
993 cached_excerpt_hints.hints_by_id[probe]
994 .position
995 .cmp(&new_hint.position, buffer_snapshot)
996 }) {
997 Ok(ix) => {
998 let mut missing_from_cache = true;
999 for id in &cached_excerpt_hints.ordered_hints[ix..] {
1000 let cached_hint = &cached_excerpt_hints.hints_by_id[id];
1001 if new_hint
1002 .position
1003 .cmp(&cached_hint.position, buffer_snapshot)
1004 .is_gt()
1005 {
1006 break;
1007 }
1008 if cached_hint == &new_hint {
1009 excerpt_hints_to_persist.insert(*id, cached_hint.kind);
1010 missing_from_cache = false;
1011 }
1012 }
1013 missing_from_cache
1014 }
1015 Err(_) => true,
1016 }
1017 }
1018 None => true,
1019 };
1020 if missing_from_cache {
1021 add_to_cache.push(new_hint);
1022 }
1023 }
1024
1025 let mut remove_from_visible = Vec::new();
1026 let mut remove_from_cache = HashSet::default();
1027 if invalidate {
1028 remove_from_visible.extend(
1029 visible_hints
1030 .iter()
1031 .filter(|hint| hint.position.excerpt_id == excerpt_id)
1032 .map(|inlay_hint| inlay_hint.id)
1033 .filter(|hint_id| !excerpt_hints_to_persist.contains_key(hint_id)),
1034 );
1035
1036 if let Some(cached_excerpt_hints) = &cached_excerpt_hints {
1037 let cached_excerpt_hints = cached_excerpt_hints.read();
1038 remove_from_cache.extend(
1039 cached_excerpt_hints
1040 .ordered_hints
1041 .iter()
1042 .filter(|cached_inlay_id| {
1043 !excerpt_hints_to_persist.contains_key(cached_inlay_id)
1044 })
1045 .copied(),
1046 );
1047 }
1048 }
1049
1050 if remove_from_visible.is_empty() && remove_from_cache.is_empty() && add_to_cache.is_empty() {
1051 None
1052 } else {
1053 Some(ExcerptHintsUpdate {
1054 excerpt_id,
1055 remove_from_visible,
1056 remove_from_cache,
1057 add_to_cache,
1058 })
1059 }
1060}
1061
1062fn contains_position(
1063 range: &Range<language::Anchor>,
1064 position: language::Anchor,
1065 buffer_snapshot: &BufferSnapshot,
1066) -> bool {
1067 range.start.cmp(&position, buffer_snapshot).is_le()
1068 && range.end.cmp(&position, buffer_snapshot).is_ge()
1069}
1070
1071fn apply_hint_update(
1072 editor: &mut Editor,
1073 new_update: ExcerptHintsUpdate,
1074 query: ExcerptQuery,
1075 invalidate: bool,
1076 buffer_snapshot: BufferSnapshot,
1077 multi_buffer_snapshot: MultiBufferSnapshot,
1078 cx: &mut ViewContext<'_, Editor>,
1079) {
1080 todo!("old implementation commented below")
1081}
1082// let cached_excerpt_hints = editor
1083// .inlay_hint_cache
1084// .hints
1085// .entry(new_update.excerpt_id)
1086// .or_insert_with(|| {
1087// Arc::new(RwLock::new(CachedExcerptHints {
1088// version: query.cache_version,
1089// buffer_version: buffer_snapshot.version().clone(),
1090// buffer_id: query.buffer_id,
1091// ordered_hints: Vec::new(),
1092// hints_by_id: HashMap::default(),
1093// }))
1094// });
1095// let mut cached_excerpt_hints = cached_excerpt_hints.write();
1096// match query.cache_version.cmp(&cached_excerpt_hints.version) {
1097// cmp::Ordering::Less => return,
1098// cmp::Ordering::Greater | cmp::Ordering::Equal => {
1099// cached_excerpt_hints.version = query.cache_version;
1100// }
1101// }
1102
1103// let mut cached_inlays_changed = !new_update.remove_from_cache.is_empty();
1104// cached_excerpt_hints
1105// .ordered_hints
1106// .retain(|hint_id| !new_update.remove_from_cache.contains(hint_id));
1107// cached_excerpt_hints
1108// .hints_by_id
1109// .retain(|hint_id, _| !new_update.remove_from_cache.contains(hint_id));
1110// let mut splice = InlaySplice {
1111// to_remove: new_update.remove_from_visible,
1112// to_insert: Vec::new(),
1113// };
1114// for new_hint in new_update.add_to_cache {
1115// let insert_position = match cached_excerpt_hints
1116// .ordered_hints
1117// .binary_search_by(|probe| {
1118// cached_excerpt_hints.hints_by_id[probe]
1119// .position
1120// .cmp(&new_hint.position, &buffer_snapshot)
1121// }) {
1122// Ok(i) => {
1123// let mut insert_position = Some(i);
1124// for id in &cached_excerpt_hints.ordered_hints[i..] {
1125// let cached_hint = &cached_excerpt_hints.hints_by_id[id];
1126// if new_hint
1127// .position
1128// .cmp(&cached_hint.position, &buffer_snapshot)
1129// .is_gt()
1130// {
1131// break;
1132// }
1133// if cached_hint.text() == new_hint.text() {
1134// insert_position = None;
1135// break;
1136// }
1137// }
1138// insert_position
1139// }
1140// Err(i) => Some(i),
1141// };
1142
1143// if let Some(insert_position) = insert_position {
1144// let new_inlay_id = post_inc(&mut editor.next_inlay_id);
1145// if editor
1146// .inlay_hint_cache
1147// .allowed_hint_kinds
1148// .contains(&new_hint.kind)
1149// {
1150// let new_hint_position =
1151// multi_buffer_snapshot.anchor_in_excerpt(query.excerpt_id, new_hint.position);
1152// splice
1153// .to_insert
1154// .push(Inlay::hint(new_inlay_id, new_hint_position, &new_hint));
1155// }
1156// let new_id = InlayId::Hint(new_inlay_id);
1157// cached_excerpt_hints.hints_by_id.insert(new_id, new_hint);
1158// cached_excerpt_hints
1159// .ordered_hints
1160// .insert(insert_position, new_id);
1161// cached_inlays_changed = true;
1162// }
1163// }
1164// cached_excerpt_hints.buffer_version = buffer_snapshot.version().clone();
1165// drop(cached_excerpt_hints);
1166
1167// if invalidate {
1168// let mut outdated_excerpt_caches = HashSet::default();
1169// for (excerpt_id, excerpt_hints) in &editor.inlay_hint_cache().hints {
1170// let excerpt_hints = excerpt_hints.read();
1171// if excerpt_hints.buffer_id == query.buffer_id
1172// && excerpt_id != &query.excerpt_id
1173// && buffer_snapshot
1174// .version()
1175// .changed_since(&excerpt_hints.buffer_version)
1176// {
1177// outdated_excerpt_caches.insert(*excerpt_id);
1178// splice
1179// .to_remove
1180// .extend(excerpt_hints.ordered_hints.iter().copied());
1181// }
1182// }
1183// cached_inlays_changed |= !outdated_excerpt_caches.is_empty();
1184// editor
1185// .inlay_hint_cache
1186// .hints
1187// .retain(|excerpt_id, _| !outdated_excerpt_caches.contains(excerpt_id));
1188// }
1189
1190// let InlaySplice {
1191// to_remove,
1192// to_insert,
1193// } = splice;
1194// let displayed_inlays_changed = !to_remove.is_empty() || !to_insert.is_empty();
1195// if cached_inlays_changed || displayed_inlays_changed {
1196// editor.inlay_hint_cache.version += 1;
1197// }
1198// if displayed_inlays_changed {
1199// editor.splice_inlay_hints(to_remove, to_insert, cx)
1200// }
1201// }
1202
1203// #[cfg(test)]
1204// pub mod tests {
1205// use std::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize, Ordering};
1206
1207// use crate::{
1208// scroll::{autoscroll::Autoscroll, scroll_amount::ScrollAmount},
1209// serde_json::json,
1210// ExcerptRange,
1211// };
1212// use futures::StreamExt;
1213// use gpui::{executor::Deterministic, TestAppContext, View};
1214// use itertools::Itertools;
1215// use language::{
1216// language_settings::AllLanguageSettingsContent, FakeLspAdapter, Language, LanguageConfig,
1217// };
1218// use lsp::FakeLanguageServer;
1219// use parking_lot::Mutex;
1220// use project::{FakeFs, Project};
1221// use settings::SettingsStore;
1222// use text::{Point, ToPoint};
1223// use workspace::Workspace;
1224
1225// use crate::editor_tests::update_test_language_settings;
1226
1227// use super::*;
1228
1229// #[gpui::test]
1230// async fn test_basic_cache_update_with_duplicate_hints(cx: &mut gpui::TestAppContext) {
1231// let allowed_hint_kinds = HashSet::from_iter([None, Some(InlayHintKind::Type)]);
1232// init_test(cx, |settings| {
1233// settings.defaults.inlay_hints = Some(InlayHintSettings {
1234// enabled: true,
1235// show_type_hints: allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
1236// show_parameter_hints: allowed_hint_kinds.contains(&Some(InlayHintKind::Parameter)),
1237// show_other_hints: allowed_hint_kinds.contains(&None),
1238// })
1239// });
1240
1241// let (file_with_hints, editor, fake_server) = prepare_test_objects(cx).await;
1242// let lsp_request_count = Arc::new(AtomicU32::new(0));
1243// fake_server
1244// .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
1245// let task_lsp_request_count = Arc::clone(&lsp_request_count);
1246// async move {
1247// assert_eq!(
1248// params.text_document.uri,
1249// lsp::Url::from_file_path(file_with_hints).unwrap(),
1250// );
1251// let current_call_id =
1252// Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst);
1253// let mut new_hints = Vec::with_capacity(2 * current_call_id as usize);
1254// for _ in 0..2 {
1255// let mut i = current_call_id;
1256// loop {
1257// new_hints.push(lsp::InlayHint {
1258// position: lsp::Position::new(0, i),
1259// label: lsp::InlayHintLabel::String(i.to_string()),
1260// kind: None,
1261// text_edits: None,
1262// tooltip: None,
1263// padding_left: None,
1264// padding_right: None,
1265// data: None,
1266// });
1267// if i == 0 {
1268// break;
1269// }
1270// i -= 1;
1271// }
1272// }
1273
1274// Ok(Some(new_hints))
1275// }
1276// })
1277// .next()
1278// .await;
1279// cx.foreground().run_until_parked();
1280
1281// let mut edits_made = 1;
1282// editor.update(cx, |editor, cx| {
1283// let expected_hints = vec!["0".to_string()];
1284// assert_eq!(
1285// expected_hints,
1286// cached_hint_labels(editor),
1287// "Should get its first hints when opening the editor"
1288// );
1289// assert_eq!(expected_hints, visible_hint_labels(editor, cx));
1290// let inlay_cache = editor.inlay_hint_cache();
1291// assert_eq!(
1292// inlay_cache.allowed_hint_kinds, allowed_hint_kinds,
1293// "Cache should use editor settings to get the allowed hint kinds"
1294// );
1295// assert_eq!(
1296// inlay_cache.version, edits_made,
1297// "The editor update the cache version after every cache/view change"
1298// );
1299// });
1300
1301// editor.update(cx, |editor, cx| {
1302// editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
1303// editor.handle_input("some change", cx);
1304// edits_made += 1;
1305// });
1306// cx.foreground().run_until_parked();
1307// editor.update(cx, |editor, cx| {
1308// let expected_hints = vec!["0".to_string(), "1".to_string()];
1309// assert_eq!(
1310// expected_hints,
1311// cached_hint_labels(editor),
1312// "Should get new hints after an edit"
1313// );
1314// assert_eq!(expected_hints, visible_hint_labels(editor, cx));
1315// let inlay_cache = editor.inlay_hint_cache();
1316// assert_eq!(
1317// inlay_cache.allowed_hint_kinds, allowed_hint_kinds,
1318// "Cache should use editor settings to get the allowed hint kinds"
1319// );
1320// assert_eq!(
1321// inlay_cache.version, edits_made,
1322// "The editor update the cache version after every cache/view change"
1323// );
1324// });
1325
1326// fake_server
1327// .request::<lsp::request::InlayHintRefreshRequest>(())
1328// .await
1329// .expect("inlay refresh request failed");
1330// edits_made += 1;
1331// cx.foreground().run_until_parked();
1332// editor.update(cx, |editor, cx| {
1333// let expected_hints = vec!["0".to_string(), "1".to_string(), "2".to_string()];
1334// assert_eq!(
1335// expected_hints,
1336// cached_hint_labels(editor),
1337// "Should get new hints after hint refresh/ request"
1338// );
1339// assert_eq!(expected_hints, visible_hint_labels(editor, cx));
1340// let inlay_cache = editor.inlay_hint_cache();
1341// assert_eq!(
1342// inlay_cache.allowed_hint_kinds, allowed_hint_kinds,
1343// "Cache should use editor settings to get the allowed hint kinds"
1344// );
1345// assert_eq!(
1346// inlay_cache.version, edits_made,
1347// "The editor update the cache version after every cache/view change"
1348// );
1349// });
1350// }
1351
1352// #[gpui::test]
1353// async fn test_cache_update_on_lsp_completion_tasks(cx: &mut gpui::TestAppContext) {
1354// init_test(cx, |settings| {
1355// settings.defaults.inlay_hints = Some(InlayHintSettings {
1356// enabled: true,
1357// show_type_hints: true,
1358// show_parameter_hints: true,
1359// show_other_hints: true,
1360// })
1361// });
1362
1363// let (file_with_hints, editor, fake_server) = prepare_test_objects(cx).await;
1364// let lsp_request_count = Arc::new(AtomicU32::new(0));
1365// fake_server
1366// .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
1367// let task_lsp_request_count = Arc::clone(&lsp_request_count);
1368// async move {
1369// assert_eq!(
1370// params.text_document.uri,
1371// lsp::Url::from_file_path(file_with_hints).unwrap(),
1372// );
1373// let current_call_id =
1374// Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst);
1375// Ok(Some(vec![lsp::InlayHint {
1376// position: lsp::Position::new(0, current_call_id),
1377// label: lsp::InlayHintLabel::String(current_call_id.to_string()),
1378// kind: None,
1379// text_edits: None,
1380// tooltip: None,
1381// padding_left: None,
1382// padding_right: None,
1383// data: None,
1384// }]))
1385// }
1386// })
1387// .next()
1388// .await;
1389// cx.foreground().run_until_parked();
1390
1391// let mut edits_made = 1;
1392// editor.update(cx, |editor, cx| {
1393// let expected_hints = vec!["0".to_string()];
1394// assert_eq!(
1395// expected_hints,
1396// cached_hint_labels(editor),
1397// "Should get its first hints when opening the editor"
1398// );
1399// assert_eq!(expected_hints, visible_hint_labels(editor, cx));
1400// assert_eq!(
1401// editor.inlay_hint_cache().version,
1402// edits_made,
1403// "The editor update the cache version after every cache/view change"
1404// );
1405// });
1406
1407// let progress_token = "test_progress_token";
1408// fake_server
1409// .request::<lsp::request::WorkDoneProgressCreate>(lsp::WorkDoneProgressCreateParams {
1410// token: lsp::ProgressToken::String(progress_token.to_string()),
1411// })
1412// .await
1413// .expect("work done progress create request failed");
1414// cx.foreground().run_until_parked();
1415// fake_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
1416// token: lsp::ProgressToken::String(progress_token.to_string()),
1417// value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Begin(
1418// lsp::WorkDoneProgressBegin::default(),
1419// )),
1420// });
1421// cx.foreground().run_until_parked();
1422
1423// editor.update(cx, |editor, cx| {
1424// let expected_hints = vec!["0".to_string()];
1425// assert_eq!(
1426// expected_hints,
1427// cached_hint_labels(editor),
1428// "Should not update hints while the work task is running"
1429// );
1430// assert_eq!(expected_hints, visible_hint_labels(editor, cx));
1431// assert_eq!(
1432// editor.inlay_hint_cache().version,
1433// edits_made,
1434// "Should not update the cache while the work task is running"
1435// );
1436// });
1437
1438// fake_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
1439// token: lsp::ProgressToken::String(progress_token.to_string()),
1440// value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::End(
1441// lsp::WorkDoneProgressEnd::default(),
1442// )),
1443// });
1444// cx.foreground().run_until_parked();
1445
1446// edits_made += 1;
1447// editor.update(cx, |editor, cx| {
1448// let expected_hints = vec!["1".to_string()];
1449// assert_eq!(
1450// expected_hints,
1451// cached_hint_labels(editor),
1452// "New hints should be queried after the work task is done"
1453// );
1454// assert_eq!(expected_hints, visible_hint_labels(editor, cx));
1455// assert_eq!(
1456// editor.inlay_hint_cache().version,
1457// edits_made,
1458// "Cache version should udpate once after the work task is done"
1459// );
1460// });
1461// }
1462
1463// #[gpui::test]
1464// async fn test_no_hint_updates_for_unrelated_language_files(cx: &mut gpui::TestAppContext) {
1465// init_test(cx, |settings| {
1466// settings.defaults.inlay_hints = Some(InlayHintSettings {
1467// enabled: true,
1468// show_type_hints: true,
1469// show_parameter_hints: true,
1470// show_other_hints: true,
1471// })
1472// });
1473
1474// let fs = FakeFs::new(cx.background());
1475// fs.insert_tree(
1476// "/a",
1477// json!({
1478// "main.rs": "fn main() { a } // and some long comment to ensure inlays are not trimmed out",
1479// "other.md": "Test md file with some text",
1480// }),
1481// )
1482// .await;
1483// let project = Project::test(fs, ["/a".as_ref()], cx).await;
1484// let workspace = cx
1485// .add_window(|cx| Workspace::test_new(project.clone(), cx))
1486// .root(cx);
1487// let worktree_id = workspace.update(cx, |workspace, cx| {
1488// workspace.project().read_with(cx, |project, cx| {
1489// project.worktrees(cx).next().unwrap().read(cx).id()
1490// })
1491// });
1492
1493// let mut rs_fake_servers = None;
1494// let mut md_fake_servers = None;
1495// for (name, path_suffix) in [("Rust", "rs"), ("Markdown", "md")] {
1496// let mut language = Language::new(
1497// LanguageConfig {
1498// name: name.into(),
1499// path_suffixes: vec![path_suffix.to_string()],
1500// ..Default::default()
1501// },
1502// Some(tree_sitter_rust::language()),
1503// );
1504// let fake_servers = language
1505// .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
1506// name,
1507// capabilities: lsp::ServerCapabilities {
1508// inlay_hint_provider: Some(lsp::OneOf::Left(true)),
1509// ..Default::default()
1510// },
1511// ..Default::default()
1512// }))
1513// .await;
1514// match name {
1515// "Rust" => rs_fake_servers = Some(fake_servers),
1516// "Markdown" => md_fake_servers = Some(fake_servers),
1517// _ => unreachable!(),
1518// }
1519// project.update(cx, |project, _| {
1520// project.languages().add(Arc::new(language));
1521// });
1522// }
1523
1524// let _rs_buffer = project
1525// .update(cx, |project, cx| {
1526// project.open_local_buffer("/a/main.rs", cx)
1527// })
1528// .await
1529// .unwrap();
1530// cx.foreground().run_until_parked();
1531// cx.foreground().start_waiting();
1532// let rs_fake_server = rs_fake_servers.unwrap().next().await.unwrap();
1533// let rs_editor = workspace
1534// .update(cx, |workspace, cx| {
1535// workspace.open_path((worktree_id, "main.rs"), None, true, cx)
1536// })
1537// .await
1538// .unwrap()
1539// .downcast::<Editor>()
1540// .unwrap();
1541// let rs_lsp_request_count = Arc::new(AtomicU32::new(0));
1542// rs_fake_server
1543// .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
1544// let task_lsp_request_count = Arc::clone(&rs_lsp_request_count);
1545// async move {
1546// assert_eq!(
1547// params.text_document.uri,
1548// lsp::Url::from_file_path("/a/main.rs").unwrap(),
1549// );
1550// let i = Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst);
1551// Ok(Some(vec![lsp::InlayHint {
1552// position: lsp::Position::new(0, i),
1553// label: lsp::InlayHintLabel::String(i.to_string()),
1554// kind: None,
1555// text_edits: None,
1556// tooltip: None,
1557// padding_left: None,
1558// padding_right: None,
1559// data: None,
1560// }]))
1561// }
1562// })
1563// .next()
1564// .await;
1565// cx.foreground().run_until_parked();
1566// rs_editor.update(cx, |editor, cx| {
1567// let expected_hints = vec!["0".to_string()];
1568// assert_eq!(
1569// expected_hints,
1570// cached_hint_labels(editor),
1571// "Should get its first hints when opening the editor"
1572// );
1573// assert_eq!(expected_hints, visible_hint_labels(editor, cx));
1574// assert_eq!(
1575// editor.inlay_hint_cache().version,
1576// 1,
1577// "Rust editor update the cache version after every cache/view change"
1578// );
1579// });
1580
1581// cx.foreground().run_until_parked();
1582// let _md_buffer = project
1583// .update(cx, |project, cx| {
1584// project.open_local_buffer("/a/other.md", cx)
1585// })
1586// .await
1587// .unwrap();
1588// cx.foreground().run_until_parked();
1589// cx.foreground().start_waiting();
1590// let md_fake_server = md_fake_servers.unwrap().next().await.unwrap();
1591// let md_editor = workspace
1592// .update(cx, |workspace, cx| {
1593// workspace.open_path((worktree_id, "other.md"), None, true, cx)
1594// })
1595// .await
1596// .unwrap()
1597// .downcast::<Editor>()
1598// .unwrap();
1599// let md_lsp_request_count = Arc::new(AtomicU32::new(0));
1600// md_fake_server
1601// .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
1602// let task_lsp_request_count = Arc::clone(&md_lsp_request_count);
1603// async move {
1604// assert_eq!(
1605// params.text_document.uri,
1606// lsp::Url::from_file_path("/a/other.md").unwrap(),
1607// );
1608// let i = Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst);
1609// Ok(Some(vec![lsp::InlayHint {
1610// position: lsp::Position::new(0, i),
1611// label: lsp::InlayHintLabel::String(i.to_string()),
1612// kind: None,
1613// text_edits: None,
1614// tooltip: None,
1615// padding_left: None,
1616// padding_right: None,
1617// data: None,
1618// }]))
1619// }
1620// })
1621// .next()
1622// .await;
1623// cx.foreground().run_until_parked();
1624// md_editor.update(cx, |editor, cx| {
1625// let expected_hints = vec!["0".to_string()];
1626// assert_eq!(
1627// expected_hints,
1628// cached_hint_labels(editor),
1629// "Markdown editor should have a separate verison, repeating Rust editor rules"
1630// );
1631// assert_eq!(expected_hints, visible_hint_labels(editor, cx));
1632// assert_eq!(editor.inlay_hint_cache().version, 1);
1633// });
1634
1635// rs_editor.update(cx, |editor, cx| {
1636// editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
1637// editor.handle_input("some rs change", cx);
1638// });
1639// cx.foreground().run_until_parked();
1640// rs_editor.update(cx, |editor, cx| {
1641// let expected_hints = vec!["1".to_string()];
1642// assert_eq!(
1643// expected_hints,
1644// cached_hint_labels(editor),
1645// "Rust inlay cache should change after the edit"
1646// );
1647// assert_eq!(expected_hints, visible_hint_labels(editor, cx));
1648// assert_eq!(
1649// editor.inlay_hint_cache().version,
1650// 2,
1651// "Every time hint cache changes, cache version should be incremented"
1652// );
1653// });
1654// md_editor.update(cx, |editor, cx| {
1655// let expected_hints = vec!["0".to_string()];
1656// assert_eq!(
1657// expected_hints,
1658// cached_hint_labels(editor),
1659// "Markdown editor should not be affected by Rust editor changes"
1660// );
1661// assert_eq!(expected_hints, visible_hint_labels(editor, cx));
1662// assert_eq!(editor.inlay_hint_cache().version, 1);
1663// });
1664
1665// md_editor.update(cx, |editor, cx| {
1666// editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
1667// editor.handle_input("some md change", cx);
1668// });
1669// cx.foreground().run_until_parked();
1670// md_editor.update(cx, |editor, cx| {
1671// let expected_hints = vec!["1".to_string()];
1672// assert_eq!(
1673// expected_hints,
1674// cached_hint_labels(editor),
1675// "Rust editor should not be affected by Markdown editor changes"
1676// );
1677// assert_eq!(expected_hints, visible_hint_labels(editor, cx));
1678// assert_eq!(editor.inlay_hint_cache().version, 2);
1679// });
1680// rs_editor.update(cx, |editor, cx| {
1681// let expected_hints = vec!["1".to_string()];
1682// assert_eq!(
1683// expected_hints,
1684// cached_hint_labels(editor),
1685// "Markdown editor should also change independently"
1686// );
1687// assert_eq!(expected_hints, visible_hint_labels(editor, cx));
1688// assert_eq!(editor.inlay_hint_cache().version, 2);
1689// });
1690// }
1691
1692// #[gpui::test]
1693// async fn test_hint_setting_changes(cx: &mut gpui::TestAppContext) {
1694// let allowed_hint_kinds = HashSet::from_iter([None, Some(InlayHintKind::Type)]);
1695// init_test(cx, |settings| {
1696// settings.defaults.inlay_hints = Some(InlayHintSettings {
1697// enabled: true,
1698// show_type_hints: allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
1699// show_parameter_hints: allowed_hint_kinds.contains(&Some(InlayHintKind::Parameter)),
1700// show_other_hints: allowed_hint_kinds.contains(&None),
1701// })
1702// });
1703
1704// let (file_with_hints, editor, fake_server) = prepare_test_objects(cx).await;
1705// let lsp_request_count = Arc::new(AtomicU32::new(0));
1706// let another_lsp_request_count = Arc::clone(&lsp_request_count);
1707// fake_server
1708// .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
1709// let task_lsp_request_count = Arc::clone(&another_lsp_request_count);
1710// async move {
1711// Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst);
1712// assert_eq!(
1713// params.text_document.uri,
1714// lsp::Url::from_file_path(file_with_hints).unwrap(),
1715// );
1716// Ok(Some(vec![
1717// lsp::InlayHint {
1718// position: lsp::Position::new(0, 1),
1719// label: lsp::InlayHintLabel::String("type hint".to_string()),
1720// kind: Some(lsp::InlayHintKind::TYPE),
1721// text_edits: None,
1722// tooltip: None,
1723// padding_left: None,
1724// padding_right: None,
1725// data: None,
1726// },
1727// lsp::InlayHint {
1728// position: lsp::Position::new(0, 2),
1729// label: lsp::InlayHintLabel::String("parameter hint".to_string()),
1730// kind: Some(lsp::InlayHintKind::PARAMETER),
1731// text_edits: None,
1732// tooltip: None,
1733// padding_left: None,
1734// padding_right: None,
1735// data: None,
1736// },
1737// lsp::InlayHint {
1738// position: lsp::Position::new(0, 3),
1739// label: lsp::InlayHintLabel::String("other hint".to_string()),
1740// kind: None,
1741// text_edits: None,
1742// tooltip: None,
1743// padding_left: None,
1744// padding_right: None,
1745// data: None,
1746// },
1747// ]))
1748// }
1749// })
1750// .next()
1751// .await;
1752// cx.foreground().run_until_parked();
1753
1754// let mut edits_made = 1;
1755// editor.update(cx, |editor, cx| {
1756// assert_eq!(
1757// lsp_request_count.load(Ordering::Relaxed),
1758// 1,
1759// "Should query new hints once"
1760// );
1761// assert_eq!(
1762// vec![
1763// "other hint".to_string(),
1764// "parameter hint".to_string(),
1765// "type hint".to_string(),
1766// ],
1767// cached_hint_labels(editor),
1768// "Should get its first hints when opening the editor"
1769// );
1770// assert_eq!(
1771// vec!["other hint".to_string(), "type hint".to_string()],
1772// visible_hint_labels(editor, cx)
1773// );
1774// let inlay_cache = editor.inlay_hint_cache();
1775// assert_eq!(
1776// inlay_cache.allowed_hint_kinds, allowed_hint_kinds,
1777// "Cache should use editor settings to get the allowed hint kinds"
1778// );
1779// assert_eq!(
1780// inlay_cache.version, edits_made,
1781// "The editor update the cache version after every cache/view change"
1782// );
1783// });
1784
1785// fake_server
1786// .request::<lsp::request::InlayHintRefreshRequest>(())
1787// .await
1788// .expect("inlay refresh request failed");
1789// cx.foreground().run_until_parked();
1790// editor.update(cx, |editor, cx| {
1791// assert_eq!(
1792// lsp_request_count.load(Ordering::Relaxed),
1793// 2,
1794// "Should load new hints twice"
1795// );
1796// assert_eq!(
1797// vec![
1798// "other hint".to_string(),
1799// "parameter hint".to_string(),
1800// "type hint".to_string(),
1801// ],
1802// cached_hint_labels(editor),
1803// "Cached hints should not change due to allowed hint kinds settings update"
1804// );
1805// assert_eq!(
1806// vec!["other hint".to_string(), "type hint".to_string()],
1807// visible_hint_labels(editor, cx)
1808// );
1809// assert_eq!(
1810// editor.inlay_hint_cache().version,
1811// edits_made,
1812// "Should not update cache version due to new loaded hints being the same"
1813// );
1814// });
1815
1816// for (new_allowed_hint_kinds, expected_visible_hints) in [
1817// (HashSet::from_iter([None]), vec!["other hint".to_string()]),
1818// (
1819// HashSet::from_iter([Some(InlayHintKind::Type)]),
1820// vec!["type hint".to_string()],
1821// ),
1822// (
1823// HashSet::from_iter([Some(InlayHintKind::Parameter)]),
1824// vec!["parameter hint".to_string()],
1825// ),
1826// (
1827// HashSet::from_iter([None, Some(InlayHintKind::Type)]),
1828// vec!["other hint".to_string(), "type hint".to_string()],
1829// ),
1830// (
1831// HashSet::from_iter([None, Some(InlayHintKind::Parameter)]),
1832// vec!["other hint".to_string(), "parameter hint".to_string()],
1833// ),
1834// (
1835// HashSet::from_iter([Some(InlayHintKind::Type), Some(InlayHintKind::Parameter)]),
1836// vec!["parameter hint".to_string(), "type hint".to_string()],
1837// ),
1838// (
1839// HashSet::from_iter([
1840// None,
1841// Some(InlayHintKind::Type),
1842// Some(InlayHintKind::Parameter),
1843// ]),
1844// vec![
1845// "other hint".to_string(),
1846// "parameter hint".to_string(),
1847// "type hint".to_string(),
1848// ],
1849// ),
1850// ] {
1851// edits_made += 1;
1852// update_test_language_settings(cx, |settings| {
1853// settings.defaults.inlay_hints = Some(InlayHintSettings {
1854// enabled: true,
1855// show_type_hints: new_allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
1856// show_parameter_hints: new_allowed_hint_kinds
1857// .contains(&Some(InlayHintKind::Parameter)),
1858// show_other_hints: new_allowed_hint_kinds.contains(&None),
1859// })
1860// });
1861// cx.foreground().run_until_parked();
1862// editor.update(cx, |editor, cx| {
1863// assert_eq!(
1864// lsp_request_count.load(Ordering::Relaxed),
1865// 2,
1866// "Should not load new hints on allowed hint kinds change for hint kinds {new_allowed_hint_kinds:?}"
1867// );
1868// assert_eq!(
1869// vec![
1870// "other hint".to_string(),
1871// "parameter hint".to_string(),
1872// "type hint".to_string(),
1873// ],
1874// cached_hint_labels(editor),
1875// "Should get its cached hints unchanged after the settings change for hint kinds {new_allowed_hint_kinds:?}"
1876// );
1877// assert_eq!(
1878// expected_visible_hints,
1879// visible_hint_labels(editor, cx),
1880// "Should get its visible hints filtered after the settings change for hint kinds {new_allowed_hint_kinds:?}"
1881// );
1882// let inlay_cache = editor.inlay_hint_cache();
1883// assert_eq!(
1884// inlay_cache.allowed_hint_kinds, new_allowed_hint_kinds,
1885// "Cache should use editor settings to get the allowed hint kinds for hint kinds {new_allowed_hint_kinds:?}"
1886// );
1887// assert_eq!(
1888// inlay_cache.version, edits_made,
1889// "The editor should update the cache version after every cache/view change for hint kinds {new_allowed_hint_kinds:?} due to visible hints change"
1890// );
1891// });
1892// }
1893
1894// edits_made += 1;
1895// let another_allowed_hint_kinds = HashSet::from_iter([Some(InlayHintKind::Type)]);
1896// update_test_language_settings(cx, |settings| {
1897// settings.defaults.inlay_hints = Some(InlayHintSettings {
1898// enabled: false,
1899// show_type_hints: another_allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
1900// show_parameter_hints: another_allowed_hint_kinds
1901// .contains(&Some(InlayHintKind::Parameter)),
1902// show_other_hints: another_allowed_hint_kinds.contains(&None),
1903// })
1904// });
1905// cx.foreground().run_until_parked();
1906// editor.update(cx, |editor, cx| {
1907// assert_eq!(
1908// lsp_request_count.load(Ordering::Relaxed),
1909// 2,
1910// "Should not load new hints when hints got disabled"
1911// );
1912// assert!(
1913// cached_hint_labels(editor).is_empty(),
1914// "Should clear the cache when hints got disabled"
1915// );
1916// assert!(
1917// visible_hint_labels(editor, cx).is_empty(),
1918// "Should clear visible hints when hints got disabled"
1919// );
1920// let inlay_cache = editor.inlay_hint_cache();
1921// assert_eq!(
1922// inlay_cache.allowed_hint_kinds, another_allowed_hint_kinds,
1923// "Should update its allowed hint kinds even when hints got disabled"
1924// );
1925// assert_eq!(
1926// inlay_cache.version, edits_made,
1927// "The editor should update the cache version after hints got disabled"
1928// );
1929// });
1930
1931// fake_server
1932// .request::<lsp::request::InlayHintRefreshRequest>(())
1933// .await
1934// .expect("inlay refresh request failed");
1935// cx.foreground().run_until_parked();
1936// editor.update(cx, |editor, cx| {
1937// assert_eq!(
1938// lsp_request_count.load(Ordering::Relaxed),
1939// 2,
1940// "Should not load new hints when they got disabled"
1941// );
1942// assert!(cached_hint_labels(editor).is_empty());
1943// assert!(visible_hint_labels(editor, cx).is_empty());
1944// assert_eq!(
1945// editor.inlay_hint_cache().version, edits_made,
1946// "The editor should not update the cache version after /refresh query without updates"
1947// );
1948// });
1949
1950// let final_allowed_hint_kinds = HashSet::from_iter([Some(InlayHintKind::Parameter)]);
1951// edits_made += 1;
1952// update_test_language_settings(cx, |settings| {
1953// settings.defaults.inlay_hints = Some(InlayHintSettings {
1954// enabled: true,
1955// show_type_hints: final_allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
1956// show_parameter_hints: final_allowed_hint_kinds
1957// .contains(&Some(InlayHintKind::Parameter)),
1958// show_other_hints: final_allowed_hint_kinds.contains(&None),
1959// })
1960// });
1961// cx.foreground().run_until_parked();
1962// editor.update(cx, |editor, cx| {
1963// assert_eq!(
1964// lsp_request_count.load(Ordering::Relaxed),
1965// 3,
1966// "Should query for new hints when they got reenabled"
1967// );
1968// assert_eq!(
1969// vec![
1970// "other hint".to_string(),
1971// "parameter hint".to_string(),
1972// "type hint".to_string(),
1973// ],
1974// cached_hint_labels(editor),
1975// "Should get its cached hints fully repopulated after the hints got reenabled"
1976// );
1977// assert_eq!(
1978// vec!["parameter hint".to_string()],
1979// visible_hint_labels(editor, cx),
1980// "Should get its visible hints repopulated and filtered after the h"
1981// );
1982// let inlay_cache = editor.inlay_hint_cache();
1983// assert_eq!(
1984// inlay_cache.allowed_hint_kinds, final_allowed_hint_kinds,
1985// "Cache should update editor settings when hints got reenabled"
1986// );
1987// assert_eq!(
1988// inlay_cache.version, edits_made,
1989// "Cache should update its version after hints got reenabled"
1990// );
1991// });
1992
1993// fake_server
1994// .request::<lsp::request::InlayHintRefreshRequest>(())
1995// .await
1996// .expect("inlay refresh request failed");
1997// cx.foreground().run_until_parked();
1998// editor.update(cx, |editor, cx| {
1999// assert_eq!(
2000// lsp_request_count.load(Ordering::Relaxed),
2001// 4,
2002// "Should query for new hints again"
2003// );
2004// assert_eq!(
2005// vec![
2006// "other hint".to_string(),
2007// "parameter hint".to_string(),
2008// "type hint".to_string(),
2009// ],
2010// cached_hint_labels(editor),
2011// );
2012// assert_eq!(
2013// vec!["parameter hint".to_string()],
2014// visible_hint_labels(editor, cx),
2015// );
2016// assert_eq!(editor.inlay_hint_cache().version, edits_made);
2017// });
2018// }
2019
2020// #[gpui::test]
2021// async fn test_hint_request_cancellation(cx: &mut gpui::TestAppContext) {
2022// init_test(cx, |settings| {
2023// settings.defaults.inlay_hints = Some(InlayHintSettings {
2024// enabled: true,
2025// show_type_hints: true,
2026// show_parameter_hints: true,
2027// show_other_hints: true,
2028// })
2029// });
2030
2031// let (file_with_hints, editor, fake_server) = prepare_test_objects(cx).await;
2032// let fake_server = Arc::new(fake_server);
2033// let lsp_request_count = Arc::new(AtomicU32::new(0));
2034// let another_lsp_request_count = Arc::clone(&lsp_request_count);
2035// fake_server
2036// .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
2037// let task_lsp_request_count = Arc::clone(&another_lsp_request_count);
2038// async move {
2039// let i = Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst) + 1;
2040// assert_eq!(
2041// params.text_document.uri,
2042// lsp::Url::from_file_path(file_with_hints).unwrap(),
2043// );
2044// Ok(Some(vec![lsp::InlayHint {
2045// position: lsp::Position::new(0, i),
2046// label: lsp::InlayHintLabel::String(i.to_string()),
2047// kind: None,
2048// text_edits: None,
2049// tooltip: None,
2050// padding_left: None,
2051// padding_right: None,
2052// data: None,
2053// }]))
2054// }
2055// })
2056// .next()
2057// .await;
2058
2059// let mut expected_changes = Vec::new();
2060// for change_after_opening in [
2061// "initial change #1",
2062// "initial change #2",
2063// "initial change #3",
2064// ] {
2065// editor.update(cx, |editor, cx| {
2066// editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
2067// editor.handle_input(change_after_opening, cx);
2068// });
2069// expected_changes.push(change_after_opening);
2070// }
2071
2072// cx.foreground().run_until_parked();
2073
2074// editor.update(cx, |editor, cx| {
2075// let current_text = editor.text(cx);
2076// for change in &expected_changes {
2077// assert!(
2078// current_text.contains(change),
2079// "Should apply all changes made"
2080// );
2081// }
2082// assert_eq!(
2083// lsp_request_count.load(Ordering::Relaxed),
2084// 2,
2085// "Should query new hints twice: for editor init and for the last edit that interrupted all others"
2086// );
2087// let expected_hints = vec!["2".to_string()];
2088// assert_eq!(
2089// expected_hints,
2090// cached_hint_labels(editor),
2091// "Should get hints from the last edit landed only"
2092// );
2093// assert_eq!(expected_hints, visible_hint_labels(editor, cx));
2094// assert_eq!(
2095// editor.inlay_hint_cache().version, 1,
2096// "Only one update should be registered in the cache after all cancellations"
2097// );
2098// });
2099
2100// let mut edits = Vec::new();
2101// for async_later_change in [
2102// "another change #1",
2103// "another change #2",
2104// "another change #3",
2105// ] {
2106// expected_changes.push(async_later_change);
2107// let task_editor = editor.clone();
2108// let mut task_cx = cx.clone();
2109// edits.push(cx.foreground().spawn(async move {
2110// task_editor.update(&mut task_cx, |editor, cx| {
2111// editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
2112// editor.handle_input(async_later_change, cx);
2113// });
2114// }));
2115// }
2116// let _ = future::join_all(edits).await;
2117// cx.foreground().run_until_parked();
2118
2119// editor.update(cx, |editor, cx| {
2120// let current_text = editor.text(cx);
2121// for change in &expected_changes {
2122// assert!(
2123// current_text.contains(change),
2124// "Should apply all changes made"
2125// );
2126// }
2127// assert_eq!(
2128// lsp_request_count.load(Ordering::SeqCst),
2129// 3,
2130// "Should query new hints one more time, for the last edit only"
2131// );
2132// let expected_hints = vec!["3".to_string()];
2133// assert_eq!(
2134// expected_hints,
2135// cached_hint_labels(editor),
2136// "Should get hints from the last edit landed only"
2137// );
2138// assert_eq!(expected_hints, visible_hint_labels(editor, cx));
2139// assert_eq!(
2140// editor.inlay_hint_cache().version,
2141// 2,
2142// "Should update the cache version once more, for the new change"
2143// );
2144// });
2145// }
2146
2147// #[gpui::test(iterations = 10)]
2148// async fn test_large_buffer_inlay_requests_split(cx: &mut gpui::TestAppContext) {
2149// init_test(cx, |settings| {
2150// settings.defaults.inlay_hints = Some(InlayHintSettings {
2151// enabled: true,
2152// show_type_hints: true,
2153// show_parameter_hints: true,
2154// show_other_hints: true,
2155// })
2156// });
2157
2158// let mut language = Language::new(
2159// LanguageConfig {
2160// name: "Rust".into(),
2161// path_suffixes: vec!["rs".to_string()],
2162// ..Default::default()
2163// },
2164// Some(tree_sitter_rust::language()),
2165// );
2166// let mut fake_servers = language
2167// .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
2168// capabilities: lsp::ServerCapabilities {
2169// inlay_hint_provider: Some(lsp::OneOf::Left(true)),
2170// ..Default::default()
2171// },
2172// ..Default::default()
2173// }))
2174// .await;
2175// let fs = FakeFs::new(cx.background());
2176// fs.insert_tree(
2177// "/a",
2178// json!({
2179// "main.rs": format!("fn main() {{\n{}\n}}", "let i = 5;\n".repeat(500)),
2180// "other.rs": "// Test file",
2181// }),
2182// )
2183// .await;
2184// let project = Project::test(fs, ["/a".as_ref()], cx).await;
2185// project.update(cx, |project, _| project.languages().add(Arc::new(language)));
2186// let workspace = cx
2187// .add_window(|cx| Workspace::test_new(project.clone(), cx))
2188// .root(cx);
2189// let worktree_id = workspace.update(cx, |workspace, cx| {
2190// workspace.project().read_with(cx, |project, cx| {
2191// project.worktrees(cx).next().unwrap().read(cx).id()
2192// })
2193// });
2194
2195// let _buffer = project
2196// .update(cx, |project, cx| {
2197// project.open_local_buffer("/a/main.rs", cx)
2198// })
2199// .await
2200// .unwrap();
2201// cx.foreground().run_until_parked();
2202// cx.foreground().start_waiting();
2203// let fake_server = fake_servers.next().await.unwrap();
2204// let editor = workspace
2205// .update(cx, |workspace, cx| {
2206// workspace.open_path((worktree_id, "main.rs"), None, true, cx)
2207// })
2208// .await
2209// .unwrap()
2210// .downcast::<Editor>()
2211// .unwrap();
2212// let lsp_request_ranges = Arc::new(Mutex::new(Vec::new()));
2213// let lsp_request_count = Arc::new(AtomicUsize::new(0));
2214// let closure_lsp_request_ranges = Arc::clone(&lsp_request_ranges);
2215// let closure_lsp_request_count = Arc::clone(&lsp_request_count);
2216// fake_server
2217// .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
2218// let task_lsp_request_ranges = Arc::clone(&closure_lsp_request_ranges);
2219// let task_lsp_request_count = Arc::clone(&closure_lsp_request_count);
2220// async move {
2221// assert_eq!(
2222// params.text_document.uri,
2223// lsp::Url::from_file_path("/a/main.rs").unwrap(),
2224// );
2225
2226// task_lsp_request_ranges.lock().push(params.range);
2227// let i = Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::Release) + 1;
2228// Ok(Some(vec![lsp::InlayHint {
2229// position: params.range.end,
2230// label: lsp::InlayHintLabel::String(i.to_string()),
2231// kind: None,
2232// text_edits: None,
2233// tooltip: None,
2234// padding_left: None,
2235// padding_right: None,
2236// data: None,
2237// }]))
2238// }
2239// })
2240// .next()
2241// .await;
2242// fn editor_visible_range(
2243// editor: &ViewHandle<Editor>,
2244// cx: &mut gpui::TestAppContext,
2245// ) -> Range<Point> {
2246// let ranges = editor.update(cx, |editor, cx| editor.excerpt_visible_offsets(None, cx));
2247// assert_eq!(
2248// ranges.len(),
2249// 1,
2250// "Single buffer should produce a single excerpt with visible range"
2251// );
2252// let (_, (excerpt_buffer, _, excerpt_visible_range)) =
2253// ranges.into_iter().next().unwrap();
2254// excerpt_buffer.update(cx, |buffer, _| {
2255// let snapshot = buffer.snapshot();
2256// let start = buffer
2257// .anchor_before(excerpt_visible_range.start)
2258// .to_point(&snapshot);
2259// let end = buffer
2260// .anchor_after(excerpt_visible_range.end)
2261// .to_point(&snapshot);
2262// start..end
2263// })
2264// }
2265
2266// // in large buffers, requests are made for more than visible range of a buffer.
2267// // invisible parts are queried later, to avoid excessive requests on quick typing.
2268// // wait the timeout needed to get all requests.
2269// cx.foreground().advance_clock(Duration::from_millis(
2270// INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100,
2271// ));
2272// cx.foreground().run_until_parked();
2273// let initial_visible_range = editor_visible_range(&editor, cx);
2274// let lsp_initial_visible_range = lsp::Range::new(
2275// lsp::Position::new(
2276// initial_visible_range.start.row,
2277// initial_visible_range.start.column,
2278// ),
2279// lsp::Position::new(
2280// initial_visible_range.end.row,
2281// initial_visible_range.end.column,
2282// ),
2283// );
2284// let expected_initial_query_range_end =
2285// lsp::Position::new(initial_visible_range.end.row * 2, 2);
2286// let mut expected_invisible_query_start = lsp_initial_visible_range.end;
2287// expected_invisible_query_start.character += 1;
2288// editor.update(cx, |editor, cx| {
2289// let ranges = lsp_request_ranges.lock().drain(..).collect::<Vec<_>>();
2290// assert_eq!(ranges.len(), 2,
2291// "When scroll is at the edge of a big document, its visible part and the same range further should be queried in order, but got: {ranges:?}");
2292// let visible_query_range = &ranges[0];
2293// assert_eq!(visible_query_range.start, lsp_initial_visible_range.start);
2294// assert_eq!(visible_query_range.end, lsp_initial_visible_range.end);
2295// let invisible_query_range = &ranges[1];
2296
2297// assert_eq!(invisible_query_range.start, expected_invisible_query_start, "Should initially query visible edge of the document");
2298// assert_eq!(invisible_query_range.end, expected_initial_query_range_end, "Should initially query visible edge of the document");
2299
2300// let requests_count = lsp_request_count.load(Ordering::Acquire);
2301// assert_eq!(requests_count, 2, "Visible + invisible request");
2302// let expected_hints = vec!["1".to_string(), "2".to_string()];
2303// assert_eq!(
2304// expected_hints,
2305// cached_hint_labels(editor),
2306// "Should have hints from both LSP requests made for a big file"
2307// );
2308// assert_eq!(expected_hints, visible_hint_labels(editor, cx), "Should display only hints from the visible range");
2309// assert_eq!(
2310// editor.inlay_hint_cache().version, requests_count,
2311// "LSP queries should've bumped the cache version"
2312// );
2313// });
2314
2315// editor.update(cx, |editor, cx| {
2316// editor.scroll_screen(&ScrollAmount::Page(1.0), cx);
2317// editor.scroll_screen(&ScrollAmount::Page(1.0), cx);
2318// });
2319// cx.foreground().advance_clock(Duration::from_millis(
2320// INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100,
2321// ));
2322// cx.foreground().run_until_parked();
2323// let visible_range_after_scrolls = editor_visible_range(&editor, cx);
2324// let visible_line_count =
2325// editor.update(cx, |editor, _| editor.visible_line_count().unwrap());
2326// let selection_in_cached_range = editor.update(cx, |editor, cx| {
2327// let ranges = lsp_request_ranges
2328// .lock()
2329// .drain(..)
2330// .sorted_by_key(|r| r.start)
2331// .collect::<Vec<_>>();
2332// assert_eq!(
2333// ranges.len(),
2334// 2,
2335// "Should query 2 ranges after both scrolls, but got: {ranges:?}"
2336// );
2337// let first_scroll = &ranges[0];
2338// let second_scroll = &ranges[1];
2339// assert_eq!(
2340// first_scroll.end, second_scroll.start,
2341// "Should query 2 adjacent ranges after the scrolls, but got: {ranges:?}"
2342// );
2343// assert_eq!(
2344// first_scroll.start, expected_initial_query_range_end,
2345// "First scroll should start the query right after the end of the original scroll",
2346// );
2347// assert_eq!(
2348// second_scroll.end,
2349// lsp::Position::new(
2350// visible_range_after_scrolls.end.row
2351// + visible_line_count.ceil() as u32,
2352// 1,
2353// ),
2354// "Second scroll should query one more screen down after the end of the visible range"
2355// );
2356
2357// let lsp_requests = lsp_request_count.load(Ordering::Acquire);
2358// assert_eq!(lsp_requests, 4, "Should query for hints after every scroll");
2359// let expected_hints = vec![
2360// "1".to_string(),
2361// "2".to_string(),
2362// "3".to_string(),
2363// "4".to_string(),
2364// ];
2365// assert_eq!(
2366// expected_hints,
2367// cached_hint_labels(editor),
2368// "Should have hints from the new LSP response after the edit"
2369// );
2370// assert_eq!(expected_hints, visible_hint_labels(editor, cx));
2371// assert_eq!(
2372// editor.inlay_hint_cache().version,
2373// lsp_requests,
2374// "Should update the cache for every LSP response with hints added"
2375// );
2376
2377// let mut selection_in_cached_range = visible_range_after_scrolls.end;
2378// selection_in_cached_range.row -= visible_line_count.ceil() as u32;
2379// selection_in_cached_range
2380// });
2381
2382// editor.update(cx, |editor, cx| {
2383// editor.change_selections(Some(Autoscroll::center()), cx, |s| {
2384// s.select_ranges([selection_in_cached_range..selection_in_cached_range])
2385// });
2386// });
2387// cx.foreground().advance_clock(Duration::from_millis(
2388// INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100,
2389// ));
2390// cx.foreground().run_until_parked();
2391// editor.update(cx, |_, _| {
2392// let ranges = lsp_request_ranges
2393// .lock()
2394// .drain(..)
2395// .sorted_by_key(|r| r.start)
2396// .collect::<Vec<_>>();
2397// assert!(ranges.is_empty(), "No new ranges or LSP queries should be made after returning to the selection with cached hints");
2398// assert_eq!(lsp_request_count.load(Ordering::Acquire), 4);
2399// });
2400
2401// editor.update(cx, |editor, cx| {
2402// editor.handle_input("++++more text++++", cx);
2403// });
2404// cx.foreground().advance_clock(Duration::from_millis(
2405// INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100,
2406// ));
2407// cx.foreground().run_until_parked();
2408// editor.update(cx, |editor, cx| {
2409// let mut ranges = lsp_request_ranges.lock().drain(..).collect::<Vec<_>>();
2410// ranges.sort_by_key(|r| r.start);
2411
2412// assert_eq!(ranges.len(), 3,
2413// "On edit, should scroll to selection and query a range around it: visible + same range above and below. Instead, got query ranges {ranges:?}");
2414// let above_query_range = &ranges[0];
2415// let visible_query_range = &ranges[1];
2416// let below_query_range = &ranges[2];
2417// assert!(above_query_range.end.character < visible_query_range.start.character || above_query_range.end.line + 1 == visible_query_range.start.line,
2418// "Above range {above_query_range:?} should be before visible range {visible_query_range:?}");
2419// assert!(visible_query_range.end.character < below_query_range.start.character || visible_query_range.end.line + 1 == below_query_range.start.line,
2420// "Visible range {visible_query_range:?} should be before below range {below_query_range:?}");
2421// assert!(above_query_range.start.line < selection_in_cached_range.row,
2422// "Hints should be queried with the selected range after the query range start");
2423// assert!(below_query_range.end.line > selection_in_cached_range.row,
2424// "Hints should be queried with the selected range before the query range end");
2425// assert!(above_query_range.start.line <= selection_in_cached_range.row - (visible_line_count * 3.0 / 2.0) as u32,
2426// "Hints query range should contain one more screen before");
2427// assert!(below_query_range.end.line >= selection_in_cached_range.row + (visible_line_count * 3.0 / 2.0) as u32,
2428// "Hints query range should contain one more screen after");
2429
2430// let lsp_requests = lsp_request_count.load(Ordering::Acquire);
2431// assert_eq!(lsp_requests, 7, "There should be a visible range and two ranges above and below it queried");
2432// let expected_hints = vec!["5".to_string(), "6".to_string(), "7".to_string()];
2433// assert_eq!(expected_hints, cached_hint_labels(editor),
2434// "Should have hints from the new LSP response after the edit");
2435// assert_eq!(expected_hints, visible_hint_labels(editor, cx));
2436// assert_eq!(editor.inlay_hint_cache().version, lsp_requests, "Should update the cache for every LSP response with hints added");
2437// });
2438// }
2439
2440// #[gpui::test(iterations = 10)]
2441// async fn test_multiple_excerpts_large_multibuffer(
2442// deterministic: Arc<Deterministic>,
2443// cx: &mut gpui::TestAppContext,
2444// ) {
2445// init_test(cx, |settings| {
2446// settings.defaults.inlay_hints = Some(InlayHintSettings {
2447// enabled: true,
2448// show_type_hints: true,
2449// show_parameter_hints: true,
2450// show_other_hints: true,
2451// })
2452// });
2453
2454// let mut language = Language::new(
2455// LanguageConfig {
2456// name: "Rust".into(),
2457// path_suffixes: vec!["rs".to_string()],
2458// ..Default::default()
2459// },
2460// Some(tree_sitter_rust::language()),
2461// );
2462// let mut fake_servers = language
2463// .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
2464// capabilities: lsp::ServerCapabilities {
2465// inlay_hint_provider: Some(lsp::OneOf::Left(true)),
2466// ..Default::default()
2467// },
2468// ..Default::default()
2469// }))
2470// .await;
2471// let language = Arc::new(language);
2472// let fs = FakeFs::new(cx.background());
2473// fs.insert_tree(
2474// "/a",
2475// json!({
2476// "main.rs": format!("fn main() {{\n{}\n}}", (0..501).map(|i| format!("let i = {i};\n")).collect::<Vec<_>>().join("")),
2477// "other.rs": format!("fn main() {{\n{}\n}}", (0..501).map(|j| format!("let j = {j};\n")).collect::<Vec<_>>().join("")),
2478// }),
2479// )
2480// .await;
2481// let project = Project::test(fs, ["/a".as_ref()], cx).await;
2482// project.update(cx, |project, _| {
2483// project.languages().add(Arc::clone(&language))
2484// });
2485// let workspace = cx
2486// .add_window(|cx| Workspace::test_new(project.clone(), cx))
2487// .root(cx);
2488// let worktree_id = workspace.update(cx, |workspace, cx| {
2489// workspace.project().read_with(cx, |project, cx| {
2490// project.worktrees(cx).next().unwrap().read(cx).id()
2491// })
2492// });
2493
2494// let buffer_1 = project
2495// .update(cx, |project, cx| {
2496// project.open_buffer((worktree_id, "main.rs"), cx)
2497// })
2498// .await
2499// .unwrap();
2500// let buffer_2 = project
2501// .update(cx, |project, cx| {
2502// project.open_buffer((worktree_id, "other.rs"), cx)
2503// })
2504// .await
2505// .unwrap();
2506// let multibuffer = cx.add_model(|cx| {
2507// let mut multibuffer = MultiBuffer::new(0);
2508// multibuffer.push_excerpts(
2509// buffer_1.clone(),
2510// [
2511// ExcerptRange {
2512// context: Point::new(0, 0)..Point::new(2, 0),
2513// primary: None,
2514// },
2515// ExcerptRange {
2516// context: Point::new(4, 0)..Point::new(11, 0),
2517// primary: None,
2518// },
2519// ExcerptRange {
2520// context: Point::new(22, 0)..Point::new(33, 0),
2521// primary: None,
2522// },
2523// ExcerptRange {
2524// context: Point::new(44, 0)..Point::new(55, 0),
2525// primary: None,
2526// },
2527// ExcerptRange {
2528// context: Point::new(56, 0)..Point::new(66, 0),
2529// primary: None,
2530// },
2531// ExcerptRange {
2532// context: Point::new(67, 0)..Point::new(77, 0),
2533// primary: None,
2534// },
2535// ],
2536// cx,
2537// );
2538// multibuffer.push_excerpts(
2539// buffer_2.clone(),
2540// [
2541// ExcerptRange {
2542// context: Point::new(0, 1)..Point::new(2, 1),
2543// primary: None,
2544// },
2545// ExcerptRange {
2546// context: Point::new(4, 1)..Point::new(11, 1),
2547// primary: None,
2548// },
2549// ExcerptRange {
2550// context: Point::new(22, 1)..Point::new(33, 1),
2551// primary: None,
2552// },
2553// ExcerptRange {
2554// context: Point::new(44, 1)..Point::new(55, 1),
2555// primary: None,
2556// },
2557// ExcerptRange {
2558// context: Point::new(56, 1)..Point::new(66, 1),
2559// primary: None,
2560// },
2561// ExcerptRange {
2562// context: Point::new(67, 1)..Point::new(77, 1),
2563// primary: None,
2564// },
2565// ],
2566// cx,
2567// );
2568// multibuffer
2569// });
2570
2571// deterministic.run_until_parked();
2572// cx.foreground().run_until_parked();
2573// let editor = cx
2574// .add_window(|cx| Editor::for_multibuffer(multibuffer, Some(project.clone()), cx))
2575// .root(cx);
2576// let editor_edited = Arc::new(AtomicBool::new(false));
2577// let fake_server = fake_servers.next().await.unwrap();
2578// let closure_editor_edited = Arc::clone(&editor_edited);
2579// fake_server
2580// .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
2581// let task_editor_edited = Arc::clone(&closure_editor_edited);
2582// async move {
2583// let hint_text = if params.text_document.uri
2584// == lsp::Url::from_file_path("/a/main.rs").unwrap()
2585// {
2586// "main hint"
2587// } else if params.text_document.uri
2588// == lsp::Url::from_file_path("/a/other.rs").unwrap()
2589// {
2590// "other hint"
2591// } else {
2592// panic!("unexpected uri: {:?}", params.text_document.uri);
2593// };
2594
2595// // one hint per excerpt
2596// let positions = [
2597// lsp::Position::new(0, 2),
2598// lsp::Position::new(4, 2),
2599// lsp::Position::new(22, 2),
2600// lsp::Position::new(44, 2),
2601// lsp::Position::new(56, 2),
2602// lsp::Position::new(67, 2),
2603// ];
2604// let out_of_range_hint = lsp::InlayHint {
2605// position: lsp::Position::new(
2606// params.range.start.line + 99,
2607// params.range.start.character + 99,
2608// ),
2609// label: lsp::InlayHintLabel::String(
2610// "out of excerpt range, should be ignored".to_string(),
2611// ),
2612// kind: None,
2613// text_edits: None,
2614// tooltip: None,
2615// padding_left: None,
2616// padding_right: None,
2617// data: None,
2618// };
2619
2620// let edited = task_editor_edited.load(Ordering::Acquire);
2621// Ok(Some(
2622// std::iter::once(out_of_range_hint)
2623// .chain(positions.into_iter().enumerate().map(|(i, position)| {
2624// lsp::InlayHint {
2625// position,
2626// label: lsp::InlayHintLabel::String(format!(
2627// "{hint_text}{} #{i}",
2628// if edited { "(edited)" } else { "" },
2629// )),
2630// kind: None,
2631// text_edits: None,
2632// tooltip: None,
2633// padding_left: None,
2634// padding_right: None,
2635// data: None,
2636// }
2637// }))
2638// .collect(),
2639// ))
2640// }
2641// })
2642// .next()
2643// .await;
2644// cx.foreground().run_until_parked();
2645
2646// editor.update(cx, |editor, cx| {
2647// let expected_hints = vec![
2648// "main hint #0".to_string(),
2649// "main hint #1".to_string(),
2650// "main hint #2".to_string(),
2651// "main hint #3".to_string(),
2652// ];
2653// assert_eq!(
2654// expected_hints,
2655// cached_hint_labels(editor),
2656// "When scroll is at the edge of a multibuffer, its visible excerpts only should be queried for inlay hints"
2657// );
2658// assert_eq!(expected_hints, visible_hint_labels(editor, cx));
2659// assert_eq!(editor.inlay_hint_cache().version, expected_hints.len(), "Every visible excerpt hints should bump the verison");
2660// });
2661
2662// editor.update(cx, |editor, cx| {
2663// editor.change_selections(Some(Autoscroll::Next), cx, |s| {
2664// s.select_ranges([Point::new(4, 0)..Point::new(4, 0)])
2665// });
2666// editor.change_selections(Some(Autoscroll::Next), cx, |s| {
2667// s.select_ranges([Point::new(22, 0)..Point::new(22, 0)])
2668// });
2669// editor.change_selections(Some(Autoscroll::Next), cx, |s| {
2670// s.select_ranges([Point::new(50, 0)..Point::new(50, 0)])
2671// });
2672// });
2673// cx.foreground().run_until_parked();
2674// editor.update(cx, |editor, cx| {
2675// let expected_hints = vec![
2676// "main hint #0".to_string(),
2677// "main hint #1".to_string(),
2678// "main hint #2".to_string(),
2679// "main hint #3".to_string(),
2680// "main hint #4".to_string(),
2681// "main hint #5".to_string(),
2682// "other hint #0".to_string(),
2683// "other hint #1".to_string(),
2684// "other hint #2".to_string(),
2685// ];
2686// assert_eq!(expected_hints, cached_hint_labels(editor),
2687// "With more scrolls of the multibuffer, more hints should be added into the cache and nothing invalidated without edits");
2688// assert_eq!(expected_hints, visible_hint_labels(editor, cx));
2689// assert_eq!(editor.inlay_hint_cache().version, expected_hints.len(),
2690// "Due to every excerpt having one hint, we update cache per new excerpt scrolled");
2691// });
2692
2693// editor.update(cx, |editor, cx| {
2694// editor.change_selections(Some(Autoscroll::Next), cx, |s| {
2695// s.select_ranges([Point::new(100, 0)..Point::new(100, 0)])
2696// });
2697// });
2698// cx.foreground().advance_clock(Duration::from_millis(
2699// INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100,
2700// ));
2701// cx.foreground().run_until_parked();
2702// let last_scroll_update_version = editor.update(cx, |editor, cx| {
2703// let expected_hints = vec![
2704// "main hint #0".to_string(),
2705// "main hint #1".to_string(),
2706// "main hint #2".to_string(),
2707// "main hint #3".to_string(),
2708// "main hint #4".to_string(),
2709// "main hint #5".to_string(),
2710// "other hint #0".to_string(),
2711// "other hint #1".to_string(),
2712// "other hint #2".to_string(),
2713// "other hint #3".to_string(),
2714// "other hint #4".to_string(),
2715// "other hint #5".to_string(),
2716// ];
2717// assert_eq!(expected_hints, cached_hint_labels(editor),
2718// "After multibuffer was scrolled to the end, all hints for all excerpts should be fetched");
2719// assert_eq!(expected_hints, visible_hint_labels(editor, cx));
2720// assert_eq!(editor.inlay_hint_cache().version, expected_hints.len());
2721// expected_hints.len()
2722// });
2723
2724// editor.update(cx, |editor, cx| {
2725// editor.change_selections(Some(Autoscroll::Next), cx, |s| {
2726// s.select_ranges([Point::new(4, 0)..Point::new(4, 0)])
2727// });
2728// });
2729// cx.foreground().run_until_parked();
2730// editor.update(cx, |editor, cx| {
2731// let expected_hints = vec![
2732// "main hint #0".to_string(),
2733// "main hint #1".to_string(),
2734// "main hint #2".to_string(),
2735// "main hint #3".to_string(),
2736// "main hint #4".to_string(),
2737// "main hint #5".to_string(),
2738// "other hint #0".to_string(),
2739// "other hint #1".to_string(),
2740// "other hint #2".to_string(),
2741// "other hint #3".to_string(),
2742// "other hint #4".to_string(),
2743// "other hint #5".to_string(),
2744// ];
2745// assert_eq!(expected_hints, cached_hint_labels(editor),
2746// "After multibuffer was scrolled to the end, further scrolls up should not bring more hints");
2747// assert_eq!(expected_hints, visible_hint_labels(editor, cx));
2748// assert_eq!(editor.inlay_hint_cache().version, last_scroll_update_version, "No updates should happen during scrolling already scolled buffer");
2749// });
2750
2751// editor_edited.store(true, Ordering::Release);
2752// editor.update(cx, |editor, cx| {
2753// editor.change_selections(None, cx, |s| {
2754// s.select_ranges([Point::new(56, 0)..Point::new(56, 0)])
2755// });
2756// editor.handle_input("++++more text++++", cx);
2757// });
2758// cx.foreground().run_until_parked();
2759// editor.update(cx, |editor, cx| {
2760// let expected_hints = vec![
2761// "main hint(edited) #0".to_string(),
2762// "main hint(edited) #1".to_string(),
2763// "main hint(edited) #2".to_string(),
2764// "main hint(edited) #3".to_string(),
2765// "main hint(edited) #4".to_string(),
2766// "main hint(edited) #5".to_string(),
2767// "other hint(edited) #0".to_string(),
2768// "other hint(edited) #1".to_string(),
2769// ];
2770// assert_eq!(
2771// expected_hints,
2772// cached_hint_labels(editor),
2773// "After multibuffer edit, editor gets scolled back to the last selection; \
2774// all hints should be invalidated and requeried for all of its visible excerpts"
2775// );
2776// assert_eq!(expected_hints, visible_hint_labels(editor, cx));
2777
2778// let current_cache_version = editor.inlay_hint_cache().version;
2779// let minimum_expected_version = last_scroll_update_version + expected_hints.len();
2780// assert!(
2781// current_cache_version == minimum_expected_version || current_cache_version == minimum_expected_version + 1,
2782// "Due to every excerpt having one hint, cache should update per new excerpt received + 1 potential sporadic update"
2783// );
2784// });
2785// }
2786
2787// #[gpui::test]
2788// async fn test_excerpts_removed(
2789// deterministic: Arc<Deterministic>,
2790// cx: &mut gpui::TestAppContext,
2791// ) {
2792// init_test(cx, |settings| {
2793// settings.defaults.inlay_hints = Some(InlayHintSettings {
2794// enabled: true,
2795// show_type_hints: false,
2796// show_parameter_hints: false,
2797// show_other_hints: false,
2798// })
2799// });
2800
2801// let mut language = Language::new(
2802// LanguageConfig {
2803// name: "Rust".into(),
2804// path_suffixes: vec!["rs".to_string()],
2805// ..Default::default()
2806// },
2807// Some(tree_sitter_rust::language()),
2808// );
2809// let mut fake_servers = language
2810// .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
2811// capabilities: lsp::ServerCapabilities {
2812// inlay_hint_provider: Some(lsp::OneOf::Left(true)),
2813// ..Default::default()
2814// },
2815// ..Default::default()
2816// }))
2817// .await;
2818// let language = Arc::new(language);
2819// let fs = FakeFs::new(cx.background());
2820// fs.insert_tree(
2821// "/a",
2822// json!({
2823// "main.rs": format!("fn main() {{\n{}\n}}", (0..501).map(|i| format!("let i = {i};\n")).collect::<Vec<_>>().join("")),
2824// "other.rs": format!("fn main() {{\n{}\n}}", (0..501).map(|j| format!("let j = {j};\n")).collect::<Vec<_>>().join("")),
2825// }),
2826// )
2827// .await;
2828// let project = Project::test(fs, ["/a".as_ref()], cx).await;
2829// project.update(cx, |project, _| {
2830// project.languages().add(Arc::clone(&language))
2831// });
2832// let workspace = cx
2833// .add_window(|cx| Workspace::test_new(project.clone(), cx))
2834// .root(cx);
2835// let worktree_id = workspace.update(cx, |workspace, cx| {
2836// workspace.project().read_with(cx, |project, cx| {
2837// project.worktrees(cx).next().unwrap().read(cx).id()
2838// })
2839// });
2840
2841// let buffer_1 = project
2842// .update(cx, |project, cx| {
2843// project.open_buffer((worktree_id, "main.rs"), cx)
2844// })
2845// .await
2846// .unwrap();
2847// let buffer_2 = project
2848// .update(cx, |project, cx| {
2849// project.open_buffer((worktree_id, "other.rs"), cx)
2850// })
2851// .await
2852// .unwrap();
2853// let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
2854// let (buffer_1_excerpts, buffer_2_excerpts) = multibuffer.update(cx, |multibuffer, cx| {
2855// let buffer_1_excerpts = multibuffer.push_excerpts(
2856// buffer_1.clone(),
2857// [ExcerptRange {
2858// context: Point::new(0, 0)..Point::new(2, 0),
2859// primary: None,
2860// }],
2861// cx,
2862// );
2863// let buffer_2_excerpts = multibuffer.push_excerpts(
2864// buffer_2.clone(),
2865// [ExcerptRange {
2866// context: Point::new(0, 1)..Point::new(2, 1),
2867// primary: None,
2868// }],
2869// cx,
2870// );
2871// (buffer_1_excerpts, buffer_2_excerpts)
2872// });
2873
2874// assert!(!buffer_1_excerpts.is_empty());
2875// assert!(!buffer_2_excerpts.is_empty());
2876
2877// deterministic.run_until_parked();
2878// cx.foreground().run_until_parked();
2879// let editor = cx
2880// .add_window(|cx| Editor::for_multibuffer(multibuffer, Some(project.clone()), cx))
2881// .root(cx);
2882// let editor_edited = Arc::new(AtomicBool::new(false));
2883// let fake_server = fake_servers.next().await.unwrap();
2884// let closure_editor_edited = Arc::clone(&editor_edited);
2885// fake_server
2886// .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
2887// let task_editor_edited = Arc::clone(&closure_editor_edited);
2888// async move {
2889// let hint_text = if params.text_document.uri
2890// == lsp::Url::from_file_path("/a/main.rs").unwrap()
2891// {
2892// "main hint"
2893// } else if params.text_document.uri
2894// == lsp::Url::from_file_path("/a/other.rs").unwrap()
2895// {
2896// "other hint"
2897// } else {
2898// panic!("unexpected uri: {:?}", params.text_document.uri);
2899// };
2900
2901// let positions = [
2902// lsp::Position::new(0, 2),
2903// lsp::Position::new(4, 2),
2904// lsp::Position::new(22, 2),
2905// lsp::Position::new(44, 2),
2906// lsp::Position::new(56, 2),
2907// lsp::Position::new(67, 2),
2908// ];
2909// let out_of_range_hint = lsp::InlayHint {
2910// position: lsp::Position::new(
2911// params.range.start.line + 99,
2912// params.range.start.character + 99,
2913// ),
2914// label: lsp::InlayHintLabel::String(
2915// "out of excerpt range, should be ignored".to_string(),
2916// ),
2917// kind: None,
2918// text_edits: None,
2919// tooltip: None,
2920// padding_left: None,
2921// padding_right: None,
2922// data: None,
2923// };
2924
2925// let edited = task_editor_edited.load(Ordering::Acquire);
2926// Ok(Some(
2927// std::iter::once(out_of_range_hint)
2928// .chain(positions.into_iter().enumerate().map(|(i, position)| {
2929// lsp::InlayHint {
2930// position,
2931// label: lsp::InlayHintLabel::String(format!(
2932// "{hint_text}{} #{i}",
2933// if edited { "(edited)" } else { "" },
2934// )),
2935// kind: None,
2936// text_edits: None,
2937// tooltip: None,
2938// padding_left: None,
2939// padding_right: None,
2940// data: None,
2941// }
2942// }))
2943// .collect(),
2944// ))
2945// }
2946// })
2947// .next()
2948// .await;
2949// cx.foreground().run_until_parked();
2950
2951// editor.update(cx, |editor, cx| {
2952// assert_eq!(
2953// vec!["main hint #0".to_string(), "other hint #0".to_string()],
2954// cached_hint_labels(editor),
2955// "Cache should update for both excerpts despite hints display was disabled"
2956// );
2957// assert!(
2958// visible_hint_labels(editor, cx).is_empty(),
2959// "All hints are disabled and should not be shown despite being present in the cache"
2960// );
2961// assert_eq!(
2962// editor.inlay_hint_cache().version,
2963// 2,
2964// "Cache should update once per excerpt query"
2965// );
2966// });
2967
2968// editor.update(cx, |editor, cx| {
2969// editor.buffer().update(cx, |multibuffer, cx| {
2970// multibuffer.remove_excerpts(buffer_2_excerpts, cx)
2971// })
2972// });
2973// cx.foreground().run_until_parked();
2974// editor.update(cx, |editor, cx| {
2975// assert_eq!(
2976// vec!["main hint #0".to_string()],
2977// cached_hint_labels(editor),
2978// "For the removed excerpt, should clean corresponding cached hints"
2979// );
2980// assert!(
2981// visible_hint_labels(editor, cx).is_empty(),
2982// "All hints are disabled and should not be shown despite being present in the cache"
2983// );
2984// assert_eq!(
2985// editor.inlay_hint_cache().version,
2986// 3,
2987// "Excerpt removal should trigger a cache update"
2988// );
2989// });
2990
2991// update_test_language_settings(cx, |settings| {
2992// settings.defaults.inlay_hints = Some(InlayHintSettings {
2993// enabled: true,
2994// show_type_hints: true,
2995// show_parameter_hints: true,
2996// show_other_hints: true,
2997// })
2998// });
2999// cx.foreground().run_until_parked();
3000// editor.update(cx, |editor, cx| {
3001// let expected_hints = vec!["main hint #0".to_string()];
3002// assert_eq!(
3003// expected_hints,
3004// cached_hint_labels(editor),
3005// "Hint display settings change should not change the cache"
3006// );
3007// assert_eq!(
3008// expected_hints,
3009// visible_hint_labels(editor, cx),
3010// "Settings change should make cached hints visible"
3011// );
3012// assert_eq!(
3013// editor.inlay_hint_cache().version,
3014// 4,
3015// "Settings change should trigger a cache update"
3016// );
3017// });
3018// }
3019
3020// #[gpui::test]
3021// async fn test_inside_char_boundary_range_hints(cx: &mut gpui::TestAppContext) {
3022// init_test(cx, |settings| {
3023// settings.defaults.inlay_hints = Some(InlayHintSettings {
3024// enabled: true,
3025// show_type_hints: true,
3026// show_parameter_hints: true,
3027// show_other_hints: true,
3028// })
3029// });
3030
3031// let mut language = Language::new(
3032// LanguageConfig {
3033// name: "Rust".into(),
3034// path_suffixes: vec!["rs".to_string()],
3035// ..Default::default()
3036// },
3037// Some(tree_sitter_rust::language()),
3038// );
3039// let mut fake_servers = language
3040// .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
3041// capabilities: lsp::ServerCapabilities {
3042// inlay_hint_provider: Some(lsp::OneOf::Left(true)),
3043// ..Default::default()
3044// },
3045// ..Default::default()
3046// }))
3047// .await;
3048// let fs = FakeFs::new(cx.background());
3049// fs.insert_tree(
3050// "/a",
3051// json!({
3052// "main.rs": format!(r#"fn main() {{\n{}\n}}"#, format!("let i = {};\n", "√".repeat(10)).repeat(500)),
3053// "other.rs": "// Test file",
3054// }),
3055// )
3056// .await;
3057// let project = Project::test(fs, ["/a".as_ref()], cx).await;
3058// project.update(cx, |project, _| project.languages().add(Arc::new(language)));
3059// let workspace = cx
3060// .add_window(|cx| Workspace::test_new(project.clone(), cx))
3061// .root(cx);
3062// let worktree_id = workspace.update(cx, |workspace, cx| {
3063// workspace.project().read_with(cx, |project, cx| {
3064// project.worktrees(cx).next().unwrap().read(cx).id()
3065// })
3066// });
3067
3068// let _buffer = project
3069// .update(cx, |project, cx| {
3070// project.open_local_buffer("/a/main.rs", cx)
3071// })
3072// .await
3073// .unwrap();
3074// cx.foreground().run_until_parked();
3075// cx.foreground().start_waiting();
3076// let fake_server = fake_servers.next().await.unwrap();
3077// let editor = workspace
3078// .update(cx, |workspace, cx| {
3079// workspace.open_path((worktree_id, "main.rs"), None, true, cx)
3080// })
3081// .await
3082// .unwrap()
3083// .downcast::<Editor>()
3084// .unwrap();
3085// let lsp_request_count = Arc::new(AtomicU32::new(0));
3086// let closure_lsp_request_count = Arc::clone(&lsp_request_count);
3087// fake_server
3088// .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
3089// let task_lsp_request_count = Arc::clone(&closure_lsp_request_count);
3090// async move {
3091// assert_eq!(
3092// params.text_document.uri,
3093// lsp::Url::from_file_path("/a/main.rs").unwrap(),
3094// );
3095// let query_start = params.range.start;
3096// let i = Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::Release) + 1;
3097// Ok(Some(vec![lsp::InlayHint {
3098// position: query_start,
3099// label: lsp::InlayHintLabel::String(i.to_string()),
3100// kind: None,
3101// text_edits: None,
3102// tooltip: None,
3103// padding_left: None,
3104// padding_right: None,
3105// data: None,
3106// }]))
3107// }
3108// })
3109// .next()
3110// .await;
3111
3112// cx.foreground().run_until_parked();
3113// editor.update(cx, |editor, cx| {
3114// editor.change_selections(None, cx, |s| {
3115// s.select_ranges([Point::new(10, 0)..Point::new(10, 0)])
3116// })
3117// });
3118// cx.foreground().run_until_parked();
3119// editor.update(cx, |editor, cx| {
3120// let expected_hints = vec!["1".to_string()];
3121// assert_eq!(expected_hints, cached_hint_labels(editor));
3122// assert_eq!(expected_hints, visible_hint_labels(editor, cx));
3123// assert_eq!(editor.inlay_hint_cache().version, 1);
3124// });
3125// }
3126
3127// #[gpui::test]
3128// async fn test_toggle_inlay_hints(cx: &mut gpui::TestAppContext) {
3129// init_test(cx, |settings| {
3130// settings.defaults.inlay_hints = Some(InlayHintSettings {
3131// enabled: false,
3132// show_type_hints: true,
3133// show_parameter_hints: true,
3134// show_other_hints: true,
3135// })
3136// });
3137
3138// let (file_with_hints, editor, fake_server) = prepare_test_objects(cx).await;
3139
3140// editor.update(cx, |editor, cx| {
3141// editor.toggle_inlay_hints(&crate::ToggleInlayHints, cx)
3142// });
3143// cx.foreground().start_waiting();
3144// let lsp_request_count = Arc::new(AtomicU32::new(0));
3145// let closure_lsp_request_count = Arc::clone(&lsp_request_count);
3146// fake_server
3147// .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
3148// let task_lsp_request_count = Arc::clone(&closure_lsp_request_count);
3149// async move {
3150// assert_eq!(
3151// params.text_document.uri,
3152// lsp::Url::from_file_path(file_with_hints).unwrap(),
3153// );
3154
3155// let i = Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst) + 1;
3156// Ok(Some(vec![lsp::InlayHint {
3157// position: lsp::Position::new(0, i),
3158// label: lsp::InlayHintLabel::String(i.to_string()),
3159// kind: None,
3160// text_edits: None,
3161// tooltip: None,
3162// padding_left: None,
3163// padding_right: None,
3164// data: None,
3165// }]))
3166// }
3167// })
3168// .next()
3169// .await;
3170// cx.foreground().run_until_parked();
3171// editor.update(cx, |editor, cx| {
3172// let expected_hints = vec!["1".to_string()];
3173// assert_eq!(
3174// expected_hints,
3175// cached_hint_labels(editor),
3176// "Should display inlays after toggle despite them disabled in settings"
3177// );
3178// assert_eq!(expected_hints, visible_hint_labels(editor, cx));
3179// assert_eq!(
3180// editor.inlay_hint_cache().version,
3181// 1,
3182// "First toggle should be cache's first update"
3183// );
3184// });
3185
3186// editor.update(cx, |editor, cx| {
3187// editor.toggle_inlay_hints(&crate::ToggleInlayHints, cx)
3188// });
3189// cx.foreground().run_until_parked();
3190// editor.update(cx, |editor, cx| {
3191// assert!(
3192// cached_hint_labels(editor).is_empty(),
3193// "Should clear hints after 2nd toggle"
3194// );
3195// assert!(visible_hint_labels(editor, cx).is_empty());
3196// assert_eq!(editor.inlay_hint_cache().version, 2);
3197// });
3198
3199// update_test_language_settings(cx, |settings| {
3200// settings.defaults.inlay_hints = Some(InlayHintSettings {
3201// enabled: true,
3202// show_type_hints: true,
3203// show_parameter_hints: true,
3204// show_other_hints: true,
3205// })
3206// });
3207// cx.foreground().run_until_parked();
3208// editor.update(cx, |editor, cx| {
3209// let expected_hints = vec!["2".to_string()];
3210// assert_eq!(
3211// expected_hints,
3212// cached_hint_labels(editor),
3213// "Should query LSP hints for the 2nd time after enabling hints in settings"
3214// );
3215// assert_eq!(expected_hints, visible_hint_labels(editor, cx));
3216// assert_eq!(editor.inlay_hint_cache().version, 3);
3217// });
3218
3219// editor.update(cx, |editor, cx| {
3220// editor.toggle_inlay_hints(&crate::ToggleInlayHints, cx)
3221// });
3222// cx.foreground().run_until_parked();
3223// editor.update(cx, |editor, cx| {
3224// assert!(
3225// cached_hint_labels(editor).is_empty(),
3226// "Should clear hints after enabling in settings and a 3rd toggle"
3227// );
3228// assert!(visible_hint_labels(editor, cx).is_empty());
3229// assert_eq!(editor.inlay_hint_cache().version, 4);
3230// });
3231
3232// editor.update(cx, |editor, cx| {
3233// editor.toggle_inlay_hints(&crate::ToggleInlayHints, cx)
3234// });
3235// cx.foreground().run_until_parked();
3236// editor.update(cx, |editor, cx| {
3237// let expected_hints = vec!["3".to_string()];
3238// assert_eq!(
3239// expected_hints,
3240// cached_hint_labels(editor),
3241// "Should query LSP hints for the 3rd time after enabling hints in settings and toggling them back on"
3242// );
3243// assert_eq!(expected_hints, visible_hint_labels(editor, cx));
3244// assert_eq!(editor.inlay_hint_cache().version, 5);
3245// });
3246// }
3247
3248// pub(crate) fn init_test(cx: &mut TestAppContext, f: impl Fn(&mut AllLanguageSettingsContent)) {
3249// cx.foreground().forbid_parking();
3250
3251// cx.update(|cx| {
3252// cx.set_global(SettingsStore::test(cx));
3253// theme::init(cx);
3254// client::init_settings(cx);
3255// language::init(cx);
3256// Project::init_settings(cx);
3257// workspace::init_settings(cx);
3258// crate::init(cx);
3259// });
3260
3261// update_test_language_settings(cx, f);
3262// }
3263
3264// async fn prepare_test_objects(
3265// cx: &mut TestAppContext,
3266// ) -> (&'static str, ViewHandle<Editor>, FakeLanguageServer) {
3267// let mut language = Language::new(
3268// LanguageConfig {
3269// name: "Rust".into(),
3270// path_suffixes: vec!["rs".to_string()],
3271// ..Default::default()
3272// },
3273// Some(tree_sitter_rust::language()),
3274// );
3275// let mut fake_servers = language
3276// .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
3277// capabilities: lsp::ServerCapabilities {
3278// inlay_hint_provider: Some(lsp::OneOf::Left(true)),
3279// ..Default::default()
3280// },
3281// ..Default::default()
3282// }))
3283// .await;
3284
3285// let fs = FakeFs::new(cx.background());
3286// fs.insert_tree(
3287// "/a",
3288// json!({
3289// "main.rs": "fn main() { a } // and some long comment to ensure inlays are not trimmed out",
3290// "other.rs": "// Test file",
3291// }),
3292// )
3293// .await;
3294
3295// let project = Project::test(fs, ["/a".as_ref()], cx).await;
3296// project.update(cx, |project, _| project.languages().add(Arc::new(language)));
3297// let workspace = cx
3298// .add_window(|cx| Workspace::test_new(project.clone(), cx))
3299// .root(cx);
3300// let worktree_id = workspace.update(cx, |workspace, cx| {
3301// workspace.project().read_with(cx, |project, cx| {
3302// project.worktrees(cx).next().unwrap().read(cx).id()
3303// })
3304// });
3305
3306// let _buffer = project
3307// .update(cx, |project, cx| {
3308// project.open_local_buffer("/a/main.rs", cx)
3309// })
3310// .await
3311// .unwrap();
3312// cx.foreground().run_until_parked();
3313// cx.foreground().start_waiting();
3314// let fake_server = fake_servers.next().await.unwrap();
3315// let editor = workspace
3316// .update(cx, |workspace, cx| {
3317// workspace.open_path((worktree_id, "main.rs"), None, true, cx)
3318// })
3319// .await
3320// .unwrap()
3321// .downcast::<Editor>()
3322// .unwrap();
3323
3324// editor.update(cx, |editor, cx| {
3325// assert!(cached_hint_labels(editor).is_empty());
3326// assert!(visible_hint_labels(editor, cx).is_empty());
3327// assert_eq!(editor.inlay_hint_cache().version, 0);
3328// });
3329
3330// ("/a/main.rs", editor, fake_server)
3331// }
3332
3333// pub fn cached_hint_labels(editor: &Editor) -> Vec<String> {
3334// let mut labels = Vec::new();
3335// for (_, excerpt_hints) in &editor.inlay_hint_cache().hints {
3336// let excerpt_hints = excerpt_hints.read();
3337// for id in &excerpt_hints.ordered_hints {
3338// labels.push(excerpt_hints.hints_by_id[id].text());
3339// }
3340// }
3341
3342// labels.sort();
3343// labels
3344// }
3345
3346// pub fn visible_hint_labels(editor: &Editor, cx: &ViewContext<'_, '_, Editor>) -> Vec<String> {
3347// let mut hints = editor
3348// .visible_inlay_hints(cx)
3349// .into_iter()
3350// .map(|hint| hint.text.to_string())
3351// .collect::<Vec<_>>();
3352// hints.sort();
3353// hints
3354// }
3355// }