Uncomment inlay hints logic and tests (#3307)

Kirill Bulatov created

Tests compile, but do not pass due to todo!s in the platform code, hence
ignored for now.

Release Notes:

- N/A

Change summary

crates/editor2/src/editor_tests.rs         |   24 
crates/editor2/src/inlay_hint_cache.rs     | 5083 +++++++++++------------
crates/gpui2/src/platform/test/platform.rs |    3 
crates/workspace2/src/workspace2.rs        |   42 
4 files changed, 2,548 insertions(+), 2,604 deletions(-)

Detailed changes

crates/editor2/src/editor_tests.rs 🔗

@@ -1,3 +1,7 @@
+use gpui::TestAppContext;
+use language::language_settings::{AllLanguageSettings, AllLanguageSettingsContent};
+use settings::SettingsStore;
+
 // use super::*;
 // use crate::{
 //     scroll::scroll_amount::ScrollAmount,
@@ -8152,16 +8156,16 @@
 //     });
 // }
 
-// pub(crate) fn update_test_language_settings(
-//     cx: &mut TestAppContext,
-//     f: impl Fn(&mut AllLanguageSettingsContent),
-// ) {
-//     cx.update(|cx| {
-//         cx.update_global::<SettingsStore, _, _>(|store, cx| {
-//             store.update_user_settings::<AllLanguageSettings>(cx, f);
-//         });
-//     });
-// }
+pub(crate) fn update_test_language_settings(
+    cx: &mut TestAppContext,
+    f: impl Fn(&mut AllLanguageSettingsContent),
+) {
+    cx.update(|cx| {
+        cx.update_global::<SettingsStore, _>(|store, cx| {
+            store.update_user_settings::<AllLanguageSettings>(cx, f);
+        });
+    });
+}
 
 // pub(crate) fn update_test_project_settings(
 //     cx: &mut TestAppContext,

crates/editor2/src/inlay_hint_cache.rs 🔗

@@ -553,18 +553,17 @@ impl InlayHintCache {
                             let mut resolved_hint =
                                 resolved_hint_task.await.context("hint resolve task")?;
                             editor.update(&mut cx, |editor, _| {
-                                todo!()
-                                // if let Some(excerpt_hints) =
-                                //     editor.inlay_hint_cache.hints.get(&excerpt_id)
-                                // {
-                                //     let mut guard = excerpt_hints.write();
-                                //     if let Some(cached_hint) = guard.hints_by_id.get_mut(&id) {
-                                //         if cached_hint.resolve_state == ResolveState::Resolving {
-                                //             resolved_hint.resolve_state = ResolveState::Resolved;
-                                //             *cached_hint = resolved_hint;
-                                //         }
-                                //     }
-                                // }
+                                if let Some(excerpt_hints) =
+                                    editor.inlay_hint_cache.hints.get(&excerpt_id)
+                                {
+                                    let mut guard = excerpt_hints.write();
+                                    if let Some(cached_hint) = guard.hints_by_id.get_mut(&id) {
+                                        if cached_hint.resolve_state == ResolveState::Resolving {
+                                            resolved_hint.resolve_state = ResolveState::Resolved;
+                                            *cached_hint = resolved_hint;
+                                        }
+                                    }
+                                }
                             })?;
                         }
 
@@ -585,91 +584,89 @@ fn spawn_new_update_tasks(
     update_cache_version: usize,
     cx: &mut ViewContext<'_, Editor>,
 ) {
-    todo!("old version below");
+    let visible_hints = Arc::new(editor.visible_inlay_hints(cx));
+    for (excerpt_id, (excerpt_buffer, new_task_buffer_version, excerpt_visible_range)) in
+        excerpts_to_query
+    {
+        if excerpt_visible_range.is_empty() {
+            continue;
+        }
+        let buffer = excerpt_buffer.read(cx);
+        let buffer_id = buffer.remote_id();
+        let buffer_snapshot = buffer.snapshot();
+        if buffer_snapshot
+            .version()
+            .changed_since(&new_task_buffer_version)
+        {
+            continue;
+        }
+
+        let cached_excerpt_hints = editor.inlay_hint_cache.hints.get(&excerpt_id).cloned();
+        if let Some(cached_excerpt_hints) = &cached_excerpt_hints {
+            let cached_excerpt_hints = cached_excerpt_hints.read();
+            let cached_buffer_version = &cached_excerpt_hints.buffer_version;
+            if cached_excerpt_hints.version > update_cache_version
+                || cached_buffer_version.changed_since(&new_task_buffer_version)
+            {
+                continue;
+            }
+        };
+
+        let (multi_buffer_snapshot, Some(query_ranges)) =
+            editor.buffer.update(cx, |multi_buffer, cx| {
+                (
+                    multi_buffer.snapshot(cx),
+                    determine_query_ranges(
+                        multi_buffer,
+                        excerpt_id,
+                        &excerpt_buffer,
+                        excerpt_visible_range,
+                        cx,
+                    ),
+                )
+            })
+        else {
+            return;
+        };
+        let query = ExcerptQuery {
+            buffer_id,
+            excerpt_id,
+            cache_version: update_cache_version,
+            invalidate,
+            reason,
+        };
+
+        let new_update_task = |query_ranges| {
+            new_update_task(
+                query,
+                query_ranges,
+                multi_buffer_snapshot,
+                buffer_snapshot.clone(),
+                Arc::clone(&visible_hints),
+                cached_excerpt_hints,
+                Arc::clone(&editor.inlay_hint_cache.lsp_request_limiter),
+                cx,
+            )
+        };
+
+        match editor.inlay_hint_cache.update_tasks.entry(excerpt_id) {
+            hash_map::Entry::Occupied(mut o) => {
+                o.get_mut().update_cached_tasks(
+                    &buffer_snapshot,
+                    query_ranges,
+                    invalidate,
+                    new_update_task,
+                );
+            }
+            hash_map::Entry::Vacant(v) => {
+                v.insert(TasksForRanges::new(
+                    query_ranges.clone(),
+                    new_update_task(query_ranges),
+                ));
+            }
+        }
+    }
 }
-//     let visible_hints = Arc::new(editor.visible_inlay_hints(cx));
-//     for (excerpt_id, (excerpt_buffer, new_task_buffer_version, excerpt_visible_range)) in
-//         excerpts_to_query
-//     {
-//         if excerpt_visible_range.is_empty() {
-//             continue;
-//         }
-//         let buffer = excerpt_buffer.read(cx);
-//         let buffer_id = buffer.remote_id();
-//         let buffer_snapshot = buffer.snapshot();
-//         if buffer_snapshot
-//             .version()
-//             .changed_since(&new_task_buffer_version)
-//         {
-//             continue;
-//         }
-
-//         let cached_excerpt_hints = editor.inlay_hint_cache.hints.get(&excerpt_id).cloned();
-//         if let Some(cached_excerpt_hints) = &cached_excerpt_hints {
-//             let cached_excerpt_hints = cached_excerpt_hints.read();
-//             let cached_buffer_version = &cached_excerpt_hints.buffer_version;
-//             if cached_excerpt_hints.version > update_cache_version
-//                 || cached_buffer_version.changed_since(&new_task_buffer_version)
-//             {
-//                 continue;
-//             }
-//         };
-
-//         let (multi_buffer_snapshot, Some(query_ranges)) =
-//             editor.buffer.update(cx, |multi_buffer, cx| {
-//                 (
-//                     multi_buffer.snapshot(cx),
-//                     determine_query_ranges(
-//                         multi_buffer,
-//                         excerpt_id,
-//                         &excerpt_buffer,
-//                         excerpt_visible_range,
-//                         cx,
-//                     ),
-//                 )
-//             })
-//         else {
-//             return;
-//         };
-//         let query = ExcerptQuery {
-//             buffer_id,
-//             excerpt_id,
-//             cache_version: update_cache_version,
-//             invalidate,
-//             reason,
-//         };
-
-//         let new_update_task = |query_ranges| {
-//             new_update_task(
-//                 query,
-//                 query_ranges,
-//                 multi_buffer_snapshot,
-//                 buffer_snapshot.clone(),
-//                 Arc::clone(&visible_hints),
-//                 cached_excerpt_hints,
-//                 Arc::clone(&editor.inlay_hint_cache.lsp_request_limiter),
-//                 cx,
-//             )
-//         };
-
-//         match editor.inlay_hint_cache.update_tasks.entry(excerpt_id) {
-//             hash_map::Entry::Occupied(mut o) => {
-//                 o.get_mut().update_cached_tasks(
-//                     &buffer_snapshot,
-//                     query_ranges,
-//                     invalidate,
-//                     new_update_task,
-//                 );
-//             }
-//             hash_map::Entry::Vacant(v) => {
-//                 v.insert(TasksForRanges::new(
-//                     query_ranges.clone(),
-//                     new_update_task(query_ranges),
-//                 ));
-//             }
-//         }
-//     }
-// }
 
 #[derive(Debug, Clone)]
 struct QueryRanges {
@@ -765,209 +762,208 @@ fn new_update_task(
     lsp_request_limiter: Arc<Semaphore>,
     cx: &mut ViewContext<'_, Editor>,
 ) -> Task<()> {
-    todo!()
-    // cx.spawn(|editor, mut cx| async move {
-    //     let closure_cx = cx.clone();
-    //     let fetch_and_update_hints = |invalidate, range| {
-    //         fetch_and_update_hints(
-    //             editor.clone(),
-    //             multi_buffer_snapshot.clone(),
-    //             buffer_snapshot.clone(),
-    //             Arc::clone(&visible_hints),
-    //             cached_excerpt_hints.as_ref().map(Arc::clone),
-    //             query,
-    //             invalidate,
-    //             range,
-    //             Arc::clone(&lsp_request_limiter),
-    //             closure_cx.clone(),
-    //         )
-    //     };
-    //     let visible_range_update_results = future::join_all(query_ranges.visible.into_iter().map(
-    //         |visible_range| async move {
-    //             (
-    //                 visible_range.clone(),
-    //                 fetch_and_update_hints(query.invalidate.should_invalidate(), visible_range)
-    //                     .await,
-    //             )
-    //         },
-    //     ))
-    //     .await;
-
-    //     let hint_delay = cx.background().timer(Duration::from_millis(
-    //         INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS,
-    //     ));
-
-    //     let mut query_range_failed = |range: &Range<language::Anchor>, e: anyhow::Error| {
-    //         log::error!("inlay hint update task for range {range:?} failed: {e:#}");
-    //         editor
-    //             .update(&mut cx, |editor, _| {
-    //                 if let Some(task_ranges) = editor
-    //                     .inlay_hint_cache
-    //                     .update_tasks
-    //                     .get_mut(&query.excerpt_id)
-    //                 {
-    //                     task_ranges.invalidate_range(&buffer_snapshot, &range);
-    //                 }
-    //             })
-    //             .ok()
-    //     };
-
-    //     for (range, result) in visible_range_update_results {
-    //         if let Err(e) = result {
-    //             query_range_failed(&range, e);
-    //         }
-    //     }
-
-    //     hint_delay.await;
-    //     let invisible_range_update_results = future::join_all(
-    //         query_ranges
-    //             .before_visible
-    //             .into_iter()
-    //             .chain(query_ranges.after_visible.into_iter())
-    //             .map(|invisible_range| async move {
-    //                 (
-    //                     invisible_range.clone(),
-    //                     fetch_and_update_hints(false, invisible_range).await,
-    //                 )
-    //             }),
-    //     )
-    //     .await;
-    //     for (range, result) in invisible_range_update_results {
-    //         if let Err(e) = result {
-    //             query_range_failed(&range, e);
-    //         }
-    //     }
-    // })
+    cx.spawn(|editor, mut cx| async move {
+        let closure_cx = cx.clone();
+        let fetch_and_update_hints = |invalidate, range| {
+            fetch_and_update_hints(
+                editor.clone(),
+                multi_buffer_snapshot.clone(),
+                buffer_snapshot.clone(),
+                Arc::clone(&visible_hints),
+                cached_excerpt_hints.as_ref().map(Arc::clone),
+                query,
+                invalidate,
+                range,
+                Arc::clone(&lsp_request_limiter),
+                closure_cx.clone(),
+            )
+        };
+        let visible_range_update_results = future::join_all(query_ranges.visible.into_iter().map(
+            |visible_range| async move {
+                (
+                    visible_range.clone(),
+                    fetch_and_update_hints(query.invalidate.should_invalidate(), visible_range)
+                        .await,
+                )
+            },
+        ))
+        .await;
+
+        let hint_delay = cx.background_executor().timer(Duration::from_millis(
+            INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS,
+        ));
+
+        let mut query_range_failed = |range: &Range<language::Anchor>, e: anyhow::Error| {
+            log::error!("inlay hint update task for range {range:?} failed: {e:#}");
+            editor
+                .update(&mut cx, |editor, _| {
+                    if let Some(task_ranges) = editor
+                        .inlay_hint_cache
+                        .update_tasks
+                        .get_mut(&query.excerpt_id)
+                    {
+                        task_ranges.invalidate_range(&buffer_snapshot, &range);
+                    }
+                })
+                .ok()
+        };
+
+        for (range, result) in visible_range_update_results {
+            if let Err(e) = result {
+                query_range_failed(&range, e);
+            }
+        }
+
+        hint_delay.await;
+        let invisible_range_update_results = future::join_all(
+            query_ranges
+                .before_visible
+                .into_iter()
+                .chain(query_ranges.after_visible.into_iter())
+                .map(|invisible_range| async move {
+                    (
+                        invisible_range.clone(),
+                        fetch_and_update_hints(false, invisible_range).await,
+                    )
+                }),
+        )
+        .await;
+        for (range, result) in invisible_range_update_results {
+            if let Err(e) = result {
+                query_range_failed(&range, e);
+            }
+        }
+    })
 }
 
-// async fn fetch_and_update_hints(
-//     editor: gpui::WeakView<Editor>,
-//     multi_buffer_snapshot: MultiBufferSnapshot,
-//     buffer_snapshot: BufferSnapshot,
-//     visible_hints: Arc<Vec<Inlay>>,
-//     cached_excerpt_hints: Option<Arc<RwLock<CachedExcerptHints>>>,
-//     query: ExcerptQuery,
-//     invalidate: bool,
-//     fetch_range: Range<language::Anchor>,
-//     lsp_request_limiter: Arc<Semaphore>,
-//     mut cx: gpui::AsyncAppContext,
-// ) -> anyhow::Result<()> {
-//     let (lsp_request_guard, got_throttled) = if query.invalidate.should_invalidate() {
-//         (None, false)
-//     } else {
-//         match lsp_request_limiter.try_acquire() {
-//             Some(guard) => (Some(guard), false),
-//             None => (Some(lsp_request_limiter.acquire().await), true),
-//         }
-//     };
-//     let fetch_range_to_log =
-//         fetch_range.start.to_point(&buffer_snapshot)..fetch_range.end.to_point(&buffer_snapshot);
-//     let inlay_hints_fetch_task = editor
-//         .update(&mut cx, |editor, cx| {
-//             if got_throttled {
-//                 let query_not_around_visible_range = match editor.excerpt_visible_offsets(None, cx).remove(&query.excerpt_id) {
-//                     Some((_, _, current_visible_range)) => {
-//                         let visible_offset_length = current_visible_range.len();
-//                         let double_visible_range = current_visible_range
-//                             .start
-//                             .saturating_sub(visible_offset_length)
-//                             ..current_visible_range
-//                                 .end
-//                                 .saturating_add(visible_offset_length)
-//                                 .min(buffer_snapshot.len());
-//                         !double_visible_range
-//                             .contains(&fetch_range.start.to_offset(&buffer_snapshot))
-//                             && !double_visible_range
-//                                 .contains(&fetch_range.end.to_offset(&buffer_snapshot))
-//                     },
-//                     None => true,
-//                 };
-//                 if query_not_around_visible_range {
-//                     log::trace!("Fetching inlay hints for range {fetch_range_to_log:?} got throttled and fell off the current visible range, skipping.");
-//                     if let Some(task_ranges) = editor
-//                         .inlay_hint_cache
-//                         .update_tasks
-//                         .get_mut(&query.excerpt_id)
-//                     {
-//                         task_ranges.invalidate_range(&buffer_snapshot, &fetch_range);
-//                     }
-//                     return None;
-//                 }
-//             }
-//             editor
-//                 .buffer()
-//                 .read(cx)
-//                 .buffer(query.buffer_id)
-//                 .and_then(|buffer| {
-//                     let project = editor.project.as_ref()?;
-//                     Some(project.update(cx, |project, cx| {
-//                         project.inlay_hints(buffer, fetch_range.clone(), cx)
-//                     }))
-//                 })
-//         })
-//         .ok()
-//         .flatten();
-//     let new_hints = match inlay_hints_fetch_task {
-//         Some(fetch_task) => {
-//             log::debug!(
-//                 "Fetching inlay hints for range {fetch_range_to_log:?}, reason: {query_reason}, invalidate: {invalidate}",
-//                 query_reason = query.reason,
-//             );
-//             log::trace!(
-//                 "Currently visible hints: {visible_hints:?}, cached hints present: {}",
-//                 cached_excerpt_hints.is_some(),
-//             );
-//             fetch_task.await.context("inlay hint fetch task")?
-//         }
-//         None => return Ok(()),
-//     };
-//     drop(lsp_request_guard);
-//     log::debug!(
-//         "Fetched {} hints for range {fetch_range_to_log:?}",
-//         new_hints.len()
-//     );
-//     log::trace!("Fetched hints: {new_hints:?}");
-
-//     let background_task_buffer_snapshot = buffer_snapshot.clone();
-//     let backround_fetch_range = fetch_range.clone();
-//     let new_update = cx
-//         .background()
-//         .spawn(async move {
-//             calculate_hint_updates(
-//                 query.excerpt_id,
-//                 invalidate,
-//                 backround_fetch_range,
-//                 new_hints,
-//                 &background_task_buffer_snapshot,
-//                 cached_excerpt_hints,
-//                 &visible_hints,
-//             )
-//         })
-//         .await;
-//     if let Some(new_update) = new_update {
-//         log::debug!(
-//             "Applying update for range {fetch_range_to_log:?}: remove from editor: {}, remove from cache: {}, add to cache: {}",
-//             new_update.remove_from_visible.len(),
-//             new_update.remove_from_cache.len(),
-//             new_update.add_to_cache.len()
-//         );
-//         log::trace!("New update: {new_update:?}");
-//         editor
-//             .update(&mut cx, |editor, cx| {
-//                 apply_hint_update(
-//                     editor,
-//                     new_update,
-//                     query,
-//                     invalidate,
-//                     buffer_snapshot,
-//                     multi_buffer_snapshot,
-//                     cx,
-//                 );
-//             })
-//             .ok();
-//     }
-//     Ok(())
-// }
+async fn fetch_and_update_hints(
+    editor: gpui::WeakView<Editor>,
+    multi_buffer_snapshot: MultiBufferSnapshot,
+    buffer_snapshot: BufferSnapshot,
+    visible_hints: Arc<Vec<Inlay>>,
+    cached_excerpt_hints: Option<Arc<RwLock<CachedExcerptHints>>>,
+    query: ExcerptQuery,
+    invalidate: bool,
+    fetch_range: Range<language::Anchor>,
+    lsp_request_limiter: Arc<Semaphore>,
+    mut cx: gpui::AsyncWindowContext,
+) -> anyhow::Result<()> {
+    let (lsp_request_guard, got_throttled) = if query.invalidate.should_invalidate() {
+        (None, false)
+    } else {
+        match lsp_request_limiter.try_acquire() {
+            Some(guard) => (Some(guard), false),
+            None => (Some(lsp_request_limiter.acquire().await), true),
+        }
+    };
+    let fetch_range_to_log =
+        fetch_range.start.to_point(&buffer_snapshot)..fetch_range.end.to_point(&buffer_snapshot);
+    let inlay_hints_fetch_task = editor
+        .update(&mut cx, |editor, cx| {
+            if got_throttled {
+                let query_not_around_visible_range = match editor.excerpt_visible_offsets(None, cx).remove(&query.excerpt_id) {
+                    Some((_, _, current_visible_range)) => {
+                        let visible_offset_length = current_visible_range.len();
+                        let double_visible_range = current_visible_range
+                            .start
+                            .saturating_sub(visible_offset_length)
+                            ..current_visible_range
+                                .end
+                                .saturating_add(visible_offset_length)
+                                .min(buffer_snapshot.len());
+                        !double_visible_range
+                            .contains(&fetch_range.start.to_offset(&buffer_snapshot))
+                            && !double_visible_range
+                                .contains(&fetch_range.end.to_offset(&buffer_snapshot))
+                    },
+                    None => true,
+                };
+                if query_not_around_visible_range {
+                    log::trace!("Fetching inlay hints for range {fetch_range_to_log:?} got throttled and fell off the current visible range, skipping.");
+                    if let Some(task_ranges) = editor
+                        .inlay_hint_cache
+                        .update_tasks
+                        .get_mut(&query.excerpt_id)
+                    {
+                        task_ranges.invalidate_range(&buffer_snapshot, &fetch_range);
+                    }
+                    return None;
+                }
+            }
+            editor
+                .buffer()
+                .read(cx)
+                .buffer(query.buffer_id)
+                .and_then(|buffer| {
+                    let project = editor.project.as_ref()?;
+                    Some(project.update(cx, |project, cx| {
+                        project.inlay_hints(buffer, fetch_range.clone(), cx)
+                    }))
+                })
+        })
+        .ok()
+        .flatten();
+    let new_hints = match inlay_hints_fetch_task {
+        Some(fetch_task) => {
+            log::debug!(
+                "Fetching inlay hints for range {fetch_range_to_log:?}, reason: {query_reason}, invalidate: {invalidate}",
+                query_reason = query.reason,
+            );
+            log::trace!(
+                "Currently visible hints: {visible_hints:?}, cached hints present: {}",
+                cached_excerpt_hints.is_some(),
+            );
+            fetch_task.await.context("inlay hint fetch task")?
+        }
+        None => return Ok(()),
+    };
+    drop(lsp_request_guard);
+    log::debug!(
+        "Fetched {} hints for range {fetch_range_to_log:?}",
+        new_hints.len()
+    );
+    log::trace!("Fetched hints: {new_hints:?}");
+
+    let background_task_buffer_snapshot = buffer_snapshot.clone();
+    let backround_fetch_range = fetch_range.clone();
+    let new_update = cx
+        .background_executor()
+        .spawn(async move {
+            calculate_hint_updates(
+                query.excerpt_id,
+                invalidate,
+                backround_fetch_range,
+                new_hints,
+                &background_task_buffer_snapshot,
+                cached_excerpt_hints,
+                &visible_hints,
+            )
+        })
+        .await;
+    if let Some(new_update) = new_update {
+        log::debug!(
+            "Applying update for range {fetch_range_to_log:?}: remove from editor: {}, remove from cache: {}, add to cache: {}",
+            new_update.remove_from_visible.len(),
+            new_update.remove_from_cache.len(),
+            new_update.add_to_cache.len()
+        );
+        log::trace!("New update: {new_update:?}");
+        editor
+            .update(&mut cx, |editor, cx| {
+                apply_hint_update(
+                    editor,
+                    new_update,
+                    query,
+                    invalidate,
+                    buffer_snapshot,
+                    multi_buffer_snapshot,
+                    cx,
+                );
+            })
+            .ok();
+    }
+    Ok(())
+}
 
 fn calculate_hint_updates(
     excerpt_id: ExcerptId,
@@ -1077,2279 +1073,2222 @@ fn apply_hint_update(
     multi_buffer_snapshot: MultiBufferSnapshot,
     cx: &mut ViewContext<'_, Editor>,
 ) {
-    todo!("old implementation commented below")
+    let cached_excerpt_hints = editor
+        .inlay_hint_cache
+        .hints
+        .entry(new_update.excerpt_id)
+        .or_insert_with(|| {
+            Arc::new(RwLock::new(CachedExcerptHints {
+                version: query.cache_version,
+                buffer_version: buffer_snapshot.version().clone(),
+                buffer_id: query.buffer_id,
+                ordered_hints: Vec::new(),
+                hints_by_id: HashMap::default(),
+            }))
+        });
+    let mut cached_excerpt_hints = cached_excerpt_hints.write();
+    match query.cache_version.cmp(&cached_excerpt_hints.version) {
+        cmp::Ordering::Less => return,
+        cmp::Ordering::Greater | cmp::Ordering::Equal => {
+            cached_excerpt_hints.version = query.cache_version;
+        }
+    }
+
+    let mut cached_inlays_changed = !new_update.remove_from_cache.is_empty();
+    cached_excerpt_hints
+        .ordered_hints
+        .retain(|hint_id| !new_update.remove_from_cache.contains(hint_id));
+    cached_excerpt_hints
+        .hints_by_id
+        .retain(|hint_id, _| !new_update.remove_from_cache.contains(hint_id));
+    let mut splice = InlaySplice {
+        to_remove: new_update.remove_from_visible,
+        to_insert: Vec::new(),
+    };
+    for new_hint in new_update.add_to_cache {
+        let insert_position = match cached_excerpt_hints
+            .ordered_hints
+            .binary_search_by(|probe| {
+                cached_excerpt_hints.hints_by_id[probe]
+                    .position
+                    .cmp(&new_hint.position, &buffer_snapshot)
+            }) {
+            Ok(i) => {
+                let mut insert_position = Some(i);
+                for id in &cached_excerpt_hints.ordered_hints[i..] {
+                    let cached_hint = &cached_excerpt_hints.hints_by_id[id];
+                    if new_hint
+                        .position
+                        .cmp(&cached_hint.position, &buffer_snapshot)
+                        .is_gt()
+                    {
+                        break;
+                    }
+                    if cached_hint.text() == new_hint.text() {
+                        insert_position = None;
+                        break;
+                    }
+                }
+                insert_position
+            }
+            Err(i) => Some(i),
+        };
+
+        if let Some(insert_position) = insert_position {
+            let new_inlay_id = post_inc(&mut editor.next_inlay_id);
+            if editor
+                .inlay_hint_cache
+                .allowed_hint_kinds
+                .contains(&new_hint.kind)
+            {
+                let new_hint_position =
+                    multi_buffer_snapshot.anchor_in_excerpt(query.excerpt_id, new_hint.position);
+                splice
+                    .to_insert
+                    .push(Inlay::hint(new_inlay_id, new_hint_position, &new_hint));
+            }
+            let new_id = InlayId::Hint(new_inlay_id);
+            cached_excerpt_hints.hints_by_id.insert(new_id, new_hint);
+            cached_excerpt_hints
+                .ordered_hints
+                .insert(insert_position, new_id);
+            cached_inlays_changed = true;
+        }
+    }
+    cached_excerpt_hints.buffer_version = buffer_snapshot.version().clone();
+    drop(cached_excerpt_hints);
+
+    if invalidate {
+        let mut outdated_excerpt_caches = HashSet::default();
+        for (excerpt_id, excerpt_hints) in &editor.inlay_hint_cache().hints {
+            let excerpt_hints = excerpt_hints.read();
+            if excerpt_hints.buffer_id == query.buffer_id
+                && excerpt_id != &query.excerpt_id
+                && buffer_snapshot
+                    .version()
+                    .changed_since(&excerpt_hints.buffer_version)
+            {
+                outdated_excerpt_caches.insert(*excerpt_id);
+                splice
+                    .to_remove
+                    .extend(excerpt_hints.ordered_hints.iter().copied());
+            }
+        }
+        cached_inlays_changed |= !outdated_excerpt_caches.is_empty();
+        editor
+            .inlay_hint_cache
+            .hints
+            .retain(|excerpt_id, _| !outdated_excerpt_caches.contains(excerpt_id));
+    }
+
+    let InlaySplice {
+        to_remove,
+        to_insert,
+    } = splice;
+    let displayed_inlays_changed = !to_remove.is_empty() || !to_insert.is_empty();
+    if cached_inlays_changed || displayed_inlays_changed {
+        editor.inlay_hint_cache.version += 1;
+    }
+    if displayed_inlays_changed {
+        editor.splice_inlay_hints(to_remove, to_insert, cx)
+    }
+}
+
+#[cfg(test)]
+pub mod tests {
+    use std::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize, Ordering};
+
+    use crate::{
+        scroll::{autoscroll::Autoscroll, scroll_amount::ScrollAmount},
+        ExcerptRange,
+    };
+    use futures::StreamExt;
+    use gpui::{Context, TestAppContext, View, WindowHandle};
+    use itertools::Itertools;
+    use language::{
+        language_settings::AllLanguageSettingsContent, FakeLspAdapter, Language, LanguageConfig,
+    };
+    use lsp::FakeLanguageServer;
+    use parking_lot::Mutex;
+    use project::{FakeFs, Project};
+    use serde_json::json;
+    use settings::SettingsStore;
+    use text::{Point, ToPoint};
+    use workspace::Workspace;
+
+    use crate::editor_tests::update_test_language_settings;
+
+    use super::*;
+
+    // todo!()
+    #[ignore = "fails due to unimplemented `impl PlatformAtlas for TestAtlas` method"]
+    #[gpui::test]
+    async fn test_basic_cache_update_with_duplicate_hints(cx: &mut gpui::TestAppContext) {
+        let allowed_hint_kinds = HashSet::from_iter([None, Some(InlayHintKind::Type)]);
+        init_test(cx, |settings| {
+            settings.defaults.inlay_hints = Some(InlayHintSettings {
+                enabled: true,
+                show_type_hints: allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
+                show_parameter_hints: allowed_hint_kinds.contains(&Some(InlayHintKind::Parameter)),
+                show_other_hints: allowed_hint_kinds.contains(&None),
+            })
+        });
+
+        let (file_with_hints, editor, fake_server) = prepare_test_objects(cx).await;
+        let lsp_request_count = Arc::new(AtomicU32::new(0));
+        fake_server
+            .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
+                let task_lsp_request_count = Arc::clone(&lsp_request_count);
+                async move {
+                    assert_eq!(
+                        params.text_document.uri,
+                        lsp::Url::from_file_path(file_with_hints).unwrap(),
+                    );
+                    let current_call_id =
+                        Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst);
+                    let mut new_hints = Vec::with_capacity(2 * current_call_id as usize);
+                    for _ in 0..2 {
+                        let mut i = current_call_id;
+                        loop {
+                            new_hints.push(lsp::InlayHint {
+                                position: lsp::Position::new(0, i),
+                                label: lsp::InlayHintLabel::String(i.to_string()),
+                                kind: None,
+                                text_edits: None,
+                                tooltip: None,
+                                padding_left: None,
+                                padding_right: None,
+                                data: None,
+                            });
+                            if i == 0 {
+                                break;
+                            }
+                            i -= 1;
+                        }
+                    }
+
+                    Ok(Some(new_hints))
+                }
+            })
+            .next()
+            .await;
+        cx.executor().run_until_parked();
+
+        let mut edits_made = 1;
+        editor.update(cx, |editor, cx| {
+            let expected_hints = vec!["0".to_string()];
+            assert_eq!(
+                expected_hints,
+                cached_hint_labels(editor),
+                "Should get its first hints when opening the editor"
+            );
+            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+            let inlay_cache = editor.inlay_hint_cache();
+            assert_eq!(
+                inlay_cache.allowed_hint_kinds, allowed_hint_kinds,
+                "Cache should use editor settings to get the allowed hint kinds"
+            );
+            assert_eq!(
+                inlay_cache.version, edits_made,
+                "The editor update the cache version after every cache/view change"
+            );
+        });
+
+        editor.update(cx, |editor, cx| {
+            editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
+            editor.handle_input("some change", cx);
+            edits_made += 1;
+        });
+        cx.executor().run_until_parked();
+        editor.update(cx, |editor, cx| {
+            let expected_hints = vec!["0".to_string(), "1".to_string()];
+            assert_eq!(
+                expected_hints,
+                cached_hint_labels(editor),
+                "Should get new hints after an edit"
+            );
+            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+            let inlay_cache = editor.inlay_hint_cache();
+            assert_eq!(
+                inlay_cache.allowed_hint_kinds, allowed_hint_kinds,
+                "Cache should use editor settings to get the allowed hint kinds"
+            );
+            assert_eq!(
+                inlay_cache.version, edits_made,
+                "The editor update the cache version after every cache/view change"
+            );
+        });
+
+        fake_server
+            .request::<lsp::request::InlayHintRefreshRequest>(())
+            .await
+            .expect("inlay refresh request failed");
+        edits_made += 1;
+        cx.executor().run_until_parked();
+        editor.update(cx, |editor, cx| {
+            let expected_hints = vec!["0".to_string(), "1".to_string(), "2".to_string()];
+            assert_eq!(
+                expected_hints,
+                cached_hint_labels(editor),
+                "Should get new hints after hint refresh/ request"
+            );
+            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+            let inlay_cache = editor.inlay_hint_cache();
+            assert_eq!(
+                inlay_cache.allowed_hint_kinds, allowed_hint_kinds,
+                "Cache should use editor settings to get the allowed hint kinds"
+            );
+            assert_eq!(
+                inlay_cache.version, edits_made,
+                "The editor update the cache version after every cache/view change"
+            );
+        });
+    }
+
+    // todo!()
+    #[ignore = "fails due to unimplemented `impl PlatformAtlas for TestAtlas` method"]
+    #[gpui::test]
+    async fn test_cache_update_on_lsp_completion_tasks(cx: &mut gpui::TestAppContext) {
+        init_test(cx, |settings| {
+            settings.defaults.inlay_hints = Some(InlayHintSettings {
+                enabled: true,
+                show_type_hints: true,
+                show_parameter_hints: true,
+                show_other_hints: true,
+            })
+        });
+
+        let (file_with_hints, editor, fake_server) = prepare_test_objects(cx).await;
+        let lsp_request_count = Arc::new(AtomicU32::new(0));
+        fake_server
+            .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
+                let task_lsp_request_count = Arc::clone(&lsp_request_count);
+                async move {
+                    assert_eq!(
+                        params.text_document.uri,
+                        lsp::Url::from_file_path(file_with_hints).unwrap(),
+                    );
+                    let current_call_id =
+                        Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst);
+                    Ok(Some(vec![lsp::InlayHint {
+                        position: lsp::Position::new(0, current_call_id),
+                        label: lsp::InlayHintLabel::String(current_call_id.to_string()),
+                        kind: None,
+                        text_edits: None,
+                        tooltip: None,
+                        padding_left: None,
+                        padding_right: None,
+                        data: None,
+                    }]))
+                }
+            })
+            .next()
+            .await;
+        cx.executor().run_until_parked();
+
+        let mut edits_made = 1;
+        editor.update(cx, |editor, cx| {
+            let expected_hints = vec!["0".to_string()];
+            assert_eq!(
+                expected_hints,
+                cached_hint_labels(editor),
+                "Should get its first hints when opening the editor"
+            );
+            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+            assert_eq!(
+                editor.inlay_hint_cache().version,
+                edits_made,
+                "The editor update the cache version after every cache/view change"
+            );
+        });
+
+        let progress_token = "test_progress_token";
+        fake_server
+            .request::<lsp::request::WorkDoneProgressCreate>(lsp::WorkDoneProgressCreateParams {
+                token: lsp::ProgressToken::String(progress_token.to_string()),
+            })
+            .await
+            .expect("work done progress create request failed");
+        cx.executor().run_until_parked();
+        fake_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
+            token: lsp::ProgressToken::String(progress_token.to_string()),
+            value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Begin(
+                lsp::WorkDoneProgressBegin::default(),
+            )),
+        });
+        cx.executor().run_until_parked();
+
+        editor.update(cx, |editor, cx| {
+            let expected_hints = vec!["0".to_string()];
+            assert_eq!(
+                expected_hints,
+                cached_hint_labels(editor),
+                "Should not update hints while the work task is running"
+            );
+            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+            assert_eq!(
+                editor.inlay_hint_cache().version,
+                edits_made,
+                "Should not update the cache while the work task is running"
+            );
+        });
+
+        fake_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
+            token: lsp::ProgressToken::String(progress_token.to_string()),
+            value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::End(
+                lsp::WorkDoneProgressEnd::default(),
+            )),
+        });
+        cx.executor().run_until_parked();
+
+        edits_made += 1;
+        editor.update(cx, |editor, cx| {
+            let expected_hints = vec!["1".to_string()];
+            assert_eq!(
+                expected_hints,
+                cached_hint_labels(editor),
+                "New hints should be queried after the work task is done"
+            );
+            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+            assert_eq!(
+                editor.inlay_hint_cache().version,
+                edits_made,
+                "Cache version should udpate once after the work task is done"
+            );
+        });
+    }
+
+    // todo!()
+    #[ignore = "fails due to unimplemented `impl PlatformAtlas for TestAtlas` method"]
+    #[gpui::test]
+    async fn test_no_hint_updates_for_unrelated_language_files(cx: &mut gpui::TestAppContext) {
+        init_test(cx, |settings| {
+            settings.defaults.inlay_hints = Some(InlayHintSettings {
+                enabled: true,
+                show_type_hints: true,
+                show_parameter_hints: true,
+                show_other_hints: true,
+            })
+        });
+
+        let fs = FakeFs::new(cx.background_executor.clone());
+        fs.insert_tree(
+                    "/a",
+                    json!({
+                        "main.rs": "fn main() { a } // and some long comment to ensure inlays are not trimmed out",
+                        "other.md": "Test md file with some text",
+                    }),
+                )
+                .await;
+        let project = Project::test(fs, ["/a".as_ref()], cx).await;
+
+        let mut rs_fake_servers = None;
+        let mut md_fake_servers = None;
+        for (name, path_suffix) in [("Rust", "rs"), ("Markdown", "md")] {
+            let mut language = Language::new(
+                LanguageConfig {
+                    name: name.into(),
+                    path_suffixes: vec![path_suffix.to_string()],
+                    ..Default::default()
+                },
+                Some(tree_sitter_rust::language()),
+            );
+            let fake_servers = language
+                .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
+                    name,
+                    capabilities: lsp::ServerCapabilities {
+                        inlay_hint_provider: Some(lsp::OneOf::Left(true)),
+                        ..Default::default()
+                    },
+                    ..Default::default()
+                }))
+                .await;
+            match name {
+                "Rust" => rs_fake_servers = Some(fake_servers),
+                "Markdown" => md_fake_servers = Some(fake_servers),
+                _ => unreachable!(),
+            }
+            project.update(cx, |project, _| {
+                project.languages().add(Arc::new(language));
+            });
+        }
+
+        let rs_buffer = project
+            .update(cx, |project, cx| {
+                project.open_local_buffer("/a/main.rs", cx)
+            })
+            .await
+            .unwrap();
+        cx.executor().run_until_parked();
+        cx.executor().start_waiting();
+        let rs_fake_server = rs_fake_servers.unwrap().next().await.unwrap();
+        let rs_editor =
+            cx.add_window(|cx| Editor::for_buffer(rs_buffer, Some(project.clone()), cx));
+        let rs_lsp_request_count = Arc::new(AtomicU32::new(0));
+        rs_fake_server
+            .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
+                let task_lsp_request_count = Arc::clone(&rs_lsp_request_count);
+                async move {
+                    assert_eq!(
+                        params.text_document.uri,
+                        lsp::Url::from_file_path("/a/main.rs").unwrap(),
+                    );
+                    let i = Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst);
+                    Ok(Some(vec![lsp::InlayHint {
+                        position: lsp::Position::new(0, i),
+                        label: lsp::InlayHintLabel::String(i.to_string()),
+                        kind: None,
+                        text_edits: None,
+                        tooltip: None,
+                        padding_left: None,
+                        padding_right: None,
+                        data: None,
+                    }]))
+                }
+            })
+            .next()
+            .await;
+        cx.executor().run_until_parked();
+        rs_editor.update(cx, |editor, cx| {
+            let expected_hints = vec!["0".to_string()];
+            assert_eq!(
+                expected_hints,
+                cached_hint_labels(editor),
+                "Should get its first hints when opening the editor"
+            );
+            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+            assert_eq!(
+                editor.inlay_hint_cache().version,
+                1,
+                "Rust editor update the cache version after every cache/view change"
+            );
+        });
+
+        cx.executor().run_until_parked();
+        let md_buffer = project
+            .update(cx, |project, cx| {
+                project.open_local_buffer("/a/other.md", cx)
+            })
+            .await
+            .unwrap();
+        cx.executor().run_until_parked();
+        cx.executor().start_waiting();
+        let md_fake_server = md_fake_servers.unwrap().next().await.unwrap();
+        let md_editor = cx.add_window(|cx| Editor::for_buffer(md_buffer, Some(project), cx));
+        let md_lsp_request_count = Arc::new(AtomicU32::new(0));
+        md_fake_server
+            .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
+                let task_lsp_request_count = Arc::clone(&md_lsp_request_count);
+                async move {
+                    assert_eq!(
+                        params.text_document.uri,
+                        lsp::Url::from_file_path("/a/other.md").unwrap(),
+                    );
+                    let i = Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst);
+                    Ok(Some(vec![lsp::InlayHint {
+                        position: lsp::Position::new(0, i),
+                        label: lsp::InlayHintLabel::String(i.to_string()),
+                        kind: None,
+                        text_edits: None,
+                        tooltip: None,
+                        padding_left: None,
+                        padding_right: None,
+                        data: None,
+                    }]))
+                }
+            })
+            .next()
+            .await;
+        cx.executor().run_until_parked();
+        md_editor.update(cx, |editor, cx| {
+            let expected_hints = vec!["0".to_string()];
+            assert_eq!(
+                expected_hints,
+                cached_hint_labels(editor),
+                "Markdown editor should have a separate verison, repeating Rust editor rules"
+            );
+            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+            assert_eq!(editor.inlay_hint_cache().version, 1);
+        });
+
+        rs_editor.update(cx, |editor, cx| {
+            editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
+            editor.handle_input("some rs change", cx);
+        });
+        cx.executor().run_until_parked();
+        rs_editor.update(cx, |editor, cx| {
+            let expected_hints = vec!["1".to_string()];
+            assert_eq!(
+                expected_hints,
+                cached_hint_labels(editor),
+                "Rust inlay cache should change after the edit"
+            );
+            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+            assert_eq!(
+                editor.inlay_hint_cache().version,
+                2,
+                "Every time hint cache changes, cache version should be incremented"
+            );
+        });
+        md_editor.update(cx, |editor, cx| {
+            let expected_hints = vec!["0".to_string()];
+            assert_eq!(
+                expected_hints,
+                cached_hint_labels(editor),
+                "Markdown editor should not be affected by Rust editor changes"
+            );
+            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+            assert_eq!(editor.inlay_hint_cache().version, 1);
+        });
+
+        md_editor.update(cx, |editor, cx| {
+            editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
+            editor.handle_input("some md change", cx);
+        });
+        cx.executor().run_until_parked();
+        md_editor.update(cx, |editor, cx| {
+            let expected_hints = vec!["1".to_string()];
+            assert_eq!(
+                expected_hints,
+                cached_hint_labels(editor),
+                "Rust editor should not be affected by Markdown editor changes"
+            );
+            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+            assert_eq!(editor.inlay_hint_cache().version, 2);
+        });
+        rs_editor.update(cx, |editor, cx| {
+            let expected_hints = vec!["1".to_string()];
+            assert_eq!(
+                expected_hints,
+                cached_hint_labels(editor),
+                "Markdown editor should also change independently"
+            );
+            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+            assert_eq!(editor.inlay_hint_cache().version, 2);
+        });
+    }
+
+    // todo!()
+    #[ignore = "fails due to unimplemented `impl PlatformAtlas for TestAtlas` method"]
+    #[gpui::test]
+    async fn test_hint_setting_changes(cx: &mut gpui::TestAppContext) {
+        let allowed_hint_kinds = HashSet::from_iter([None, Some(InlayHintKind::Type)]);
+        init_test(cx, |settings| {
+            settings.defaults.inlay_hints = Some(InlayHintSettings {
+                enabled: true,
+                show_type_hints: allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
+                show_parameter_hints: allowed_hint_kinds.contains(&Some(InlayHintKind::Parameter)),
+                show_other_hints: allowed_hint_kinds.contains(&None),
+            })
+        });
+
+        let (file_with_hints, editor, fake_server) = prepare_test_objects(cx).await;
+        let lsp_request_count = Arc::new(AtomicU32::new(0));
+        let another_lsp_request_count = Arc::clone(&lsp_request_count);
+        fake_server
+            .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
+                let task_lsp_request_count = Arc::clone(&another_lsp_request_count);
+                async move {
+                    Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst);
+                    assert_eq!(
+                        params.text_document.uri,
+                        lsp::Url::from_file_path(file_with_hints).unwrap(),
+                    );
+                    Ok(Some(vec![
+                        lsp::InlayHint {
+                            position: lsp::Position::new(0, 1),
+                            label: lsp::InlayHintLabel::String("type hint".to_string()),
+                            kind: Some(lsp::InlayHintKind::TYPE),
+                            text_edits: None,
+                            tooltip: None,
+                            padding_left: None,
+                            padding_right: None,
+                            data: None,
+                        },
+                        lsp::InlayHint {
+                            position: lsp::Position::new(0, 2),
+                            label: lsp::InlayHintLabel::String("parameter hint".to_string()),
+                            kind: Some(lsp::InlayHintKind::PARAMETER),
+                            text_edits: None,
+                            tooltip: None,
+                            padding_left: None,
+                            padding_right: None,
+                            data: None,
+                        },
+                        lsp::InlayHint {
+                            position: lsp::Position::new(0, 3),
+                            label: lsp::InlayHintLabel::String("other hint".to_string()),
+                            kind: None,
+                            text_edits: None,
+                            tooltip: None,
+                            padding_left: None,
+                            padding_right: None,
+                            data: None,
+                        },
+                    ]))
+                }
+            })
+            .next()
+            .await;
+        cx.executor().run_until_parked();
+
+        let mut edits_made = 1;
+        editor.update(cx, |editor, cx| {
+            assert_eq!(
+                lsp_request_count.load(Ordering::Relaxed),
+                1,
+                "Should query new hints once"
+            );
+            assert_eq!(
+                vec![
+                    "other hint".to_string(),
+                    "parameter hint".to_string(),
+                    "type hint".to_string(),
+                ],
+                cached_hint_labels(editor),
+                "Should get its first hints when opening the editor"
+            );
+            assert_eq!(
+                vec!["other hint".to_string(), "type hint".to_string()],
+                visible_hint_labels(editor, cx)
+            );
+            let inlay_cache = editor.inlay_hint_cache();
+            assert_eq!(
+                inlay_cache.allowed_hint_kinds, allowed_hint_kinds,
+                "Cache should use editor settings to get the allowed hint kinds"
+            );
+            assert_eq!(
+                inlay_cache.version, edits_made,
+                "The editor update the cache version after every cache/view change"
+            );
+        });
+
+        fake_server
+            .request::<lsp::request::InlayHintRefreshRequest>(())
+            .await
+            .expect("inlay refresh request failed");
+        cx.executor().run_until_parked();
+        editor.update(cx, |editor, cx| {
+            assert_eq!(
+                lsp_request_count.load(Ordering::Relaxed),
+                2,
+                "Should load new hints twice"
+            );
+            assert_eq!(
+                vec![
+                    "other hint".to_string(),
+                    "parameter hint".to_string(),
+                    "type hint".to_string(),
+                ],
+                cached_hint_labels(editor),
+                "Cached hints should not change due to allowed hint kinds settings update"
+            );
+            assert_eq!(
+                vec!["other hint".to_string(), "type hint".to_string()],
+                visible_hint_labels(editor, cx)
+            );
+            assert_eq!(
+                editor.inlay_hint_cache().version,
+                edits_made,
+                "Should not update cache version due to new loaded hints being the same"
+            );
+        });
+
+        for (new_allowed_hint_kinds, expected_visible_hints) in [
+            (HashSet::from_iter([None]), vec!["other hint".to_string()]),
+            (
+                HashSet::from_iter([Some(InlayHintKind::Type)]),
+                vec!["type hint".to_string()],
+            ),
+            (
+                HashSet::from_iter([Some(InlayHintKind::Parameter)]),
+                vec!["parameter hint".to_string()],
+            ),
+            (
+                HashSet::from_iter([None, Some(InlayHintKind::Type)]),
+                vec!["other hint".to_string(), "type hint".to_string()],
+            ),
+            (
+                HashSet::from_iter([None, Some(InlayHintKind::Parameter)]),
+                vec!["other hint".to_string(), "parameter hint".to_string()],
+            ),
+            (
+                HashSet::from_iter([Some(InlayHintKind::Type), Some(InlayHintKind::Parameter)]),
+                vec!["parameter hint".to_string(), "type hint".to_string()],
+            ),
+            (
+                HashSet::from_iter([
+                    None,
+                    Some(InlayHintKind::Type),
+                    Some(InlayHintKind::Parameter),
+                ]),
+                vec![
+                    "other hint".to_string(),
+                    "parameter hint".to_string(),
+                    "type hint".to_string(),
+                ],
+            ),
+        ] {
+            edits_made += 1;
+            update_test_language_settings(cx, |settings| {
+                settings.defaults.inlay_hints = Some(InlayHintSettings {
+                    enabled: true,
+                    show_type_hints: new_allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
+                    show_parameter_hints: new_allowed_hint_kinds
+                        .contains(&Some(InlayHintKind::Parameter)),
+                    show_other_hints: new_allowed_hint_kinds.contains(&None),
+                })
+            });
+            cx.executor().run_until_parked();
+            editor.update(cx, |editor, cx| {
+                assert_eq!(
+                    lsp_request_count.load(Ordering::Relaxed),
+                    2,
+                    "Should not load new hints on allowed hint kinds change for hint kinds {new_allowed_hint_kinds:?}"
+                );
+                assert_eq!(
+                    vec![
+                        "other hint".to_string(),
+                        "parameter hint".to_string(),
+                        "type hint".to_string(),
+                    ],
+                    cached_hint_labels(editor),
+                    "Should get its cached hints unchanged after the settings change for hint kinds {new_allowed_hint_kinds:?}"
+                );
+                assert_eq!(
+                    expected_visible_hints,
+                    visible_hint_labels(editor, cx),
+                    "Should get its visible hints filtered after the settings change for hint kinds {new_allowed_hint_kinds:?}"
+                );
+                let inlay_cache = editor.inlay_hint_cache();
+                assert_eq!(
+                    inlay_cache.allowed_hint_kinds, new_allowed_hint_kinds,
+                    "Cache should use editor settings to get the allowed hint kinds for hint kinds {new_allowed_hint_kinds:?}"
+                );
+                assert_eq!(
+                    inlay_cache.version, edits_made,
+                    "The editor should update the cache version after every cache/view change for hint kinds {new_allowed_hint_kinds:?} due to visible hints change"
+                );
+            });
+        }
+
+        edits_made += 1;
+        let another_allowed_hint_kinds = HashSet::from_iter([Some(InlayHintKind::Type)]);
+        update_test_language_settings(cx, |settings| {
+            settings.defaults.inlay_hints = Some(InlayHintSettings {
+                enabled: false,
+                show_type_hints: another_allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
+                show_parameter_hints: another_allowed_hint_kinds
+                    .contains(&Some(InlayHintKind::Parameter)),
+                show_other_hints: another_allowed_hint_kinds.contains(&None),
+            })
+        });
+        cx.executor().run_until_parked();
+        editor.update(cx, |editor, cx| {
+            assert_eq!(
+                lsp_request_count.load(Ordering::Relaxed),
+                2,
+                "Should not load new hints when hints got disabled"
+            );
+            assert!(
+                cached_hint_labels(editor).is_empty(),
+                "Should clear the cache when hints got disabled"
+            );
+            assert!(
+                visible_hint_labels(editor, cx).is_empty(),
+                "Should clear visible hints when hints got disabled"
+            );
+            let inlay_cache = editor.inlay_hint_cache();
+            assert_eq!(
+                inlay_cache.allowed_hint_kinds, another_allowed_hint_kinds,
+                "Should update its allowed hint kinds even when hints got disabled"
+            );
+            assert_eq!(
+                inlay_cache.version, edits_made,
+                "The editor should update the cache version after hints got disabled"
+            );
+        });
+
+        fake_server
+            .request::<lsp::request::InlayHintRefreshRequest>(())
+            .await
+            .expect("inlay refresh request failed");
+        cx.executor().run_until_parked();
+        editor.update(cx, |editor, cx| {
+            assert_eq!(
+                lsp_request_count.load(Ordering::Relaxed),
+                2,
+                "Should not load new hints when they got disabled"
+            );
+            assert!(cached_hint_labels(editor).is_empty());
+            assert!(visible_hint_labels(editor, cx).is_empty());
+            assert_eq!(
+                editor.inlay_hint_cache().version, edits_made,
+                "The editor should not update the cache version after /refresh query without updates"
+            );
+        });
+
+        let final_allowed_hint_kinds = HashSet::from_iter([Some(InlayHintKind::Parameter)]);
+        edits_made += 1;
+        update_test_language_settings(cx, |settings| {
+            settings.defaults.inlay_hints = Some(InlayHintSettings {
+                enabled: true,
+                show_type_hints: final_allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
+                show_parameter_hints: final_allowed_hint_kinds
+                    .contains(&Some(InlayHintKind::Parameter)),
+                show_other_hints: final_allowed_hint_kinds.contains(&None),
+            })
+        });
+        cx.executor().run_until_parked();
+        editor.update(cx, |editor, cx| {
+            assert_eq!(
+                lsp_request_count.load(Ordering::Relaxed),
+                3,
+                "Should query for new hints when they got reenabled"
+            );
+            assert_eq!(
+                vec![
+                    "other hint".to_string(),
+                    "parameter hint".to_string(),
+                    "type hint".to_string(),
+                ],
+                cached_hint_labels(editor),
+                "Should get its cached hints fully repopulated after the hints got reenabled"
+            );
+            assert_eq!(
+                vec!["parameter hint".to_string()],
+                visible_hint_labels(editor, cx),
+                "Should get its visible hints repopulated and filtered after the h"
+            );
+            let inlay_cache = editor.inlay_hint_cache();
+            assert_eq!(
+                inlay_cache.allowed_hint_kinds, final_allowed_hint_kinds,
+                "Cache should update editor settings when hints got reenabled"
+            );
+            assert_eq!(
+                inlay_cache.version, edits_made,
+                "Cache should update its version after hints got reenabled"
+            );
+        });
+
+        fake_server
+            .request::<lsp::request::InlayHintRefreshRequest>(())
+            .await
+            .expect("inlay refresh request failed");
+        cx.executor().run_until_parked();
+        editor.update(cx, |editor, cx| {
+            assert_eq!(
+                lsp_request_count.load(Ordering::Relaxed),
+                4,
+                "Should query for new hints again"
+            );
+            assert_eq!(
+                vec![
+                    "other hint".to_string(),
+                    "parameter hint".to_string(),
+                    "type hint".to_string(),
+                ],
+                cached_hint_labels(editor),
+            );
+            assert_eq!(
+                vec!["parameter hint".to_string()],
+                visible_hint_labels(editor, cx),
+            );
+            assert_eq!(editor.inlay_hint_cache().version, edits_made);
+        });
+    }
+
+    // todo!()
+    #[ignore = "fails due to unimplemented `impl PlatformAtlas for TestAtlas` method"]
+    #[gpui::test]
+    async fn test_hint_request_cancellation(cx: &mut gpui::TestAppContext) {
+        init_test(cx, |settings| {
+            settings.defaults.inlay_hints = Some(InlayHintSettings {
+                enabled: true,
+                show_type_hints: true,
+                show_parameter_hints: true,
+                show_other_hints: true,
+            })
+        });
+
+        let (file_with_hints, editor, fake_server) = prepare_test_objects(cx).await;
+        let fake_server = Arc::new(fake_server);
+        let lsp_request_count = Arc::new(AtomicU32::new(0));
+        let another_lsp_request_count = Arc::clone(&lsp_request_count);
+        fake_server
+            .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
+                let task_lsp_request_count = Arc::clone(&another_lsp_request_count);
+                async move {
+                    let i = Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst) + 1;
+                    assert_eq!(
+                        params.text_document.uri,
+                        lsp::Url::from_file_path(file_with_hints).unwrap(),
+                    );
+                    Ok(Some(vec![lsp::InlayHint {
+                        position: lsp::Position::new(0, i),
+                        label: lsp::InlayHintLabel::String(i.to_string()),
+                        kind: None,
+                        text_edits: None,
+                        tooltip: None,
+                        padding_left: None,
+                        padding_right: None,
+                        data: None,
+                    }]))
+                }
+            })
+            .next()
+            .await;
+
+        let mut expected_changes = Vec::new();
+        for change_after_opening in [
+            "initial change #1",
+            "initial change #2",
+            "initial change #3",
+        ] {
+            editor.update(cx, |editor, cx| {
+                editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
+                editor.handle_input(change_after_opening, cx);
+            });
+            expected_changes.push(change_after_opening);
+        }
+
+        cx.executor().run_until_parked();
+
+        editor.update(cx, |editor, cx| {
+            let current_text = editor.text(cx);
+            for change in &expected_changes {
+                assert!(
+                    current_text.contains(change),
+                    "Should apply all changes made"
+                );
+            }
+            assert_eq!(
+                lsp_request_count.load(Ordering::Relaxed),
+                2,
+                "Should query new hints twice: for editor init and for the last edit that interrupted all others"
+            );
+            let expected_hints = vec!["2".to_string()];
+            assert_eq!(
+                expected_hints,
+                cached_hint_labels(editor),
+                "Should get hints from the last edit landed only"
+            );
+            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+            assert_eq!(
+                editor.inlay_hint_cache().version, 1,
+                "Only one update should be registered in the cache after all cancellations"
+            );
+        });
+
+        let mut edits = Vec::new();
+        for async_later_change in [
+            "another change #1",
+            "another change #2",
+            "another change #3",
+        ] {
+            expected_changes.push(async_later_change);
+            let task_editor = editor.clone();
+            edits.push(cx.spawn(|mut cx| async move {
+                task_editor.update(&mut cx, |editor, cx| {
+                    editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
+                    editor.handle_input(async_later_change, cx);
+                });
+            }));
+        }
+        let _ = future::join_all(edits).await;
+        cx.executor().run_until_parked();
+
+        editor.update(cx, |editor, cx| {
+            let current_text = editor.text(cx);
+            for change in &expected_changes {
+                assert!(
+                    current_text.contains(change),
+                    "Should apply all changes made"
+                );
+            }
+            assert_eq!(
+                lsp_request_count.load(Ordering::SeqCst),
+                3,
+                "Should query new hints one more time, for the last edit only"
+            );
+            let expected_hints = vec!["3".to_string()];
+            assert_eq!(
+                expected_hints,
+                cached_hint_labels(editor),
+                "Should get hints from the last edit landed only"
+            );
+            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+            assert_eq!(
+                editor.inlay_hint_cache().version,
+                2,
+                "Should update the cache version once more, for the new change"
+            );
+        });
+    }
+
+    // todo!()
+    #[ignore = "fails due to unimplemented `impl PlatformAtlas for TestAtlas` method"]
+    #[gpui::test(iterations = 10)]
+    async fn test_large_buffer_inlay_requests_split(cx: &mut gpui::TestAppContext) {
+        init_test(cx, |settings| {
+            settings.defaults.inlay_hints = Some(InlayHintSettings {
+                enabled: true,
+                show_type_hints: true,
+                show_parameter_hints: true,
+                show_other_hints: true,
+            })
+        });
+
+        let mut language = Language::new(
+            LanguageConfig {
+                name: "Rust".into(),
+                path_suffixes: vec!["rs".to_string()],
+                ..Default::default()
+            },
+            Some(tree_sitter_rust::language()),
+        );
+        let mut fake_servers = language
+            .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
+                capabilities: lsp::ServerCapabilities {
+                    inlay_hint_provider: Some(lsp::OneOf::Left(true)),
+                    ..Default::default()
+                },
+                ..Default::default()
+            }))
+            .await;
+        let fs = FakeFs::new(cx.background_executor.clone());
+        fs.insert_tree(
+            "/a",
+            json!({
+                "main.rs": format!("fn main() {{\n{}\n}}", "let i = 5;\n".repeat(500)),
+                "other.rs": "// Test file",
+            }),
+        )
+        .await;
+        let project = Project::test(fs, ["/a".as_ref()], cx).await;
+        project.update(cx, |project, _| project.languages().add(Arc::new(language)));
+        let buffer = project
+            .update(cx, |project, cx| {
+                project.open_local_buffer("/a/main.rs", cx)
+            })
+            .await
+            .unwrap();
+        cx.executor().run_until_parked();
+        cx.executor().start_waiting();
+        let fake_server = fake_servers.next().await.unwrap();
+        let editor = cx.add_window(|cx| Editor::for_buffer(buffer, Some(project), cx));
+        let lsp_request_ranges = Arc::new(Mutex::new(Vec::new()));
+        let lsp_request_count = Arc::new(AtomicUsize::new(0));
+        let closure_lsp_request_ranges = Arc::clone(&lsp_request_ranges);
+        let closure_lsp_request_count = Arc::clone(&lsp_request_count);
+        fake_server
+            .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
+                let task_lsp_request_ranges = Arc::clone(&closure_lsp_request_ranges);
+                let task_lsp_request_count = Arc::clone(&closure_lsp_request_count);
+                async move {
+                    assert_eq!(
+                        params.text_document.uri,
+                        lsp::Url::from_file_path("/a/main.rs").unwrap(),
+                    );
+
+                    task_lsp_request_ranges.lock().push(params.range);
+                    let i = Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::Release) + 1;
+                    Ok(Some(vec![lsp::InlayHint {
+                        position: params.range.end,
+                        label: lsp::InlayHintLabel::String(i.to_string()),
+                        kind: None,
+                        text_edits: None,
+                        tooltip: None,
+                        padding_left: None,
+                        padding_right: None,
+                        data: None,
+                    }]))
+                }
+            })
+            .next()
+            .await;
+
+        fn editor_visible_range(
+            editor: &WindowHandle<Editor>,
+            cx: &mut gpui::TestAppContext,
+        ) -> Range<Point> {
+            let ranges = editor
+                .update(cx, |editor, cx| editor.excerpt_visible_offsets(None, cx))
+                .unwrap();
+            assert_eq!(
+                ranges.len(),
+                1,
+                "Single buffer should produce a single excerpt with visible range"
+            );
+            let (_, (excerpt_buffer, _, excerpt_visible_range)) =
+                ranges.into_iter().next().unwrap();
+            excerpt_buffer.update(cx, |buffer, _| {
+                let snapshot = buffer.snapshot();
+                let start = buffer
+                    .anchor_before(excerpt_visible_range.start)
+                    .to_point(&snapshot);
+                let end = buffer
+                    .anchor_after(excerpt_visible_range.end)
+                    .to_point(&snapshot);
+                start..end
+            })
+        }
+
+        // in large buffers, requests are made for more than visible range of a buffer.
+        // invisible parts are queried later, to avoid excessive requests on quick typing.
+        // wait the timeout needed to get all requests.
+        cx.executor().advance_clock(Duration::from_millis(
+            INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100,
+        ));
+        cx.executor().run_until_parked();
+        let initial_visible_range = editor_visible_range(&editor, cx);
+        let lsp_initial_visible_range = lsp::Range::new(
+            lsp::Position::new(
+                initial_visible_range.start.row,
+                initial_visible_range.start.column,
+            ),
+            lsp::Position::new(
+                initial_visible_range.end.row,
+                initial_visible_range.end.column,
+            ),
+        );
+        let expected_initial_query_range_end =
+            lsp::Position::new(initial_visible_range.end.row * 2, 2);
+        let mut expected_invisible_query_start = lsp_initial_visible_range.end;
+        expected_invisible_query_start.character += 1;
+        editor.update(cx, |editor, cx| {
+            let ranges = lsp_request_ranges.lock().drain(..).collect::<Vec<_>>();
+            assert_eq!(ranges.len(), 2,
+                "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:?}");
+            let visible_query_range = &ranges[0];
+            assert_eq!(visible_query_range.start, lsp_initial_visible_range.start);
+            assert_eq!(visible_query_range.end, lsp_initial_visible_range.end);
+            let invisible_query_range = &ranges[1];
+
+            assert_eq!(invisible_query_range.start, expected_invisible_query_start, "Should initially query visible edge of the document");
+            assert_eq!(invisible_query_range.end, expected_initial_query_range_end, "Should initially query visible edge of the document");
+
+            let requests_count = lsp_request_count.load(Ordering::Acquire);
+            assert_eq!(requests_count, 2, "Visible + invisible request");
+            let expected_hints = vec!["1".to_string(), "2".to_string()];
+            assert_eq!(
+                expected_hints,
+                cached_hint_labels(editor),
+                "Should have hints from both LSP requests made for a big file"
+            );
+            assert_eq!(expected_hints, visible_hint_labels(editor, cx), "Should display only hints from the visible range");
+            assert_eq!(
+                editor.inlay_hint_cache().version, requests_count,
+                "LSP queries should've bumped the cache version"
+            );
+        });
+
+        editor.update(cx, |editor, cx| {
+            editor.scroll_screen(&ScrollAmount::Page(1.0), cx);
+            editor.scroll_screen(&ScrollAmount::Page(1.0), cx);
+        });
+        cx.executor().advance_clock(Duration::from_millis(
+            INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100,
+        ));
+        cx.executor().run_until_parked();
+        let visible_range_after_scrolls = editor_visible_range(&editor, cx);
+        let visible_line_count = editor
+            .update(cx, |editor, _| editor.visible_line_count().unwrap())
+            .unwrap();
+        let selection_in_cached_range = editor
+            .update(cx, |editor, cx| {
+                let ranges = lsp_request_ranges
+                    .lock()
+                    .drain(..)
+                    .sorted_by_key(|r| r.start)
+                    .collect::<Vec<_>>();
+                assert_eq!(
+                    ranges.len(),
+                    2,
+                    "Should query 2 ranges after both scrolls, but got: {ranges:?}"
+                );
+                let first_scroll = &ranges[0];
+                let second_scroll = &ranges[1];
+                assert_eq!(
+                    first_scroll.end, second_scroll.start,
+                    "Should query 2 adjacent ranges after the scrolls, but got: {ranges:?}"
+                );
+                assert_eq!(
+                first_scroll.start, expected_initial_query_range_end,
+                "First scroll should start the query right after the end of the original scroll",
+            );
+                assert_eq!(
+                second_scroll.end,
+                lsp::Position::new(
+                    visible_range_after_scrolls.end.row
+                        + visible_line_count.ceil() as u32,
+                    1,
+                ),
+                "Second scroll should query one more screen down after the end of the visible range"
+            );
+
+                let lsp_requests = lsp_request_count.load(Ordering::Acquire);
+                assert_eq!(lsp_requests, 4, "Should query for hints after every scroll");
+                let expected_hints = vec![
+                    "1".to_string(),
+                    "2".to_string(),
+                    "3".to_string(),
+                    "4".to_string(),
+                ];
+                assert_eq!(
+                    expected_hints,
+                    cached_hint_labels(editor),
+                    "Should have hints from the new LSP response after the edit"
+                );
+                assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+                assert_eq!(
+                    editor.inlay_hint_cache().version,
+                    lsp_requests,
+                    "Should update the cache for every LSP response with hints added"
+                );
+
+                let mut selection_in_cached_range = visible_range_after_scrolls.end;
+                selection_in_cached_range.row -= visible_line_count.ceil() as u32;
+                selection_in_cached_range
+            })
+            .unwrap();
+
+        editor.update(cx, |editor, cx| {
+            editor.change_selections(Some(Autoscroll::center()), cx, |s| {
+                s.select_ranges([selection_in_cached_range..selection_in_cached_range])
+            });
+        });
+        cx.executor().advance_clock(Duration::from_millis(
+            INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100,
+        ));
+        cx.executor().run_until_parked();
+        editor.update(cx, |_, _| {
+            let ranges = lsp_request_ranges
+                .lock()
+                .drain(..)
+                .sorted_by_key(|r| r.start)
+                .collect::<Vec<_>>();
+            assert!(ranges.is_empty(), "No new ranges or LSP queries should be made after returning to the selection with cached hints");
+            assert_eq!(lsp_request_count.load(Ordering::Acquire), 4);
+        });
+
+        editor.update(cx, |editor, cx| {
+            editor.handle_input("++++more text++++", cx);
+        });
+        cx.executor().advance_clock(Duration::from_millis(
+            INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100,
+        ));
+        cx.executor().run_until_parked();
+        editor.update(cx, |editor, cx| {
+            let mut ranges = lsp_request_ranges.lock().drain(..).collect::<Vec<_>>();
+            ranges.sort_by_key(|r| r.start);
+
+            assert_eq!(ranges.len(), 3,
+                "On edit, should scroll to selection and query a range around it: visible + same range above and below. Instead, got query ranges {ranges:?}");
+            let above_query_range = &ranges[0];
+            let visible_query_range = &ranges[1];
+            let below_query_range = &ranges[2];
+            assert!(above_query_range.end.character < visible_query_range.start.character || above_query_range.end.line + 1 == visible_query_range.start.line,
+                "Above range {above_query_range:?} should be before visible range {visible_query_range:?}");
+            assert!(visible_query_range.end.character < below_query_range.start.character || visible_query_range.end.line  + 1 == below_query_range.start.line,
+                "Visible range {visible_query_range:?} should be before below range {below_query_range:?}");
+            assert!(above_query_range.start.line < selection_in_cached_range.row,
+                "Hints should be queried with the selected range after the query range start");
+            assert!(below_query_range.end.line > selection_in_cached_range.row,
+                "Hints should be queried with the selected range before the query range end");
+            assert!(above_query_range.start.line <= selection_in_cached_range.row - (visible_line_count * 3.0 / 2.0) as u32,
+                "Hints query range should contain one more screen before");
+            assert!(below_query_range.end.line >= selection_in_cached_range.row + (visible_line_count * 3.0 / 2.0) as u32,
+                "Hints query range should contain one more screen after");
+
+            let lsp_requests = lsp_request_count.load(Ordering::Acquire);
+            assert_eq!(lsp_requests, 7, "There should be a visible range and two ranges above and below it queried");
+            let expected_hints = vec!["5".to_string(), "6".to_string(), "7".to_string()];
+            assert_eq!(expected_hints, cached_hint_labels(editor),
+                "Should have hints from the new LSP response after the edit");
+            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+            assert_eq!(editor.inlay_hint_cache().version, lsp_requests, "Should update the cache for every LSP response with hints added");
+        });
+    }
+
+    // todo!()
+    #[ignore = "fails due to text.rs `measurement has not been performed` error"]
+    #[gpui::test(iterations = 10)]
+    async fn test_multiple_excerpts_large_multibuffer(cx: &mut gpui::TestAppContext) {
+        init_test(cx, |settings| {
+            settings.defaults.inlay_hints = Some(InlayHintSettings {
+                enabled: true,
+                show_type_hints: true,
+                show_parameter_hints: true,
+                show_other_hints: true,
+            })
+        });
+
+        let mut language = Language::new(
+            LanguageConfig {
+                name: "Rust".into(),
+                path_suffixes: vec!["rs".to_string()],
+                ..Default::default()
+            },
+            Some(tree_sitter_rust::language()),
+        );
+        let mut fake_servers = language
+            .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
+                capabilities: lsp::ServerCapabilities {
+                    inlay_hint_provider: Some(lsp::OneOf::Left(true)),
+                    ..Default::default()
+                },
+                ..Default::default()
+            }))
+            .await;
+        let language = Arc::new(language);
+        let fs = FakeFs::new(cx.background_executor.clone());
+        fs.insert_tree(
+            "/a",
+            json!({
+                "main.rs": format!("fn main() {{\n{}\n}}", (0..501).map(|i| format!("let i = {i};\n")).collect::<Vec<_>>().join("")),
+                "other.rs": format!("fn main() {{\n{}\n}}", (0..501).map(|j| format!("let j = {j};\n")).collect::<Vec<_>>().join("")),
+            }),
+        )
+        .await;
+        let project = Project::test(fs, ["/a".as_ref()], cx).await;
+        project.update(cx, |project, _| {
+            project.languages().add(Arc::clone(&language))
+        });
+        let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
+        let worktree_id = workspace
+            .update(cx, |workspace, cx| {
+                workspace.project().read_with(cx, |project, cx| {
+                    project.worktrees().next().unwrap().read(cx).id()
+                })
+            })
+            .unwrap();
+
+        let buffer_1 = project
+            .update(cx, |project, cx| {
+                project.open_buffer((worktree_id, "main.rs"), cx)
+            })
+            .await
+            .unwrap();
+        let buffer_2 = project
+            .update(cx, |project, cx| {
+                project.open_buffer((worktree_id, "other.rs"), cx)
+            })
+            .await
+            .unwrap();
+        let multibuffer = cx.build_model(|cx| {
+            let mut multibuffer = MultiBuffer::new(0);
+            multibuffer.push_excerpts(
+                buffer_1.clone(),
+                [
+                    ExcerptRange {
+                        context: Point::new(0, 0)..Point::new(2, 0),
+                        primary: None,
+                    },
+                    ExcerptRange {
+                        context: Point::new(4, 0)..Point::new(11, 0),
+                        primary: None,
+                    },
+                    ExcerptRange {
+                        context: Point::new(22, 0)..Point::new(33, 0),
+                        primary: None,
+                    },
+                    ExcerptRange {
+                        context: Point::new(44, 0)..Point::new(55, 0),
+                        primary: None,
+                    },
+                    ExcerptRange {
+                        context: Point::new(56, 0)..Point::new(66, 0),
+                        primary: None,
+                    },
+                    ExcerptRange {
+                        context: Point::new(67, 0)..Point::new(77, 0),
+                        primary: None,
+                    },
+                ],
+                cx,
+            );
+            multibuffer.push_excerpts(
+                buffer_2.clone(),
+                [
+                    ExcerptRange {
+                        context: Point::new(0, 1)..Point::new(2, 1),
+                        primary: None,
+                    },
+                    ExcerptRange {
+                        context: Point::new(4, 1)..Point::new(11, 1),
+                        primary: None,
+                    },
+                    ExcerptRange {
+                        context: Point::new(22, 1)..Point::new(33, 1),
+                        primary: None,
+                    },
+                    ExcerptRange {
+                        context: Point::new(44, 1)..Point::new(55, 1),
+                        primary: None,
+                    },
+                    ExcerptRange {
+                        context: Point::new(56, 1)..Point::new(66, 1),
+                        primary: None,
+                    },
+                    ExcerptRange {
+                        context: Point::new(67, 1)..Point::new(77, 1),
+                        primary: None,
+                    },
+                ],
+                cx,
+            );
+            multibuffer
+        });
+
+        cx.executor().run_until_parked();
+        let editor =
+            cx.add_window(|cx| Editor::for_multibuffer(multibuffer, Some(project.clone()), cx));
+        let editor_edited = Arc::new(AtomicBool::new(false));
+        let fake_server = fake_servers.next().await.unwrap();
+        let closure_editor_edited = Arc::clone(&editor_edited);
+        fake_server
+            .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
+                let task_editor_edited = Arc::clone(&closure_editor_edited);
+                async move {
+                    let hint_text = if params.text_document.uri
+                        == lsp::Url::from_file_path("/a/main.rs").unwrap()
+                    {
+                        "main hint"
+                    } else if params.text_document.uri
+                        == lsp::Url::from_file_path("/a/other.rs").unwrap()
+                    {
+                        "other hint"
+                    } else {
+                        panic!("unexpected uri: {:?}", params.text_document.uri);
+                    };
+
+                    // one hint per excerpt
+                    let positions = [
+                        lsp::Position::new(0, 2),
+                        lsp::Position::new(4, 2),
+                        lsp::Position::new(22, 2),
+                        lsp::Position::new(44, 2),
+                        lsp::Position::new(56, 2),
+                        lsp::Position::new(67, 2),
+                    ];
+                    let out_of_range_hint = lsp::InlayHint {
+                        position: lsp::Position::new(
+                            params.range.start.line + 99,
+                            params.range.start.character + 99,
+                        ),
+                        label: lsp::InlayHintLabel::String(
+                            "out of excerpt range, should be ignored".to_string(),
+                        ),
+                        kind: None,
+                        text_edits: None,
+                        tooltip: None,
+                        padding_left: None,
+                        padding_right: None,
+                        data: None,
+                    };
+
+                    let edited = task_editor_edited.load(Ordering::Acquire);
+                    Ok(Some(
+                        std::iter::once(out_of_range_hint)
+                            .chain(positions.into_iter().enumerate().map(|(i, position)| {
+                                lsp::InlayHint {
+                                    position,
+                                    label: lsp::InlayHintLabel::String(format!(
+                                        "{hint_text}{} #{i}",
+                                        if edited { "(edited)" } else { "" },
+                                    )),
+                                    kind: None,
+                                    text_edits: None,
+                                    tooltip: None,
+                                    padding_left: None,
+                                    padding_right: None,
+                                    data: None,
+                                }
+                            }))
+                            .collect(),
+                    ))
+                }
+            })
+            .next()
+            .await;
+        cx.executor().run_until_parked();
+
+        editor.update(cx, |editor, cx| {
+            let expected_hints = vec![
+                "main hint #0".to_string(),
+                "main hint #1".to_string(),
+                "main hint #2".to_string(),
+                "main hint #3".to_string(),
+            ];
+            assert_eq!(
+                expected_hints,
+                cached_hint_labels(editor),
+                "When scroll is at the edge of a multibuffer, its visible excerpts only should be queried for inlay hints"
+            );
+            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+            assert_eq!(editor.inlay_hint_cache().version, expected_hints.len(), "Every visible excerpt hints should bump the verison");
+        });
+
+        editor.update(cx, |editor, cx| {
+            editor.change_selections(Some(Autoscroll::Next), cx, |s| {
+                s.select_ranges([Point::new(4, 0)..Point::new(4, 0)])
+            });
+            editor.change_selections(Some(Autoscroll::Next), cx, |s| {
+                s.select_ranges([Point::new(22, 0)..Point::new(22, 0)])
+            });
+            editor.change_selections(Some(Autoscroll::Next), cx, |s| {
+                s.select_ranges([Point::new(50, 0)..Point::new(50, 0)])
+            });
+        });
+        cx.executor().run_until_parked();
+        editor.update(cx, |editor, cx| {
+            let expected_hints = vec![
+                "main hint #0".to_string(),
+                "main hint #1".to_string(),
+                "main hint #2".to_string(),
+                "main hint #3".to_string(),
+                "main hint #4".to_string(),
+                "main hint #5".to_string(),
+                "other hint #0".to_string(),
+                "other hint #1".to_string(),
+                "other hint #2".to_string(),
+            ];
+            assert_eq!(expected_hints, cached_hint_labels(editor),
+                "With more scrolls of the multibuffer, more hints should be added into the cache and nothing invalidated without edits");
+            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+            assert_eq!(editor.inlay_hint_cache().version, expected_hints.len(),
+                "Due to every excerpt having one hint, we update cache per new excerpt scrolled");
+        });
+
+        editor.update(cx, |editor, cx| {
+            editor.change_selections(Some(Autoscroll::Next), cx, |s| {
+                s.select_ranges([Point::new(100, 0)..Point::new(100, 0)])
+            });
+        });
+        cx.executor().advance_clock(Duration::from_millis(
+            INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100,
+        ));
+        cx.executor().run_until_parked();
+        let last_scroll_update_version = editor.update(cx, |editor, cx| {
+            let expected_hints = vec![
+                "main hint #0".to_string(),
+                "main hint #1".to_string(),
+                "main hint #2".to_string(),
+                "main hint #3".to_string(),
+                "main hint #4".to_string(),
+                "main hint #5".to_string(),
+                "other hint #0".to_string(),
+                "other hint #1".to_string(),
+                "other hint #2".to_string(),
+                "other hint #3".to_string(),
+                "other hint #4".to_string(),
+                "other hint #5".to_string(),
+            ];
+            assert_eq!(expected_hints, cached_hint_labels(editor),
+                "After multibuffer was scrolled to the end, all hints for all excerpts should be fetched");
+            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+            assert_eq!(editor.inlay_hint_cache().version, expected_hints.len());
+            expected_hints.len()
+        }).unwrap();
+
+        editor.update(cx, |editor, cx| {
+            editor.change_selections(Some(Autoscroll::Next), cx, |s| {
+                s.select_ranges([Point::new(4, 0)..Point::new(4, 0)])
+            });
+        });
+        cx.executor().run_until_parked();
+        editor.update(cx, |editor, cx| {
+            let expected_hints = vec![
+                "main hint #0".to_string(),
+                "main hint #1".to_string(),
+                "main hint #2".to_string(),
+                "main hint #3".to_string(),
+                "main hint #4".to_string(),
+                "main hint #5".to_string(),
+                "other hint #0".to_string(),
+                "other hint #1".to_string(),
+                "other hint #2".to_string(),
+                "other hint #3".to_string(),
+                "other hint #4".to_string(),
+                "other hint #5".to_string(),
+            ];
+            assert_eq!(expected_hints, cached_hint_labels(editor),
+                "After multibuffer was scrolled to the end, further scrolls up should not bring more hints");
+            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+            assert_eq!(editor.inlay_hint_cache().version, last_scroll_update_version, "No updates should happen during scrolling already scolled buffer");
+        });
+
+        editor_edited.store(true, Ordering::Release);
+        editor.update(cx, |editor, cx| {
+            editor.change_selections(None, cx, |s| {
+                s.select_ranges([Point::new(56, 0)..Point::new(56, 0)])
+            });
+            editor.handle_input("++++more text++++", cx);
+        });
+        cx.executor().run_until_parked();
+        editor.update(cx, |editor, cx| {
+            let expected_hints = vec![
+                "main hint(edited) #0".to_string(),
+                "main hint(edited) #1".to_string(),
+                "main hint(edited) #2".to_string(),
+                "main hint(edited) #3".to_string(),
+                "main hint(edited) #4".to_string(),
+                "main hint(edited) #5".to_string(),
+                "other hint(edited) #0".to_string(),
+                "other hint(edited) #1".to_string(),
+            ];
+            assert_eq!(
+                expected_hints,
+                cached_hint_labels(editor),
+                "After multibuffer edit, editor gets scolled back to the last selection; \
+all hints should be invalidated and requeried for all of its visible excerpts"
+            );
+            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+
+            let current_cache_version = editor.inlay_hint_cache().version;
+            let minimum_expected_version = last_scroll_update_version + expected_hints.len();
+            assert!(
+                current_cache_version == minimum_expected_version || current_cache_version == minimum_expected_version + 1,
+                "Due to every excerpt having one hint, cache should update per new excerpt received + 1 potential sporadic update"
+            );
+        });
+    }
+
+    // todo!()
+    #[ignore = "fails due to text.rs `measurement has not been performed` error"]
+    #[gpui::test]
+    async fn test_excerpts_removed(cx: &mut gpui::TestAppContext) {
+        init_test(cx, |settings| {
+            settings.defaults.inlay_hints = Some(InlayHintSettings {
+                enabled: true,
+                show_type_hints: false,
+                show_parameter_hints: false,
+                show_other_hints: false,
+            })
+        });
+
+        let mut language = Language::new(
+            LanguageConfig {
+                name: "Rust".into(),
+                path_suffixes: vec!["rs".to_string()],
+                ..Default::default()
+            },
+            Some(tree_sitter_rust::language()),
+        );
+        let mut fake_servers = language
+            .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
+                capabilities: lsp::ServerCapabilities {
+                    inlay_hint_provider: Some(lsp::OneOf::Left(true)),
+                    ..Default::default()
+                },
+                ..Default::default()
+            }))
+            .await;
+        let language = Arc::new(language);
+        let fs = FakeFs::new(cx.background_executor.clone());
+        fs.insert_tree(
+            "/a",
+            json!({
+                "main.rs": format!("fn main() {{\n{}\n}}", (0..501).map(|i| format!("let i = {i};\n")).collect::<Vec<_>>().join("")),
+                "other.rs": format!("fn main() {{\n{}\n}}", (0..501).map(|j| format!("let j = {j};\n")).collect::<Vec<_>>().join("")),
+            }),
+        )
+        .await;
+        let project = Project::test(fs, ["/a".as_ref()], cx).await;
+        project.update(cx, |project, _| {
+            project.languages().add(Arc::clone(&language))
+        });
+        let workspace = cx.add_window(|cx| Workspace::test_new(project.clone(), cx));
+        let worktree_id = workspace
+            .update(cx, |workspace, cx| {
+                workspace.project().read_with(cx, |project, cx| {
+                    project.worktrees().next().unwrap().read(cx).id()
+                })
+            })
+            .unwrap();
+
+        let buffer_1 = project
+            .update(cx, |project, cx| {
+                project.open_buffer((worktree_id, "main.rs"), cx)
+            })
+            .await
+            .unwrap();
+        let buffer_2 = project
+            .update(cx, |project, cx| {
+                project.open_buffer((worktree_id, "other.rs"), cx)
+            })
+            .await
+            .unwrap();
+        let multibuffer = cx.build_model(|_| MultiBuffer::new(0));
+        let (buffer_1_excerpts, buffer_2_excerpts) = multibuffer.update(cx, |multibuffer, cx| {
+            let buffer_1_excerpts = multibuffer.push_excerpts(
+                buffer_1.clone(),
+                [ExcerptRange {
+                    context: Point::new(0, 0)..Point::new(2, 0),
+                    primary: None,
+                }],
+                cx,
+            );
+            let buffer_2_excerpts = multibuffer.push_excerpts(
+                buffer_2.clone(),
+                [ExcerptRange {
+                    context: Point::new(0, 1)..Point::new(2, 1),
+                    primary: None,
+                }],
+                cx,
+            );
+            (buffer_1_excerpts, buffer_2_excerpts)
+        });
+
+        assert!(!buffer_1_excerpts.is_empty());
+        assert!(!buffer_2_excerpts.is_empty());
+
+        cx.executor().run_until_parked();
+        let editor =
+            cx.add_window(|cx| Editor::for_multibuffer(multibuffer, Some(project.clone()), cx));
+        let editor_edited = Arc::new(AtomicBool::new(false));
+        let fake_server = fake_servers.next().await.unwrap();
+        let closure_editor_edited = Arc::clone(&editor_edited);
+        fake_server
+            .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
+                let task_editor_edited = Arc::clone(&closure_editor_edited);
+                async move {
+                    let hint_text = if params.text_document.uri
+                        == lsp::Url::from_file_path("/a/main.rs").unwrap()
+                    {
+                        "main hint"
+                    } else if params.text_document.uri
+                        == lsp::Url::from_file_path("/a/other.rs").unwrap()
+                    {
+                        "other hint"
+                    } else {
+                        panic!("unexpected uri: {:?}", params.text_document.uri);
+                    };
+
+                    let positions = [
+                        lsp::Position::new(0, 2),
+                        lsp::Position::new(4, 2),
+                        lsp::Position::new(22, 2),
+                        lsp::Position::new(44, 2),
+                        lsp::Position::new(56, 2),
+                        lsp::Position::new(67, 2),
+                    ];
+                    let out_of_range_hint = lsp::InlayHint {
+                        position: lsp::Position::new(
+                            params.range.start.line + 99,
+                            params.range.start.character + 99,
+                        ),
+                        label: lsp::InlayHintLabel::String(
+                            "out of excerpt range, should be ignored".to_string(),
+                        ),
+                        kind: None,
+                        text_edits: None,
+                        tooltip: None,
+                        padding_left: None,
+                        padding_right: None,
+                        data: None,
+                    };
+
+                    let edited = task_editor_edited.load(Ordering::Acquire);
+                    Ok(Some(
+                        std::iter::once(out_of_range_hint)
+                            .chain(positions.into_iter().enumerate().map(|(i, position)| {
+                                lsp::InlayHint {
+                                    position,
+                                    label: lsp::InlayHintLabel::String(format!(
+                                        "{hint_text}{} #{i}",
+                                        if edited { "(edited)" } else { "" },
+                                    )),
+                                    kind: None,
+                                    text_edits: None,
+                                    tooltip: None,
+                                    padding_left: None,
+                                    padding_right: None,
+                                    data: None,
+                                }
+                            }))
+                            .collect(),
+                    ))
+                }
+            })
+            .next()
+            .await;
+        cx.executor().run_until_parked();
+
+        editor.update(cx, |editor, cx| {
+            assert_eq!(
+                vec!["main hint #0".to_string(), "other hint #0".to_string()],
+                cached_hint_labels(editor),
+                "Cache should update for both excerpts despite hints display was disabled"
+            );
+            assert!(
+                visible_hint_labels(editor, cx).is_empty(),
+                "All hints are disabled and should not be shown despite being present in the cache"
+            );
+            assert_eq!(
+                editor.inlay_hint_cache().version,
+                2,
+                "Cache should update once per excerpt query"
+            );
+        });
+
+        editor.update(cx, |editor, cx| {
+            editor.buffer().update(cx, |multibuffer, cx| {
+                multibuffer.remove_excerpts(buffer_2_excerpts, cx)
+            })
+        });
+        cx.executor().run_until_parked();
+        editor.update(cx, |editor, cx| {
+            assert_eq!(
+                vec!["main hint #0".to_string()],
+                cached_hint_labels(editor),
+                "For the removed excerpt, should clean corresponding cached hints"
+            );
+            assert!(
+                visible_hint_labels(editor, cx).is_empty(),
+                "All hints are disabled and should not be shown despite being present in the cache"
+            );
+            assert_eq!(
+                editor.inlay_hint_cache().version,
+                3,
+                "Excerpt removal should trigger a cache update"
+            );
+        });
+
+        update_test_language_settings(cx, |settings| {
+            settings.defaults.inlay_hints = Some(InlayHintSettings {
+                enabled: true,
+                show_type_hints: true,
+                show_parameter_hints: true,
+                show_other_hints: true,
+            })
+        });
+        cx.executor().run_until_parked();
+        editor.update(cx, |editor, cx| {
+            let expected_hints = vec!["main hint #0".to_string()];
+            assert_eq!(
+                expected_hints,
+                cached_hint_labels(editor),
+                "Hint display settings change should not change the cache"
+            );
+            assert_eq!(
+                expected_hints,
+                visible_hint_labels(editor, cx),
+                "Settings change should make cached hints visible"
+            );
+            assert_eq!(
+                editor.inlay_hint_cache().version,
+                4,
+                "Settings change should trigger a cache update"
+            );
+        });
+    }
+
+    // todo!()
+    #[ignore = "fails due to unimplemented `impl PlatformAtlas for TestAtlas` method"]
+    #[gpui::test]
+    async fn test_inside_char_boundary_range_hints(cx: &mut gpui::TestAppContext) {
+        init_test(cx, |settings| {
+            settings.defaults.inlay_hints = Some(InlayHintSettings {
+                enabled: true,
+                show_type_hints: true,
+                show_parameter_hints: true,
+                show_other_hints: true,
+            })
+        });
+
+        let mut language = Language::new(
+            LanguageConfig {
+                name: "Rust".into(),
+                path_suffixes: vec!["rs".to_string()],
+                ..Default::default()
+            },
+            Some(tree_sitter_rust::language()),
+        );
+        let mut fake_servers = language
+            .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
+                capabilities: lsp::ServerCapabilities {
+                    inlay_hint_provider: Some(lsp::OneOf::Left(true)),
+                    ..Default::default()
+                },
+                ..Default::default()
+            }))
+            .await;
+        let fs = FakeFs::new(cx.background_executor.clone());
+        fs.insert_tree(
+            "/a",
+            json!({
+                "main.rs": format!(r#"fn main() {{\n{}\n}}"#, format!("let i = {};\n", "√".repeat(10)).repeat(500)),
+                "other.rs": "// Test file",
+            }),
+        )
+        .await;
+        let project = Project::test(fs, ["/a".as_ref()], cx).await;
+        project.update(cx, |project, _| project.languages().add(Arc::new(language)));
+        let buffer = project
+            .update(cx, |project, cx| {
+                project.open_local_buffer("/a/main.rs", cx)
+            })
+            .await
+            .unwrap();
+        cx.executor().run_until_parked();
+        cx.executor().start_waiting();
+        let fake_server = fake_servers.next().await.unwrap();
+        let editor = cx.add_window(|cx| Editor::for_buffer(buffer, Some(project), cx));
+        let lsp_request_count = Arc::new(AtomicU32::new(0));
+        let closure_lsp_request_count = Arc::clone(&lsp_request_count);
+        fake_server
+            .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
+                let task_lsp_request_count = Arc::clone(&closure_lsp_request_count);
+                async move {
+                    assert_eq!(
+                        params.text_document.uri,
+                        lsp::Url::from_file_path("/a/main.rs").unwrap(),
+                    );
+                    let query_start = params.range.start;
+                    let i = Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::Release) + 1;
+                    Ok(Some(vec![lsp::InlayHint {
+                        position: query_start,
+                        label: lsp::InlayHintLabel::String(i.to_string()),
+                        kind: None,
+                        text_edits: None,
+                        tooltip: None,
+                        padding_left: None,
+                        padding_right: None,
+                        data: None,
+                    }]))
+                }
+            })
+            .next()
+            .await;
+
+        cx.executor().run_until_parked();
+        editor.update(cx, |editor, cx| {
+            editor.change_selections(None, cx, |s| {
+                s.select_ranges([Point::new(10, 0)..Point::new(10, 0)])
+            })
+        });
+        cx.executor().run_until_parked();
+        editor.update(cx, |editor, cx| {
+            let expected_hints = vec!["1".to_string()];
+            assert_eq!(expected_hints, cached_hint_labels(editor));
+            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+            assert_eq!(editor.inlay_hint_cache().version, 1);
+        });
+    }
+
+    // todo!()
+    #[ignore = "fails due to unimplemented `impl PlatformAtlas for TestAtlas` method"]
+    #[gpui::test]
+    async fn test_toggle_inlay_hints(cx: &mut gpui::TestAppContext) {
+        init_test(cx, |settings| {
+            settings.defaults.inlay_hints = Some(InlayHintSettings {
+                enabled: false,
+                show_type_hints: true,
+                show_parameter_hints: true,
+                show_other_hints: true,
+            })
+        });
+
+        let (file_with_hints, editor, fake_server) = prepare_test_objects(cx).await;
+
+        editor.update(cx, |editor, cx| {
+            editor.toggle_inlay_hints(&crate::ToggleInlayHints, cx)
+        });
+        cx.executor().start_waiting();
+        let lsp_request_count = Arc::new(AtomicU32::new(0));
+        let closure_lsp_request_count = Arc::clone(&lsp_request_count);
+        fake_server
+            .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
+                let task_lsp_request_count = Arc::clone(&closure_lsp_request_count);
+                async move {
+                    assert_eq!(
+                        params.text_document.uri,
+                        lsp::Url::from_file_path(file_with_hints).unwrap(),
+                    );
+
+                    let i = Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst) + 1;
+                    Ok(Some(vec![lsp::InlayHint {
+                        position: lsp::Position::new(0, i),
+                        label: lsp::InlayHintLabel::String(i.to_string()),
+                        kind: None,
+                        text_edits: None,
+                        tooltip: None,
+                        padding_left: None,
+                        padding_right: None,
+                        data: None,
+                    }]))
+                }
+            })
+            .next()
+            .await;
+        cx.executor().run_until_parked();
+        editor.update(cx, |editor, cx| {
+            let expected_hints = vec!["1".to_string()];
+            assert_eq!(
+                expected_hints,
+                cached_hint_labels(editor),
+                "Should display inlays after toggle despite them disabled in settings"
+            );
+            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+            assert_eq!(
+                editor.inlay_hint_cache().version,
+                1,
+                "First toggle should be cache's first update"
+            );
+        });
+
+        editor.update(cx, |editor, cx| {
+            editor.toggle_inlay_hints(&crate::ToggleInlayHints, cx)
+        });
+        cx.executor().run_until_parked();
+        editor.update(cx, |editor, cx| {
+            assert!(
+                cached_hint_labels(editor).is_empty(),
+                "Should clear hints after 2nd toggle"
+            );
+            assert!(visible_hint_labels(editor, cx).is_empty());
+            assert_eq!(editor.inlay_hint_cache().version, 2);
+        });
+
+        update_test_language_settings(cx, |settings| {
+            settings.defaults.inlay_hints = Some(InlayHintSettings {
+                enabled: true,
+                show_type_hints: true,
+                show_parameter_hints: true,
+                show_other_hints: true,
+            })
+        });
+        cx.executor().run_until_parked();
+        editor.update(cx, |editor, cx| {
+            let expected_hints = vec!["2".to_string()];
+            assert_eq!(
+                expected_hints,
+                cached_hint_labels(editor),
+                "Should query LSP hints for the 2nd time after enabling hints in settings"
+            );
+            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+            assert_eq!(editor.inlay_hint_cache().version, 3);
+        });
+
+        editor.update(cx, |editor, cx| {
+            editor.toggle_inlay_hints(&crate::ToggleInlayHints, cx)
+        });
+        cx.executor().run_until_parked();
+        editor.update(cx, |editor, cx| {
+            assert!(
+                cached_hint_labels(editor).is_empty(),
+                "Should clear hints after enabling in settings and a 3rd toggle"
+            );
+            assert!(visible_hint_labels(editor, cx).is_empty());
+            assert_eq!(editor.inlay_hint_cache().version, 4);
+        });
+
+        editor.update(cx, |editor, cx| {
+            editor.toggle_inlay_hints(&crate::ToggleInlayHints, cx)
+        });
+        cx.executor().run_until_parked();
+        editor.update(cx, |editor, cx| {
+            let expected_hints = vec!["3".to_string()];
+            assert_eq!(
+                expected_hints,
+                cached_hint_labels(editor),
+                "Should query LSP hints for the 3rd time after enabling hints in settings and toggling them back on"
+            );
+            assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+            assert_eq!(editor.inlay_hint_cache().version, 5);
+        });
+    }
+
+    pub(crate) fn init_test(cx: &mut TestAppContext, f: impl Fn(&mut AllLanguageSettingsContent)) {
+        cx.update(|cx| {
+            let settings_store = SettingsStore::test(cx);
+            cx.set_global(settings_store);
+            theme::init(cx);
+            client::init_settings(cx);
+            language::init(cx);
+            Project::init_settings(cx);
+            workspace::init_settings(cx);
+            crate::init(cx);
+        });
+
+        update_test_language_settings(cx, f);
+    }
+
+    async fn prepare_test_objects(
+        cx: &mut TestAppContext,
+    ) -> (&'static str, WindowHandle<Editor>, FakeLanguageServer) {
+        let mut language = Language::new(
+            LanguageConfig {
+                name: "Rust".into(),
+                path_suffixes: vec!["rs".to_string()],
+                ..Default::default()
+            },
+            Some(tree_sitter_rust::language()),
+        );
+        let mut fake_servers = language
+            .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
+                capabilities: lsp::ServerCapabilities {
+                    inlay_hint_provider: Some(lsp::OneOf::Left(true)),
+                    ..Default::default()
+                },
+                ..Default::default()
+            }))
+            .await;
+
+        let fs = FakeFs::new(cx.background_executor.clone());
+        fs.insert_tree(
+            "/a",
+            json!({
+                "main.rs": "fn main() { a } // and some long comment to ensure inlays are not trimmed out",
+                "other.rs": "// Test file",
+            }),
+        )
+        .await;
+
+        let project = Project::test(fs, ["/a".as_ref()], cx).await;
+        project.update(cx, |project, _| project.languages().add(Arc::new(language)));
+        let buffer = project
+            .update(cx, |project, cx| {
+                project.open_local_buffer("/a/main.rs", cx)
+            })
+            .await
+            .unwrap();
+        cx.executor().run_until_parked();
+        cx.executor().start_waiting();
+        let fake_server = fake_servers.next().await.unwrap();
+        let editor = cx.add_window(|cx| Editor::for_buffer(buffer, Some(project), cx));
+
+        editor.update(cx, |editor, cx| {
+            assert!(cached_hint_labels(editor).is_empty());
+            assert!(visible_hint_labels(editor, cx).is_empty());
+            assert_eq!(editor.inlay_hint_cache().version, 0);
+        });
+
+        ("/a/main.rs", editor, fake_server)
+    }
+
+    pub fn cached_hint_labels(editor: &Editor) -> Vec<String> {
+        let mut labels = Vec::new();
+        for (_, excerpt_hints) in &editor.inlay_hint_cache().hints {
+            let excerpt_hints = excerpt_hints.read();
+            for id in &excerpt_hints.ordered_hints {
+                labels.push(excerpt_hints.hints_by_id[id].text());
+            }
+        }
+
+        labels.sort();
+        labels
+    }
+
+    pub fn visible_hint_labels(editor: &Editor, cx: &ViewContext<'_, Editor>) -> Vec<String> {
+        let mut hints = editor
+            .visible_inlay_hints(cx)
+            .into_iter()
+            .map(|hint| hint.text.to_string())
+            .collect::<Vec<_>>();
+        hints.sort();
+        hints
+    }
 }
-//     let cached_excerpt_hints = editor
-//         .inlay_hint_cache
-//         .hints
-//         .entry(new_update.excerpt_id)
-//         .or_insert_with(|| {
-//             Arc::new(RwLock::new(CachedExcerptHints {
-//                 version: query.cache_version,
-//                 buffer_version: buffer_snapshot.version().clone(),
-//                 buffer_id: query.buffer_id,
-//                 ordered_hints: Vec::new(),
-//                 hints_by_id: HashMap::default(),
-//             }))
-//         });
-//     let mut cached_excerpt_hints = cached_excerpt_hints.write();
-//     match query.cache_version.cmp(&cached_excerpt_hints.version) {
-//         cmp::Ordering::Less => return,
-//         cmp::Ordering::Greater | cmp::Ordering::Equal => {
-//             cached_excerpt_hints.version = query.cache_version;
-//         }
-//     }
-
-//     let mut cached_inlays_changed = !new_update.remove_from_cache.is_empty();
-//     cached_excerpt_hints
-//         .ordered_hints
-//         .retain(|hint_id| !new_update.remove_from_cache.contains(hint_id));
-//     cached_excerpt_hints
-//         .hints_by_id
-//         .retain(|hint_id, _| !new_update.remove_from_cache.contains(hint_id));
-//     let mut splice = InlaySplice {
-//         to_remove: new_update.remove_from_visible,
-//         to_insert: Vec::new(),
-//     };
-//     for new_hint in new_update.add_to_cache {
-//         let insert_position = match cached_excerpt_hints
-//             .ordered_hints
-//             .binary_search_by(|probe| {
-//                 cached_excerpt_hints.hints_by_id[probe]
-//                     .position
-//                     .cmp(&new_hint.position, &buffer_snapshot)
-//             }) {
-//             Ok(i) => {
-//                 let mut insert_position = Some(i);
-//                 for id in &cached_excerpt_hints.ordered_hints[i..] {
-//                     let cached_hint = &cached_excerpt_hints.hints_by_id[id];
-//                     if new_hint
-//                         .position
-//                         .cmp(&cached_hint.position, &buffer_snapshot)
-//                         .is_gt()
-//                     {
-//                         break;
-//                     }
-//                     if cached_hint.text() == new_hint.text() {
-//                         insert_position = None;
-//                         break;
-//                     }
-//                 }
-//                 insert_position
-//             }
-//             Err(i) => Some(i),
-//         };
-
-//         if let Some(insert_position) = insert_position {
-//             let new_inlay_id = post_inc(&mut editor.next_inlay_id);
-//             if editor
-//                 .inlay_hint_cache
-//                 .allowed_hint_kinds
-//                 .contains(&new_hint.kind)
-//             {
-//                 let new_hint_position =
-//                     multi_buffer_snapshot.anchor_in_excerpt(query.excerpt_id, new_hint.position);
-//                 splice
-//                     .to_insert
-//                     .push(Inlay::hint(new_inlay_id, new_hint_position, &new_hint));
-//             }
-//             let new_id = InlayId::Hint(new_inlay_id);
-//             cached_excerpt_hints.hints_by_id.insert(new_id, new_hint);
-//             cached_excerpt_hints
-//                 .ordered_hints
-//                 .insert(insert_position, new_id);
-//             cached_inlays_changed = true;
-//         }
-//     }
-//     cached_excerpt_hints.buffer_version = buffer_snapshot.version().clone();
-//     drop(cached_excerpt_hints);
-
-//     if invalidate {
-//         let mut outdated_excerpt_caches = HashSet::default();
-//         for (excerpt_id, excerpt_hints) in &editor.inlay_hint_cache().hints {
-//             let excerpt_hints = excerpt_hints.read();
-//             if excerpt_hints.buffer_id == query.buffer_id
-//                 && excerpt_id != &query.excerpt_id
-//                 && buffer_snapshot
-//                     .version()
-//                     .changed_since(&excerpt_hints.buffer_version)
-//             {
-//                 outdated_excerpt_caches.insert(*excerpt_id);
-//                 splice
-//                     .to_remove
-//                     .extend(excerpt_hints.ordered_hints.iter().copied());
-//             }
-//         }
-//         cached_inlays_changed |= !outdated_excerpt_caches.is_empty();
-//         editor
-//             .inlay_hint_cache
-//             .hints
-//             .retain(|excerpt_id, _| !outdated_excerpt_caches.contains(excerpt_id));
-//     }
-
-//     let InlaySplice {
-//         to_remove,
-//         to_insert,
-//     } = splice;
-//     let displayed_inlays_changed = !to_remove.is_empty() || !to_insert.is_empty();
-//     if cached_inlays_changed || displayed_inlays_changed {
-//         editor.inlay_hint_cache.version += 1;
-//     }
-//     if displayed_inlays_changed {
-//         editor.splice_inlay_hints(to_remove, to_insert, cx)
-//     }
-// }
-
-// #[cfg(test)]
-// pub mod tests {
-//     use std::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize, Ordering};
-
-//     use crate::{
-//         scroll::{autoscroll::Autoscroll, scroll_amount::ScrollAmount},
-//         serde_json::json,
-//         ExcerptRange,
-//     };
-//     use futures::StreamExt;
-//     use gpui::{executor::Deterministic, TestAppContext, View};
-//     use itertools::Itertools;
-//     use language::{
-//         language_settings::AllLanguageSettingsContent, FakeLspAdapter, Language, LanguageConfig,
-//     };
-//     use lsp::FakeLanguageServer;
-//     use parking_lot::Mutex;
-//     use project::{FakeFs, Project};
-//     use settings::SettingsStore;
-//     use text::{Point, ToPoint};
-//     use workspace::Workspace;
-
-//     use crate::editor_tests::update_test_language_settings;
-
-//     use super::*;
-
-//     #[gpui::test]
-//     async fn test_basic_cache_update_with_duplicate_hints(cx: &mut gpui::TestAppContext) {
-//         let allowed_hint_kinds = HashSet::from_iter([None, Some(InlayHintKind::Type)]);
-//         init_test(cx, |settings| {
-//             settings.defaults.inlay_hints = Some(InlayHintSettings {
-//                 enabled: true,
-//                 show_type_hints: allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
-//                 show_parameter_hints: allowed_hint_kinds.contains(&Some(InlayHintKind::Parameter)),
-//                 show_other_hints: allowed_hint_kinds.contains(&None),
-//             })
-//         });
-
-//         let (file_with_hints, editor, fake_server) = prepare_test_objects(cx).await;
-//         let lsp_request_count = Arc::new(AtomicU32::new(0));
-//         fake_server
-//             .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
-//                 let task_lsp_request_count = Arc::clone(&lsp_request_count);
-//                 async move {
-//                     assert_eq!(
-//                         params.text_document.uri,
-//                         lsp::Url::from_file_path(file_with_hints).unwrap(),
-//                     );
-//                     let current_call_id =
-//                         Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst);
-//                     let mut new_hints = Vec::with_capacity(2 * current_call_id as usize);
-//                     for _ in 0..2 {
-//                         let mut i = current_call_id;
-//                         loop {
-//                             new_hints.push(lsp::InlayHint {
-//                                 position: lsp::Position::new(0, i),
-//                                 label: lsp::InlayHintLabel::String(i.to_string()),
-//                                 kind: None,
-//                                 text_edits: None,
-//                                 tooltip: None,
-//                                 padding_left: None,
-//                                 padding_right: None,
-//                                 data: None,
-//                             });
-//                             if i == 0 {
-//                                 break;
-//                             }
-//                             i -= 1;
-//                         }
-//                     }
-
-//                     Ok(Some(new_hints))
-//                 }
-//             })
-//             .next()
-//             .await;
-//         cx.foreground().run_until_parked();
-
-//         let mut edits_made = 1;
-//         editor.update(cx, |editor, cx| {
-//             let expected_hints = vec!["0".to_string()];
-//             assert_eq!(
-//                 expected_hints,
-//                 cached_hint_labels(editor),
-//                 "Should get its first hints when opening the editor"
-//             );
-//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-//             let inlay_cache = editor.inlay_hint_cache();
-//             assert_eq!(
-//                 inlay_cache.allowed_hint_kinds, allowed_hint_kinds,
-//                 "Cache should use editor settings to get the allowed hint kinds"
-//             );
-//             assert_eq!(
-//                 inlay_cache.version, edits_made,
-//                 "The editor update the cache version after every cache/view change"
-//             );
-//         });
-
-//         editor.update(cx, |editor, cx| {
-//             editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
-//             editor.handle_input("some change", cx);
-//             edits_made += 1;
-//         });
-//         cx.foreground().run_until_parked();
-//         editor.update(cx, |editor, cx| {
-//             let expected_hints = vec!["0".to_string(), "1".to_string()];
-//             assert_eq!(
-//                 expected_hints,
-//                 cached_hint_labels(editor),
-//                 "Should get new hints after an edit"
-//             );
-//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-//             let inlay_cache = editor.inlay_hint_cache();
-//             assert_eq!(
-//                 inlay_cache.allowed_hint_kinds, allowed_hint_kinds,
-//                 "Cache should use editor settings to get the allowed hint kinds"
-//             );
-//             assert_eq!(
-//                 inlay_cache.version, edits_made,
-//                 "The editor update the cache version after every cache/view change"
-//             );
-//         });
-
-//         fake_server
-//             .request::<lsp::request::InlayHintRefreshRequest>(())
-//             .await
-//             .expect("inlay refresh request failed");
-//         edits_made += 1;
-//         cx.foreground().run_until_parked();
-//         editor.update(cx, |editor, cx| {
-//             let expected_hints = vec!["0".to_string(), "1".to_string(), "2".to_string()];
-//             assert_eq!(
-//                 expected_hints,
-//                 cached_hint_labels(editor),
-//                 "Should get new hints after hint refresh/ request"
-//             );
-//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-//             let inlay_cache = editor.inlay_hint_cache();
-//             assert_eq!(
-//                 inlay_cache.allowed_hint_kinds, allowed_hint_kinds,
-//                 "Cache should use editor settings to get the allowed hint kinds"
-//             );
-//             assert_eq!(
-//                 inlay_cache.version, edits_made,
-//                 "The editor update the cache version after every cache/view change"
-//             );
-//         });
-//     }
-
-//     #[gpui::test]
-//     async fn test_cache_update_on_lsp_completion_tasks(cx: &mut gpui::TestAppContext) {
-//         init_test(cx, |settings| {
-//             settings.defaults.inlay_hints = Some(InlayHintSettings {
-//                 enabled: true,
-//                 show_type_hints: true,
-//                 show_parameter_hints: true,
-//                 show_other_hints: true,
-//             })
-//         });
-
-//         let (file_with_hints, editor, fake_server) = prepare_test_objects(cx).await;
-//         let lsp_request_count = Arc::new(AtomicU32::new(0));
-//         fake_server
-//             .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
-//                 let task_lsp_request_count = Arc::clone(&lsp_request_count);
-//                 async move {
-//                     assert_eq!(
-//                         params.text_document.uri,
-//                         lsp::Url::from_file_path(file_with_hints).unwrap(),
-//                     );
-//                     let current_call_id =
-//                         Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst);
-//                     Ok(Some(vec![lsp::InlayHint {
-//                         position: lsp::Position::new(0, current_call_id),
-//                         label: lsp::InlayHintLabel::String(current_call_id.to_string()),
-//                         kind: None,
-//                         text_edits: None,
-//                         tooltip: None,
-//                         padding_left: None,
-//                         padding_right: None,
-//                         data: None,
-//                     }]))
-//                 }
-//             })
-//             .next()
-//             .await;
-//         cx.foreground().run_until_parked();
-
-//         let mut edits_made = 1;
-//         editor.update(cx, |editor, cx| {
-//             let expected_hints = vec!["0".to_string()];
-//             assert_eq!(
-//                 expected_hints,
-//                 cached_hint_labels(editor),
-//                 "Should get its first hints when opening the editor"
-//             );
-//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-//             assert_eq!(
-//                 editor.inlay_hint_cache().version,
-//                 edits_made,
-//                 "The editor update the cache version after every cache/view change"
-//             );
-//         });
-
-//         let progress_token = "test_progress_token";
-//         fake_server
-//             .request::<lsp::request::WorkDoneProgressCreate>(lsp::WorkDoneProgressCreateParams {
-//                 token: lsp::ProgressToken::String(progress_token.to_string()),
-//             })
-//             .await
-//             .expect("work done progress create request failed");
-//         cx.foreground().run_until_parked();
-//         fake_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
-//             token: lsp::ProgressToken::String(progress_token.to_string()),
-//             value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::Begin(
-//                 lsp::WorkDoneProgressBegin::default(),
-//             )),
-//         });
-//         cx.foreground().run_until_parked();
-
-//         editor.update(cx, |editor, cx| {
-//             let expected_hints = vec!["0".to_string()];
-//             assert_eq!(
-//                 expected_hints,
-//                 cached_hint_labels(editor),
-//                 "Should not update hints while the work task is running"
-//             );
-//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-//             assert_eq!(
-//                 editor.inlay_hint_cache().version,
-//                 edits_made,
-//                 "Should not update the cache while the work task is running"
-//             );
-//         });
-
-//         fake_server.notify::<lsp::notification::Progress>(lsp::ProgressParams {
-//             token: lsp::ProgressToken::String(progress_token.to_string()),
-//             value: lsp::ProgressParamsValue::WorkDone(lsp::WorkDoneProgress::End(
-//                 lsp::WorkDoneProgressEnd::default(),
-//             )),
-//         });
-//         cx.foreground().run_until_parked();
-
-//         edits_made += 1;
-//         editor.update(cx, |editor, cx| {
-//             let expected_hints = vec!["1".to_string()];
-//             assert_eq!(
-//                 expected_hints,
-//                 cached_hint_labels(editor),
-//                 "New hints should be queried after the work task is done"
-//             );
-//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-//             assert_eq!(
-//                 editor.inlay_hint_cache().version,
-//                 edits_made,
-//                 "Cache version should udpate once after the work task is done"
-//             );
-//         });
-//     }
-
-//     #[gpui::test]
-//     async fn test_no_hint_updates_for_unrelated_language_files(cx: &mut gpui::TestAppContext) {
-//         init_test(cx, |settings| {
-//             settings.defaults.inlay_hints = Some(InlayHintSettings {
-//                 enabled: true,
-//                 show_type_hints: true,
-//                 show_parameter_hints: true,
-//                 show_other_hints: true,
-//             })
-//         });
-
-//         let fs = FakeFs::new(cx.background());
-//         fs.insert_tree(
-//                     "/a",
-//                     json!({
-//                         "main.rs": "fn main() { a } // and some long comment to ensure inlays are not trimmed out",
-//                         "other.md": "Test md file with some text",
-//                     }),
-//                 )
-//                 .await;
-//         let project = Project::test(fs, ["/a".as_ref()], cx).await;
-//         let workspace = cx
-//             .add_window(|cx| Workspace::test_new(project.clone(), cx))
-//             .root(cx);
-//         let worktree_id = workspace.update(cx, |workspace, cx| {
-//             workspace.project().read_with(cx, |project, cx| {
-//                 project.worktrees(cx).next().unwrap().read(cx).id()
-//             })
-//         });
-
-//         let mut rs_fake_servers = None;
-//         let mut md_fake_servers = None;
-//         for (name, path_suffix) in [("Rust", "rs"), ("Markdown", "md")] {
-//             let mut language = Language::new(
-//                 LanguageConfig {
-//                     name: name.into(),
-//                     path_suffixes: vec![path_suffix.to_string()],
-//                     ..Default::default()
-//                 },
-//                 Some(tree_sitter_rust::language()),
-//             );
-//             let fake_servers = language
-//                 .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
-//                     name,
-//                     capabilities: lsp::ServerCapabilities {
-//                         inlay_hint_provider: Some(lsp::OneOf::Left(true)),
-//                         ..Default::default()
-//                     },
-//                     ..Default::default()
-//                 }))
-//                 .await;
-//             match name {
-//                 "Rust" => rs_fake_servers = Some(fake_servers),
-//                 "Markdown" => md_fake_servers = Some(fake_servers),
-//                 _ => unreachable!(),
-//             }
-//             project.update(cx, |project, _| {
-//                 project.languages().add(Arc::new(language));
-//             });
-//         }
-
-//         let _rs_buffer = project
-//             .update(cx, |project, cx| {
-//                 project.open_local_buffer("/a/main.rs", cx)
-//             })
-//             .await
-//             .unwrap();
-//         cx.foreground().run_until_parked();
-//         cx.foreground().start_waiting();
-//         let rs_fake_server = rs_fake_servers.unwrap().next().await.unwrap();
-//         let rs_editor = workspace
-//             .update(cx, |workspace, cx| {
-//                 workspace.open_path((worktree_id, "main.rs"), None, true, cx)
-//             })
-//             .await
-//             .unwrap()
-//             .downcast::<Editor>()
-//             .unwrap();
-//         let rs_lsp_request_count = Arc::new(AtomicU32::new(0));
-//         rs_fake_server
-//             .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
-//                 let task_lsp_request_count = Arc::clone(&rs_lsp_request_count);
-//                 async move {
-//                     assert_eq!(
-//                         params.text_document.uri,
-//                         lsp::Url::from_file_path("/a/main.rs").unwrap(),
-//                     );
-//                     let i = Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst);
-//                     Ok(Some(vec![lsp::InlayHint {
-//                         position: lsp::Position::new(0, i),
-//                         label: lsp::InlayHintLabel::String(i.to_string()),
-//                         kind: None,
-//                         text_edits: None,
-//                         tooltip: None,
-//                         padding_left: None,
-//                         padding_right: None,
-//                         data: None,
-//                     }]))
-//                 }
-//             })
-//             .next()
-//             .await;
-//         cx.foreground().run_until_parked();
-//         rs_editor.update(cx, |editor, cx| {
-//             let expected_hints = vec!["0".to_string()];
-//             assert_eq!(
-//                 expected_hints,
-//                 cached_hint_labels(editor),
-//                 "Should get its first hints when opening the editor"
-//             );
-//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-//             assert_eq!(
-//                 editor.inlay_hint_cache().version,
-//                 1,
-//                 "Rust editor update the cache version after every cache/view change"
-//             );
-//         });
-
-//         cx.foreground().run_until_parked();
-//         let _md_buffer = project
-//             .update(cx, |project, cx| {
-//                 project.open_local_buffer("/a/other.md", cx)
-//             })
-//             .await
-//             .unwrap();
-//         cx.foreground().run_until_parked();
-//         cx.foreground().start_waiting();
-//         let md_fake_server = md_fake_servers.unwrap().next().await.unwrap();
-//         let md_editor = workspace
-//             .update(cx, |workspace, cx| {
-//                 workspace.open_path((worktree_id, "other.md"), None, true, cx)
-//             })
-//             .await
-//             .unwrap()
-//             .downcast::<Editor>()
-//             .unwrap();
-//         let md_lsp_request_count = Arc::new(AtomicU32::new(0));
-//         md_fake_server
-//             .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
-//                 let task_lsp_request_count = Arc::clone(&md_lsp_request_count);
-//                 async move {
-//                     assert_eq!(
-//                         params.text_document.uri,
-//                         lsp::Url::from_file_path("/a/other.md").unwrap(),
-//                     );
-//                     let i = Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst);
-//                     Ok(Some(vec![lsp::InlayHint {
-//                         position: lsp::Position::new(0, i),
-//                         label: lsp::InlayHintLabel::String(i.to_string()),
-//                         kind: None,
-//                         text_edits: None,
-//                         tooltip: None,
-//                         padding_left: None,
-//                         padding_right: None,
-//                         data: None,
-//                     }]))
-//                 }
-//             })
-//             .next()
-//             .await;
-//         cx.foreground().run_until_parked();
-//         md_editor.update(cx, |editor, cx| {
-//             let expected_hints = vec!["0".to_string()];
-//             assert_eq!(
-//                 expected_hints,
-//                 cached_hint_labels(editor),
-//                 "Markdown editor should have a separate verison, repeating Rust editor rules"
-//             );
-//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-//             assert_eq!(editor.inlay_hint_cache().version, 1);
-//         });
-
-//         rs_editor.update(cx, |editor, cx| {
-//             editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
-//             editor.handle_input("some rs change", cx);
-//         });
-//         cx.foreground().run_until_parked();
-//         rs_editor.update(cx, |editor, cx| {
-//             let expected_hints = vec!["1".to_string()];
-//             assert_eq!(
-//                 expected_hints,
-//                 cached_hint_labels(editor),
-//                 "Rust inlay cache should change after the edit"
-//             );
-//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-//             assert_eq!(
-//                 editor.inlay_hint_cache().version,
-//                 2,
-//                 "Every time hint cache changes, cache version should be incremented"
-//             );
-//         });
-//         md_editor.update(cx, |editor, cx| {
-//             let expected_hints = vec!["0".to_string()];
-//             assert_eq!(
-//                 expected_hints,
-//                 cached_hint_labels(editor),
-//                 "Markdown editor should not be affected by Rust editor changes"
-//             );
-//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-//             assert_eq!(editor.inlay_hint_cache().version, 1);
-//         });
-
-//         md_editor.update(cx, |editor, cx| {
-//             editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
-//             editor.handle_input("some md change", cx);
-//         });
-//         cx.foreground().run_until_parked();
-//         md_editor.update(cx, |editor, cx| {
-//             let expected_hints = vec!["1".to_string()];
-//             assert_eq!(
-//                 expected_hints,
-//                 cached_hint_labels(editor),
-//                 "Rust editor should not be affected by Markdown editor changes"
-//             );
-//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-//             assert_eq!(editor.inlay_hint_cache().version, 2);
-//         });
-//         rs_editor.update(cx, |editor, cx| {
-//             let expected_hints = vec!["1".to_string()];
-//             assert_eq!(
-//                 expected_hints,
-//                 cached_hint_labels(editor),
-//                 "Markdown editor should also change independently"
-//             );
-//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-//             assert_eq!(editor.inlay_hint_cache().version, 2);
-//         });
-//     }
-
-//     #[gpui::test]
-//     async fn test_hint_setting_changes(cx: &mut gpui::TestAppContext) {
-//         let allowed_hint_kinds = HashSet::from_iter([None, Some(InlayHintKind::Type)]);
-//         init_test(cx, |settings| {
-//             settings.defaults.inlay_hints = Some(InlayHintSettings {
-//                 enabled: true,
-//                 show_type_hints: allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
-//                 show_parameter_hints: allowed_hint_kinds.contains(&Some(InlayHintKind::Parameter)),
-//                 show_other_hints: allowed_hint_kinds.contains(&None),
-//             })
-//         });
-
-//         let (file_with_hints, editor, fake_server) = prepare_test_objects(cx).await;
-//         let lsp_request_count = Arc::new(AtomicU32::new(0));
-//         let another_lsp_request_count = Arc::clone(&lsp_request_count);
-//         fake_server
-//             .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
-//                 let task_lsp_request_count = Arc::clone(&another_lsp_request_count);
-//                 async move {
-//                     Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst);
-//                     assert_eq!(
-//                         params.text_document.uri,
-//                         lsp::Url::from_file_path(file_with_hints).unwrap(),
-//                     );
-//                     Ok(Some(vec![
-//                         lsp::InlayHint {
-//                             position: lsp::Position::new(0, 1),
-//                             label: lsp::InlayHintLabel::String("type hint".to_string()),
-//                             kind: Some(lsp::InlayHintKind::TYPE),
-//                             text_edits: None,
-//                             tooltip: None,
-//                             padding_left: None,
-//                             padding_right: None,
-//                             data: None,
-//                         },
-//                         lsp::InlayHint {
-//                             position: lsp::Position::new(0, 2),
-//                             label: lsp::InlayHintLabel::String("parameter hint".to_string()),
-//                             kind: Some(lsp::InlayHintKind::PARAMETER),
-//                             text_edits: None,
-//                             tooltip: None,
-//                             padding_left: None,
-//                             padding_right: None,
-//                             data: None,
-//                         },
-//                         lsp::InlayHint {
-//                             position: lsp::Position::new(0, 3),
-//                             label: lsp::InlayHintLabel::String("other hint".to_string()),
-//                             kind: None,
-//                             text_edits: None,
-//                             tooltip: None,
-//                             padding_left: None,
-//                             padding_right: None,
-//                             data: None,
-//                         },
-//                     ]))
-//                 }
-//             })
-//             .next()
-//             .await;
-//         cx.foreground().run_until_parked();
-
-//         let mut edits_made = 1;
-//         editor.update(cx, |editor, cx| {
-//             assert_eq!(
-//                 lsp_request_count.load(Ordering::Relaxed),
-//                 1,
-//                 "Should query new hints once"
-//             );
-//             assert_eq!(
-//                 vec![
-//                     "other hint".to_string(),
-//                     "parameter hint".to_string(),
-//                     "type hint".to_string(),
-//                 ],
-//                 cached_hint_labels(editor),
-//                 "Should get its first hints when opening the editor"
-//             );
-//             assert_eq!(
-//                 vec!["other hint".to_string(), "type hint".to_string()],
-//                 visible_hint_labels(editor, cx)
-//             );
-//             let inlay_cache = editor.inlay_hint_cache();
-//             assert_eq!(
-//                 inlay_cache.allowed_hint_kinds, allowed_hint_kinds,
-//                 "Cache should use editor settings to get the allowed hint kinds"
-//             );
-//             assert_eq!(
-//                 inlay_cache.version, edits_made,
-//                 "The editor update the cache version after every cache/view change"
-//             );
-//         });
-
-//         fake_server
-//             .request::<lsp::request::InlayHintRefreshRequest>(())
-//             .await
-//             .expect("inlay refresh request failed");
-//         cx.foreground().run_until_parked();
-//         editor.update(cx, |editor, cx| {
-//             assert_eq!(
-//                 lsp_request_count.load(Ordering::Relaxed),
-//                 2,
-//                 "Should load new hints twice"
-//             );
-//             assert_eq!(
-//                 vec![
-//                     "other hint".to_string(),
-//                     "parameter hint".to_string(),
-//                     "type hint".to_string(),
-//                 ],
-//                 cached_hint_labels(editor),
-//                 "Cached hints should not change due to allowed hint kinds settings update"
-//             );
-//             assert_eq!(
-//                 vec!["other hint".to_string(), "type hint".to_string()],
-//                 visible_hint_labels(editor, cx)
-//             );
-//             assert_eq!(
-//                 editor.inlay_hint_cache().version,
-//                 edits_made,
-//                 "Should not update cache version due to new loaded hints being the same"
-//             );
-//         });
-
-//         for (new_allowed_hint_kinds, expected_visible_hints) in [
-//             (HashSet::from_iter([None]), vec!["other hint".to_string()]),
-//             (
-//                 HashSet::from_iter([Some(InlayHintKind::Type)]),
-//                 vec!["type hint".to_string()],
-//             ),
-//             (
-//                 HashSet::from_iter([Some(InlayHintKind::Parameter)]),
-//                 vec!["parameter hint".to_string()],
-//             ),
-//             (
-//                 HashSet::from_iter([None, Some(InlayHintKind::Type)]),
-//                 vec!["other hint".to_string(), "type hint".to_string()],
-//             ),
-//             (
-//                 HashSet::from_iter([None, Some(InlayHintKind::Parameter)]),
-//                 vec!["other hint".to_string(), "parameter hint".to_string()],
-//             ),
-//             (
-//                 HashSet::from_iter([Some(InlayHintKind::Type), Some(InlayHintKind::Parameter)]),
-//                 vec!["parameter hint".to_string(), "type hint".to_string()],
-//             ),
-//             (
-//                 HashSet::from_iter([
-//                     None,
-//                     Some(InlayHintKind::Type),
-//                     Some(InlayHintKind::Parameter),
-//                 ]),
-//                 vec![
-//                     "other hint".to_string(),
-//                     "parameter hint".to_string(),
-//                     "type hint".to_string(),
-//                 ],
-//             ),
-//         ] {
-//             edits_made += 1;
-//             update_test_language_settings(cx, |settings| {
-//                 settings.defaults.inlay_hints = Some(InlayHintSettings {
-//                     enabled: true,
-//                     show_type_hints: new_allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
-//                     show_parameter_hints: new_allowed_hint_kinds
-//                         .contains(&Some(InlayHintKind::Parameter)),
-//                     show_other_hints: new_allowed_hint_kinds.contains(&None),
-//                 })
-//             });
-//             cx.foreground().run_until_parked();
-//             editor.update(cx, |editor, cx| {
-//                 assert_eq!(
-//                     lsp_request_count.load(Ordering::Relaxed),
-//                     2,
-//                     "Should not load new hints on allowed hint kinds change for hint kinds {new_allowed_hint_kinds:?}"
-//                 );
-//                 assert_eq!(
-//                     vec![
-//                         "other hint".to_string(),
-//                         "parameter hint".to_string(),
-//                         "type hint".to_string(),
-//                     ],
-//                     cached_hint_labels(editor),
-//                     "Should get its cached hints unchanged after the settings change for hint kinds {new_allowed_hint_kinds:?}"
-//                 );
-//                 assert_eq!(
-//                     expected_visible_hints,
-//                     visible_hint_labels(editor, cx),
-//                     "Should get its visible hints filtered after the settings change for hint kinds {new_allowed_hint_kinds:?}"
-//                 );
-//                 let inlay_cache = editor.inlay_hint_cache();
-//                 assert_eq!(
-//                     inlay_cache.allowed_hint_kinds, new_allowed_hint_kinds,
-//                     "Cache should use editor settings to get the allowed hint kinds for hint kinds {new_allowed_hint_kinds:?}"
-//                 );
-//                 assert_eq!(
-//                     inlay_cache.version, edits_made,
-//                     "The editor should update the cache version after every cache/view change for hint kinds {new_allowed_hint_kinds:?} due to visible hints change"
-//                 );
-//             });
-//         }
-
-//         edits_made += 1;
-//         let another_allowed_hint_kinds = HashSet::from_iter([Some(InlayHintKind::Type)]);
-//         update_test_language_settings(cx, |settings| {
-//             settings.defaults.inlay_hints = Some(InlayHintSettings {
-//                 enabled: false,
-//                 show_type_hints: another_allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
-//                 show_parameter_hints: another_allowed_hint_kinds
-//                     .contains(&Some(InlayHintKind::Parameter)),
-//                 show_other_hints: another_allowed_hint_kinds.contains(&None),
-//             })
-//         });
-//         cx.foreground().run_until_parked();
-//         editor.update(cx, |editor, cx| {
-//             assert_eq!(
-//                 lsp_request_count.load(Ordering::Relaxed),
-//                 2,
-//                 "Should not load new hints when hints got disabled"
-//             );
-//             assert!(
-//                 cached_hint_labels(editor).is_empty(),
-//                 "Should clear the cache when hints got disabled"
-//             );
-//             assert!(
-//                 visible_hint_labels(editor, cx).is_empty(),
-//                 "Should clear visible hints when hints got disabled"
-//             );
-//             let inlay_cache = editor.inlay_hint_cache();
-//             assert_eq!(
-//                 inlay_cache.allowed_hint_kinds, another_allowed_hint_kinds,
-//                 "Should update its allowed hint kinds even when hints got disabled"
-//             );
-//             assert_eq!(
-//                 inlay_cache.version, edits_made,
-//                 "The editor should update the cache version after hints got disabled"
-//             );
-//         });
-
-//         fake_server
-//             .request::<lsp::request::InlayHintRefreshRequest>(())
-//             .await
-//             .expect("inlay refresh request failed");
-//         cx.foreground().run_until_parked();
-//         editor.update(cx, |editor, cx| {
-//             assert_eq!(
-//                 lsp_request_count.load(Ordering::Relaxed),
-//                 2,
-//                 "Should not load new hints when they got disabled"
-//             );
-//             assert!(cached_hint_labels(editor).is_empty());
-//             assert!(visible_hint_labels(editor, cx).is_empty());
-//             assert_eq!(
-//                 editor.inlay_hint_cache().version, edits_made,
-//                 "The editor should not update the cache version after /refresh query without updates"
-//             );
-//         });
-
-//         let final_allowed_hint_kinds = HashSet::from_iter([Some(InlayHintKind::Parameter)]);
-//         edits_made += 1;
-//         update_test_language_settings(cx, |settings| {
-//             settings.defaults.inlay_hints = Some(InlayHintSettings {
-//                 enabled: true,
-//                 show_type_hints: final_allowed_hint_kinds.contains(&Some(InlayHintKind::Type)),
-//                 show_parameter_hints: final_allowed_hint_kinds
-//                     .contains(&Some(InlayHintKind::Parameter)),
-//                 show_other_hints: final_allowed_hint_kinds.contains(&None),
-//             })
-//         });
-//         cx.foreground().run_until_parked();
-//         editor.update(cx, |editor, cx| {
-//             assert_eq!(
-//                 lsp_request_count.load(Ordering::Relaxed),
-//                 3,
-//                 "Should query for new hints when they got reenabled"
-//             );
-//             assert_eq!(
-//                 vec![
-//                     "other hint".to_string(),
-//                     "parameter hint".to_string(),
-//                     "type hint".to_string(),
-//                 ],
-//                 cached_hint_labels(editor),
-//                 "Should get its cached hints fully repopulated after the hints got reenabled"
-//             );
-//             assert_eq!(
-//                 vec!["parameter hint".to_string()],
-//                 visible_hint_labels(editor, cx),
-//                 "Should get its visible hints repopulated and filtered after the h"
-//             );
-//             let inlay_cache = editor.inlay_hint_cache();
-//             assert_eq!(
-//                 inlay_cache.allowed_hint_kinds, final_allowed_hint_kinds,
-//                 "Cache should update editor settings when hints got reenabled"
-//             );
-//             assert_eq!(
-//                 inlay_cache.version, edits_made,
-//                 "Cache should update its version after hints got reenabled"
-//             );
-//         });
-
-//         fake_server
-//             .request::<lsp::request::InlayHintRefreshRequest>(())
-//             .await
-//             .expect("inlay refresh request failed");
-//         cx.foreground().run_until_parked();
-//         editor.update(cx, |editor, cx| {
-//             assert_eq!(
-//                 lsp_request_count.load(Ordering::Relaxed),
-//                 4,
-//                 "Should query for new hints again"
-//             );
-//             assert_eq!(
-//                 vec![
-//                     "other hint".to_string(),
-//                     "parameter hint".to_string(),
-//                     "type hint".to_string(),
-//                 ],
-//                 cached_hint_labels(editor),
-//             );
-//             assert_eq!(
-//                 vec!["parameter hint".to_string()],
-//                 visible_hint_labels(editor, cx),
-//             );
-//             assert_eq!(editor.inlay_hint_cache().version, edits_made);
-//         });
-//     }
-
-//     #[gpui::test]
-//     async fn test_hint_request_cancellation(cx: &mut gpui::TestAppContext) {
-//         init_test(cx, |settings| {
-//             settings.defaults.inlay_hints = Some(InlayHintSettings {
-//                 enabled: true,
-//                 show_type_hints: true,
-//                 show_parameter_hints: true,
-//                 show_other_hints: true,
-//             })
-//         });
-
-//         let (file_with_hints, editor, fake_server) = prepare_test_objects(cx).await;
-//         let fake_server = Arc::new(fake_server);
-//         let lsp_request_count = Arc::new(AtomicU32::new(0));
-//         let another_lsp_request_count = Arc::clone(&lsp_request_count);
-//         fake_server
-//             .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
-//                 let task_lsp_request_count = Arc::clone(&another_lsp_request_count);
-//                 async move {
-//                     let i = Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst) + 1;
-//                     assert_eq!(
-//                         params.text_document.uri,
-//                         lsp::Url::from_file_path(file_with_hints).unwrap(),
-//                     );
-//                     Ok(Some(vec![lsp::InlayHint {
-//                         position: lsp::Position::new(0, i),
-//                         label: lsp::InlayHintLabel::String(i.to_string()),
-//                         kind: None,
-//                         text_edits: None,
-//                         tooltip: None,
-//                         padding_left: None,
-//                         padding_right: None,
-//                         data: None,
-//                     }]))
-//                 }
-//             })
-//             .next()
-//             .await;
-
-//         let mut expected_changes = Vec::new();
-//         for change_after_opening in [
-//             "initial change #1",
-//             "initial change #2",
-//             "initial change #3",
-//         ] {
-//             editor.update(cx, |editor, cx| {
-//                 editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
-//                 editor.handle_input(change_after_opening, cx);
-//             });
-//             expected_changes.push(change_after_opening);
-//         }
-
-//         cx.foreground().run_until_parked();
-
-//         editor.update(cx, |editor, cx| {
-//             let current_text = editor.text(cx);
-//             for change in &expected_changes {
-//                 assert!(
-//                     current_text.contains(change),
-//                     "Should apply all changes made"
-//                 );
-//             }
-//             assert_eq!(
-//                 lsp_request_count.load(Ordering::Relaxed),
-//                 2,
-//                 "Should query new hints twice: for editor init and for the last edit that interrupted all others"
-//             );
-//             let expected_hints = vec!["2".to_string()];
-//             assert_eq!(
-//                 expected_hints,
-//                 cached_hint_labels(editor),
-//                 "Should get hints from the last edit landed only"
-//             );
-//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-//             assert_eq!(
-//                 editor.inlay_hint_cache().version, 1,
-//                 "Only one update should be registered in the cache after all cancellations"
-//             );
-//         });
-
-//         let mut edits = Vec::new();
-//         for async_later_change in [
-//             "another change #1",
-//             "another change #2",
-//             "another change #3",
-//         ] {
-//             expected_changes.push(async_later_change);
-//             let task_editor = editor.clone();
-//             let mut task_cx = cx.clone();
-//             edits.push(cx.foreground().spawn(async move {
-//                 task_editor.update(&mut task_cx, |editor, cx| {
-//                     editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
-//                     editor.handle_input(async_later_change, cx);
-//                 });
-//             }));
-//         }
-//         let _ = future::join_all(edits).await;
-//         cx.foreground().run_until_parked();
-
-//         editor.update(cx, |editor, cx| {
-//             let current_text = editor.text(cx);
-//             for change in &expected_changes {
-//                 assert!(
-//                     current_text.contains(change),
-//                     "Should apply all changes made"
-//                 );
-//             }
-//             assert_eq!(
-//                 lsp_request_count.load(Ordering::SeqCst),
-//                 3,
-//                 "Should query new hints one more time, for the last edit only"
-//             );
-//             let expected_hints = vec!["3".to_string()];
-//             assert_eq!(
-//                 expected_hints,
-//                 cached_hint_labels(editor),
-//                 "Should get hints from the last edit landed only"
-//             );
-//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-//             assert_eq!(
-//                 editor.inlay_hint_cache().version,
-//                 2,
-//                 "Should update the cache version once more, for the new change"
-//             );
-//         });
-//     }
-
-//     #[gpui::test(iterations = 10)]
-//     async fn test_large_buffer_inlay_requests_split(cx: &mut gpui::TestAppContext) {
-//         init_test(cx, |settings| {
-//             settings.defaults.inlay_hints = Some(InlayHintSettings {
-//                 enabled: true,
-//                 show_type_hints: true,
-//                 show_parameter_hints: true,
-//                 show_other_hints: true,
-//             })
-//         });
-
-//         let mut language = Language::new(
-//             LanguageConfig {
-//                 name: "Rust".into(),
-//                 path_suffixes: vec!["rs".to_string()],
-//                 ..Default::default()
-//             },
-//             Some(tree_sitter_rust::language()),
-//         );
-//         let mut fake_servers = language
-//             .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
-//                 capabilities: lsp::ServerCapabilities {
-//                     inlay_hint_provider: Some(lsp::OneOf::Left(true)),
-//                     ..Default::default()
-//                 },
-//                 ..Default::default()
-//             }))
-//             .await;
-//         let fs = FakeFs::new(cx.background());
-//         fs.insert_tree(
-//             "/a",
-//             json!({
-//                 "main.rs": format!("fn main() {{\n{}\n}}", "let i = 5;\n".repeat(500)),
-//                 "other.rs": "// Test file",
-//             }),
-//         )
-//         .await;
-//         let project = Project::test(fs, ["/a".as_ref()], cx).await;
-//         project.update(cx, |project, _| project.languages().add(Arc::new(language)));
-//         let workspace = cx
-//             .add_window(|cx| Workspace::test_new(project.clone(), cx))
-//             .root(cx);
-//         let worktree_id = workspace.update(cx, |workspace, cx| {
-//             workspace.project().read_with(cx, |project, cx| {
-//                 project.worktrees(cx).next().unwrap().read(cx).id()
-//             })
-//         });
-
-//         let _buffer = project
-//             .update(cx, |project, cx| {
-//                 project.open_local_buffer("/a/main.rs", cx)
-//             })
-//             .await
-//             .unwrap();
-//         cx.foreground().run_until_parked();
-//         cx.foreground().start_waiting();
-//         let fake_server = fake_servers.next().await.unwrap();
-//         let editor = workspace
-//             .update(cx, |workspace, cx| {
-//                 workspace.open_path((worktree_id, "main.rs"), None, true, cx)
-//             })
-//             .await
-//             .unwrap()
-//             .downcast::<Editor>()
-//             .unwrap();
-//         let lsp_request_ranges = Arc::new(Mutex::new(Vec::new()));
-//         let lsp_request_count = Arc::new(AtomicUsize::new(0));
-//         let closure_lsp_request_ranges = Arc::clone(&lsp_request_ranges);
-//         let closure_lsp_request_count = Arc::clone(&lsp_request_count);
-//         fake_server
-//             .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
-//                 let task_lsp_request_ranges = Arc::clone(&closure_lsp_request_ranges);
-//                 let task_lsp_request_count = Arc::clone(&closure_lsp_request_count);
-//                 async move {
-//                     assert_eq!(
-//                         params.text_document.uri,
-//                         lsp::Url::from_file_path("/a/main.rs").unwrap(),
-//                     );
-
-//                     task_lsp_request_ranges.lock().push(params.range);
-//                     let i = Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::Release) + 1;
-//                     Ok(Some(vec![lsp::InlayHint {
-//                         position: params.range.end,
-//                         label: lsp::InlayHintLabel::String(i.to_string()),
-//                         kind: None,
-//                         text_edits: None,
-//                         tooltip: None,
-//                         padding_left: None,
-//                         padding_right: None,
-//                         data: None,
-//                     }]))
-//                 }
-//             })
-//             .next()
-//             .await;
-//         fn editor_visible_range(
-//             editor: &ViewHandle<Editor>,
-//             cx: &mut gpui::TestAppContext,
-//         ) -> Range<Point> {
-//             let ranges = editor.update(cx, |editor, cx| editor.excerpt_visible_offsets(None, cx));
-//             assert_eq!(
-//                 ranges.len(),
-//                 1,
-//                 "Single buffer should produce a single excerpt with visible range"
-//             );
-//             let (_, (excerpt_buffer, _, excerpt_visible_range)) =
-//                 ranges.into_iter().next().unwrap();
-//             excerpt_buffer.update(cx, |buffer, _| {
-//                 let snapshot = buffer.snapshot();
-//                 let start = buffer
-//                     .anchor_before(excerpt_visible_range.start)
-//                     .to_point(&snapshot);
-//                 let end = buffer
-//                     .anchor_after(excerpt_visible_range.end)
-//                     .to_point(&snapshot);
-//                 start..end
-//             })
-//         }
-
-//         // in large buffers, requests are made for more than visible range of a buffer.
-//         // invisible parts are queried later, to avoid excessive requests on quick typing.
-//         // wait the timeout needed to get all requests.
-//         cx.foreground().advance_clock(Duration::from_millis(
-//             INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100,
-//         ));
-//         cx.foreground().run_until_parked();
-//         let initial_visible_range = editor_visible_range(&editor, cx);
-//         let lsp_initial_visible_range = lsp::Range::new(
-//             lsp::Position::new(
-//                 initial_visible_range.start.row,
-//                 initial_visible_range.start.column,
-//             ),
-//             lsp::Position::new(
-//                 initial_visible_range.end.row,
-//                 initial_visible_range.end.column,
-//             ),
-//         );
-//         let expected_initial_query_range_end =
-//             lsp::Position::new(initial_visible_range.end.row * 2, 2);
-//         let mut expected_invisible_query_start = lsp_initial_visible_range.end;
-//         expected_invisible_query_start.character += 1;
-//         editor.update(cx, |editor, cx| {
-//             let ranges = lsp_request_ranges.lock().drain(..).collect::<Vec<_>>();
-//             assert_eq!(ranges.len(), 2,
-//                 "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:?}");
-//             let visible_query_range = &ranges[0];
-//             assert_eq!(visible_query_range.start, lsp_initial_visible_range.start);
-//             assert_eq!(visible_query_range.end, lsp_initial_visible_range.end);
-//             let invisible_query_range = &ranges[1];
-
-//             assert_eq!(invisible_query_range.start, expected_invisible_query_start, "Should initially query visible edge of the document");
-//             assert_eq!(invisible_query_range.end, expected_initial_query_range_end, "Should initially query visible edge of the document");
-
-//             let requests_count = lsp_request_count.load(Ordering::Acquire);
-//             assert_eq!(requests_count, 2, "Visible + invisible request");
-//             let expected_hints = vec!["1".to_string(), "2".to_string()];
-//             assert_eq!(
-//                 expected_hints,
-//                 cached_hint_labels(editor),
-//                 "Should have hints from both LSP requests made for a big file"
-//             );
-//             assert_eq!(expected_hints, visible_hint_labels(editor, cx), "Should display only hints from the visible range");
-//             assert_eq!(
-//                 editor.inlay_hint_cache().version, requests_count,
-//                 "LSP queries should've bumped the cache version"
-//             );
-//         });
-
-//         editor.update(cx, |editor, cx| {
-//             editor.scroll_screen(&ScrollAmount::Page(1.0), cx);
-//             editor.scroll_screen(&ScrollAmount::Page(1.0), cx);
-//         });
-//         cx.foreground().advance_clock(Duration::from_millis(
-//             INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100,
-//         ));
-//         cx.foreground().run_until_parked();
-//         let visible_range_after_scrolls = editor_visible_range(&editor, cx);
-//         let visible_line_count =
-//             editor.update(cx, |editor, _| editor.visible_line_count().unwrap());
-//         let selection_in_cached_range = editor.update(cx, |editor, cx| {
-//             let ranges = lsp_request_ranges
-//                 .lock()
-//                 .drain(..)
-//                 .sorted_by_key(|r| r.start)
-//                 .collect::<Vec<_>>();
-//             assert_eq!(
-//                 ranges.len(),
-//                 2,
-//                 "Should query 2 ranges after both scrolls, but got: {ranges:?}"
-//             );
-//             let first_scroll = &ranges[0];
-//             let second_scroll = &ranges[1];
-//             assert_eq!(
-//                 first_scroll.end, second_scroll.start,
-//                 "Should query 2 adjacent ranges after the scrolls, but got: {ranges:?}"
-//             );
-//             assert_eq!(
-//                 first_scroll.start, expected_initial_query_range_end,
-//                 "First scroll should start the query right after the end of the original scroll",
-//             );
-//             assert_eq!(
-//                 second_scroll.end,
-//                 lsp::Position::new(
-//                     visible_range_after_scrolls.end.row
-//                         + visible_line_count.ceil() as u32,
-//                     1,
-//                 ),
-//                 "Second scroll should query one more screen down after the end of the visible range"
-//             );
-
-//             let lsp_requests = lsp_request_count.load(Ordering::Acquire);
-//             assert_eq!(lsp_requests, 4, "Should query for hints after every scroll");
-//             let expected_hints = vec![
-//                 "1".to_string(),
-//                 "2".to_string(),
-//                 "3".to_string(),
-//                 "4".to_string(),
-//             ];
-//             assert_eq!(
-//                 expected_hints,
-//                 cached_hint_labels(editor),
-//                 "Should have hints from the new LSP response after the edit"
-//             );
-//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-//             assert_eq!(
-//                 editor.inlay_hint_cache().version,
-//                 lsp_requests,
-//                 "Should update the cache for every LSP response with hints added"
-//             );
-
-//             let mut selection_in_cached_range = visible_range_after_scrolls.end;
-//             selection_in_cached_range.row -= visible_line_count.ceil() as u32;
-//             selection_in_cached_range
-//         });
-
-//         editor.update(cx, |editor, cx| {
-//             editor.change_selections(Some(Autoscroll::center()), cx, |s| {
-//                 s.select_ranges([selection_in_cached_range..selection_in_cached_range])
-//             });
-//         });
-//         cx.foreground().advance_clock(Duration::from_millis(
-//             INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100,
-//         ));
-//         cx.foreground().run_until_parked();
-//         editor.update(cx, |_, _| {
-//             let ranges = lsp_request_ranges
-//                 .lock()
-//                 .drain(..)
-//                 .sorted_by_key(|r| r.start)
-//                 .collect::<Vec<_>>();
-//             assert!(ranges.is_empty(), "No new ranges or LSP queries should be made after returning to the selection with cached hints");
-//             assert_eq!(lsp_request_count.load(Ordering::Acquire), 4);
-//         });
-
-//         editor.update(cx, |editor, cx| {
-//             editor.handle_input("++++more text++++", cx);
-//         });
-//         cx.foreground().advance_clock(Duration::from_millis(
-//             INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100,
-//         ));
-//         cx.foreground().run_until_parked();
-//         editor.update(cx, |editor, cx| {
-//             let mut ranges = lsp_request_ranges.lock().drain(..).collect::<Vec<_>>();
-//             ranges.sort_by_key(|r| r.start);
-
-//             assert_eq!(ranges.len(), 3,
-//                 "On edit, should scroll to selection and query a range around it: visible + same range above and below. Instead, got query ranges {ranges:?}");
-//             let above_query_range = &ranges[0];
-//             let visible_query_range = &ranges[1];
-//             let below_query_range = &ranges[2];
-//             assert!(above_query_range.end.character < visible_query_range.start.character || above_query_range.end.line + 1 == visible_query_range.start.line,
-//                 "Above range {above_query_range:?} should be before visible range {visible_query_range:?}");
-//             assert!(visible_query_range.end.character < below_query_range.start.character || visible_query_range.end.line  + 1 == below_query_range.start.line,
-//                 "Visible range {visible_query_range:?} should be before below range {below_query_range:?}");
-//             assert!(above_query_range.start.line < selection_in_cached_range.row,
-//                 "Hints should be queried with the selected range after the query range start");
-//             assert!(below_query_range.end.line > selection_in_cached_range.row,
-//                 "Hints should be queried with the selected range before the query range end");
-//             assert!(above_query_range.start.line <= selection_in_cached_range.row - (visible_line_count * 3.0 / 2.0) as u32,
-//                 "Hints query range should contain one more screen before");
-//             assert!(below_query_range.end.line >= selection_in_cached_range.row + (visible_line_count * 3.0 / 2.0) as u32,
-//                 "Hints query range should contain one more screen after");
-
-//             let lsp_requests = lsp_request_count.load(Ordering::Acquire);
-//             assert_eq!(lsp_requests, 7, "There should be a visible range and two ranges above and below it queried");
-//             let expected_hints = vec!["5".to_string(), "6".to_string(), "7".to_string()];
-//             assert_eq!(expected_hints, cached_hint_labels(editor),
-//                 "Should have hints from the new LSP response after the edit");
-//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-//             assert_eq!(editor.inlay_hint_cache().version, lsp_requests, "Should update the cache for every LSP response with hints added");
-//         });
-//     }
-
-//     #[gpui::test(iterations = 10)]
-//     async fn test_multiple_excerpts_large_multibuffer(
-//         deterministic: Arc<Deterministic>,
-//         cx: &mut gpui::TestAppContext,
-//     ) {
-//         init_test(cx, |settings| {
-//             settings.defaults.inlay_hints = Some(InlayHintSettings {
-//                 enabled: true,
-//                 show_type_hints: true,
-//                 show_parameter_hints: true,
-//                 show_other_hints: true,
-//             })
-//         });
-
-//         let mut language = Language::new(
-//             LanguageConfig {
-//                 name: "Rust".into(),
-//                 path_suffixes: vec!["rs".to_string()],
-//                 ..Default::default()
-//             },
-//             Some(tree_sitter_rust::language()),
-//         );
-//         let mut fake_servers = language
-//             .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
-//                 capabilities: lsp::ServerCapabilities {
-//                     inlay_hint_provider: Some(lsp::OneOf::Left(true)),
-//                     ..Default::default()
-//                 },
-//                 ..Default::default()
-//             }))
-//             .await;
-//         let language = Arc::new(language);
-//         let fs = FakeFs::new(cx.background());
-//         fs.insert_tree(
-//             "/a",
-//             json!({
-//                 "main.rs": format!("fn main() {{\n{}\n}}", (0..501).map(|i| format!("let i = {i};\n")).collect::<Vec<_>>().join("")),
-//                 "other.rs": format!("fn main() {{\n{}\n}}", (0..501).map(|j| format!("let j = {j};\n")).collect::<Vec<_>>().join("")),
-//             }),
-//         )
-//         .await;
-//         let project = Project::test(fs, ["/a".as_ref()], cx).await;
-//         project.update(cx, |project, _| {
-//             project.languages().add(Arc::clone(&language))
-//         });
-//         let workspace = cx
-//             .add_window(|cx| Workspace::test_new(project.clone(), cx))
-//             .root(cx);
-//         let worktree_id = workspace.update(cx, |workspace, cx| {
-//             workspace.project().read_with(cx, |project, cx| {
-//                 project.worktrees(cx).next().unwrap().read(cx).id()
-//             })
-//         });
-
-//         let buffer_1 = project
-//             .update(cx, |project, cx| {
-//                 project.open_buffer((worktree_id, "main.rs"), cx)
-//             })
-//             .await
-//             .unwrap();
-//         let buffer_2 = project
-//             .update(cx, |project, cx| {
-//                 project.open_buffer((worktree_id, "other.rs"), cx)
-//             })
-//             .await
-//             .unwrap();
-//         let multibuffer = cx.add_model(|cx| {
-//             let mut multibuffer = MultiBuffer::new(0);
-//             multibuffer.push_excerpts(
-//                 buffer_1.clone(),
-//                 [
-//                     ExcerptRange {
-//                         context: Point::new(0, 0)..Point::new(2, 0),
-//                         primary: None,
-//                     },
-//                     ExcerptRange {
-//                         context: Point::new(4, 0)..Point::new(11, 0),
-//                         primary: None,
-//                     },
-//                     ExcerptRange {
-//                         context: Point::new(22, 0)..Point::new(33, 0),
-//                         primary: None,
-//                     },
-//                     ExcerptRange {
-//                         context: Point::new(44, 0)..Point::new(55, 0),
-//                         primary: None,
-//                     },
-//                     ExcerptRange {
-//                         context: Point::new(56, 0)..Point::new(66, 0),
-//                         primary: None,
-//                     },
-//                     ExcerptRange {
-//                         context: Point::new(67, 0)..Point::new(77, 0),
-//                         primary: None,
-//                     },
-//                 ],
-//                 cx,
-//             );
-//             multibuffer.push_excerpts(
-//                 buffer_2.clone(),
-//                 [
-//                     ExcerptRange {
-//                         context: Point::new(0, 1)..Point::new(2, 1),
-//                         primary: None,
-//                     },
-//                     ExcerptRange {
-//                         context: Point::new(4, 1)..Point::new(11, 1),
-//                         primary: None,
-//                     },
-//                     ExcerptRange {
-//                         context: Point::new(22, 1)..Point::new(33, 1),
-//                         primary: None,
-//                     },
-//                     ExcerptRange {
-//                         context: Point::new(44, 1)..Point::new(55, 1),
-//                         primary: None,
-//                     },
-//                     ExcerptRange {
-//                         context: Point::new(56, 1)..Point::new(66, 1),
-//                         primary: None,
-//                     },
-//                     ExcerptRange {
-//                         context: Point::new(67, 1)..Point::new(77, 1),
-//                         primary: None,
-//                     },
-//                 ],
-//                 cx,
-//             );
-//             multibuffer
-//         });
-
-//         deterministic.run_until_parked();
-//         cx.foreground().run_until_parked();
-//         let editor = cx
-//             .add_window(|cx| Editor::for_multibuffer(multibuffer, Some(project.clone()), cx))
-//             .root(cx);
-//         let editor_edited = Arc::new(AtomicBool::new(false));
-//         let fake_server = fake_servers.next().await.unwrap();
-//         let closure_editor_edited = Arc::clone(&editor_edited);
-//         fake_server
-//             .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
-//                 let task_editor_edited = Arc::clone(&closure_editor_edited);
-//                 async move {
-//                     let hint_text = if params.text_document.uri
-//                         == lsp::Url::from_file_path("/a/main.rs").unwrap()
-//                     {
-//                         "main hint"
-//                     } else if params.text_document.uri
-//                         == lsp::Url::from_file_path("/a/other.rs").unwrap()
-//                     {
-//                         "other hint"
-//                     } else {
-//                         panic!("unexpected uri: {:?}", params.text_document.uri);
-//                     };
-
-//                     // one hint per excerpt
-//                     let positions = [
-//                         lsp::Position::new(0, 2),
-//                         lsp::Position::new(4, 2),
-//                         lsp::Position::new(22, 2),
-//                         lsp::Position::new(44, 2),
-//                         lsp::Position::new(56, 2),
-//                         lsp::Position::new(67, 2),
-//                     ];
-//                     let out_of_range_hint = lsp::InlayHint {
-//                         position: lsp::Position::new(
-//                             params.range.start.line + 99,
-//                             params.range.start.character + 99,
-//                         ),
-//                         label: lsp::InlayHintLabel::String(
-//                             "out of excerpt range, should be ignored".to_string(),
-//                         ),
-//                         kind: None,
-//                         text_edits: None,
-//                         tooltip: None,
-//                         padding_left: None,
-//                         padding_right: None,
-//                         data: None,
-//                     };
-
-//                     let edited = task_editor_edited.load(Ordering::Acquire);
-//                     Ok(Some(
-//                         std::iter::once(out_of_range_hint)
-//                             .chain(positions.into_iter().enumerate().map(|(i, position)| {
-//                                 lsp::InlayHint {
-//                                     position,
-//                                     label: lsp::InlayHintLabel::String(format!(
-//                                         "{hint_text}{} #{i}",
-//                                         if edited { "(edited)" } else { "" },
-//                                     )),
-//                                     kind: None,
-//                                     text_edits: None,
-//                                     tooltip: None,
-//                                     padding_left: None,
-//                                     padding_right: None,
-//                                     data: None,
-//                                 }
-//                             }))
-//                             .collect(),
-//                     ))
-//                 }
-//             })
-//             .next()
-//             .await;
-//         cx.foreground().run_until_parked();
-
-//         editor.update(cx, |editor, cx| {
-//             let expected_hints = vec![
-//                 "main hint #0".to_string(),
-//                 "main hint #1".to_string(),
-//                 "main hint #2".to_string(),
-//                 "main hint #3".to_string(),
-//             ];
-//             assert_eq!(
-//                 expected_hints,
-//                 cached_hint_labels(editor),
-//                 "When scroll is at the edge of a multibuffer, its visible excerpts only should be queried for inlay hints"
-//             );
-//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-//             assert_eq!(editor.inlay_hint_cache().version, expected_hints.len(), "Every visible excerpt hints should bump the verison");
-//         });
-
-//         editor.update(cx, |editor, cx| {
-//             editor.change_selections(Some(Autoscroll::Next), cx, |s| {
-//                 s.select_ranges([Point::new(4, 0)..Point::new(4, 0)])
-//             });
-//             editor.change_selections(Some(Autoscroll::Next), cx, |s| {
-//                 s.select_ranges([Point::new(22, 0)..Point::new(22, 0)])
-//             });
-//             editor.change_selections(Some(Autoscroll::Next), cx, |s| {
-//                 s.select_ranges([Point::new(50, 0)..Point::new(50, 0)])
-//             });
-//         });
-//         cx.foreground().run_until_parked();
-//         editor.update(cx, |editor, cx| {
-//             let expected_hints = vec![
-//                 "main hint #0".to_string(),
-//                 "main hint #1".to_string(),
-//                 "main hint #2".to_string(),
-//                 "main hint #3".to_string(),
-//                 "main hint #4".to_string(),
-//                 "main hint #5".to_string(),
-//                 "other hint #0".to_string(),
-//                 "other hint #1".to_string(),
-//                 "other hint #2".to_string(),
-//             ];
-//             assert_eq!(expected_hints, cached_hint_labels(editor),
-//                 "With more scrolls of the multibuffer, more hints should be added into the cache and nothing invalidated without edits");
-//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-//             assert_eq!(editor.inlay_hint_cache().version, expected_hints.len(),
-//                 "Due to every excerpt having one hint, we update cache per new excerpt scrolled");
-//         });
-
-//         editor.update(cx, |editor, cx| {
-//             editor.change_selections(Some(Autoscroll::Next), cx, |s| {
-//                 s.select_ranges([Point::new(100, 0)..Point::new(100, 0)])
-//             });
-//         });
-//         cx.foreground().advance_clock(Duration::from_millis(
-//             INVISIBLE_RANGES_HINTS_REQUEST_DELAY_MILLIS + 100,
-//         ));
-//         cx.foreground().run_until_parked();
-//         let last_scroll_update_version = editor.update(cx, |editor, cx| {
-//             let expected_hints = vec![
-//                 "main hint #0".to_string(),
-//                 "main hint #1".to_string(),
-//                 "main hint #2".to_string(),
-//                 "main hint #3".to_string(),
-//                 "main hint #4".to_string(),
-//                 "main hint #5".to_string(),
-//                 "other hint #0".to_string(),
-//                 "other hint #1".to_string(),
-//                 "other hint #2".to_string(),
-//                 "other hint #3".to_string(),
-//                 "other hint #4".to_string(),
-//                 "other hint #5".to_string(),
-//             ];
-//             assert_eq!(expected_hints, cached_hint_labels(editor),
-//                 "After multibuffer was scrolled to the end, all hints for all excerpts should be fetched");
-//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-//             assert_eq!(editor.inlay_hint_cache().version, expected_hints.len());
-//             expected_hints.len()
-//         });
-
-//         editor.update(cx, |editor, cx| {
-//             editor.change_selections(Some(Autoscroll::Next), cx, |s| {
-//                 s.select_ranges([Point::new(4, 0)..Point::new(4, 0)])
-//             });
-//         });
-//         cx.foreground().run_until_parked();
-//         editor.update(cx, |editor, cx| {
-//             let expected_hints = vec![
-//                 "main hint #0".to_string(),
-//                 "main hint #1".to_string(),
-//                 "main hint #2".to_string(),
-//                 "main hint #3".to_string(),
-//                 "main hint #4".to_string(),
-//                 "main hint #5".to_string(),
-//                 "other hint #0".to_string(),
-//                 "other hint #1".to_string(),
-//                 "other hint #2".to_string(),
-//                 "other hint #3".to_string(),
-//                 "other hint #4".to_string(),
-//                 "other hint #5".to_string(),
-//             ];
-//             assert_eq!(expected_hints, cached_hint_labels(editor),
-//                 "After multibuffer was scrolled to the end, further scrolls up should not bring more hints");
-//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-//             assert_eq!(editor.inlay_hint_cache().version, last_scroll_update_version, "No updates should happen during scrolling already scolled buffer");
-//         });
-
-//         editor_edited.store(true, Ordering::Release);
-//         editor.update(cx, |editor, cx| {
-//             editor.change_selections(None, cx, |s| {
-//                 s.select_ranges([Point::new(56, 0)..Point::new(56, 0)])
-//             });
-//             editor.handle_input("++++more text++++", cx);
-//         });
-//         cx.foreground().run_until_parked();
-//         editor.update(cx, |editor, cx| {
-//             let expected_hints = vec![
-//                 "main hint(edited) #0".to_string(),
-//                 "main hint(edited) #1".to_string(),
-//                 "main hint(edited) #2".to_string(),
-//                 "main hint(edited) #3".to_string(),
-//                 "main hint(edited) #4".to_string(),
-//                 "main hint(edited) #5".to_string(),
-//                 "other hint(edited) #0".to_string(),
-//                 "other hint(edited) #1".to_string(),
-//             ];
-//             assert_eq!(
-//                 expected_hints,
-//                 cached_hint_labels(editor),
-//                 "After multibuffer edit, editor gets scolled back to the last selection; \
-// all hints should be invalidated and requeried for all of its visible excerpts"
-//             );
-//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-
-//             let current_cache_version = editor.inlay_hint_cache().version;
-//             let minimum_expected_version = last_scroll_update_version + expected_hints.len();
-//             assert!(
-//                 current_cache_version == minimum_expected_version || current_cache_version == minimum_expected_version + 1,
-//                 "Due to every excerpt having one hint, cache should update per new excerpt received + 1 potential sporadic update"
-//             );
-//         });
-//     }
-
-//     #[gpui::test]
-//     async fn test_excerpts_removed(
-//         deterministic: Arc<Deterministic>,
-//         cx: &mut gpui::TestAppContext,
-//     ) {
-//         init_test(cx, |settings| {
-//             settings.defaults.inlay_hints = Some(InlayHintSettings {
-//                 enabled: true,
-//                 show_type_hints: false,
-//                 show_parameter_hints: false,
-//                 show_other_hints: false,
-//             })
-//         });
-
-//         let mut language = Language::new(
-//             LanguageConfig {
-//                 name: "Rust".into(),
-//                 path_suffixes: vec!["rs".to_string()],
-//                 ..Default::default()
-//             },
-//             Some(tree_sitter_rust::language()),
-//         );
-//         let mut fake_servers = language
-//             .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
-//                 capabilities: lsp::ServerCapabilities {
-//                     inlay_hint_provider: Some(lsp::OneOf::Left(true)),
-//                     ..Default::default()
-//                 },
-//                 ..Default::default()
-//             }))
-//             .await;
-//         let language = Arc::new(language);
-//         let fs = FakeFs::new(cx.background());
-//         fs.insert_tree(
-//             "/a",
-//             json!({
-//                 "main.rs": format!("fn main() {{\n{}\n}}", (0..501).map(|i| format!("let i = {i};\n")).collect::<Vec<_>>().join("")),
-//                 "other.rs": format!("fn main() {{\n{}\n}}", (0..501).map(|j| format!("let j = {j};\n")).collect::<Vec<_>>().join("")),
-//             }),
-//         )
-//         .await;
-//         let project = Project::test(fs, ["/a".as_ref()], cx).await;
-//         project.update(cx, |project, _| {
-//             project.languages().add(Arc::clone(&language))
-//         });
-//         let workspace = cx
-//             .add_window(|cx| Workspace::test_new(project.clone(), cx))
-//             .root(cx);
-//         let worktree_id = workspace.update(cx, |workspace, cx| {
-//             workspace.project().read_with(cx, |project, cx| {
-//                 project.worktrees(cx).next().unwrap().read(cx).id()
-//             })
-//         });
-
-//         let buffer_1 = project
-//             .update(cx, |project, cx| {
-//                 project.open_buffer((worktree_id, "main.rs"), cx)
-//             })
-//             .await
-//             .unwrap();
-//         let buffer_2 = project
-//             .update(cx, |project, cx| {
-//                 project.open_buffer((worktree_id, "other.rs"), cx)
-//             })
-//             .await
-//             .unwrap();
-//         let multibuffer = cx.add_model(|_| MultiBuffer::new(0));
-//         let (buffer_1_excerpts, buffer_2_excerpts) = multibuffer.update(cx, |multibuffer, cx| {
-//             let buffer_1_excerpts = multibuffer.push_excerpts(
-//                 buffer_1.clone(),
-//                 [ExcerptRange {
-//                     context: Point::new(0, 0)..Point::new(2, 0),
-//                     primary: None,
-//                 }],
-//                 cx,
-//             );
-//             let buffer_2_excerpts = multibuffer.push_excerpts(
-//                 buffer_2.clone(),
-//                 [ExcerptRange {
-//                     context: Point::new(0, 1)..Point::new(2, 1),
-//                     primary: None,
-//                 }],
-//                 cx,
-//             );
-//             (buffer_1_excerpts, buffer_2_excerpts)
-//         });
-
-//         assert!(!buffer_1_excerpts.is_empty());
-//         assert!(!buffer_2_excerpts.is_empty());
-
-//         deterministic.run_until_parked();
-//         cx.foreground().run_until_parked();
-//         let editor = cx
-//             .add_window(|cx| Editor::for_multibuffer(multibuffer, Some(project.clone()), cx))
-//             .root(cx);
-//         let editor_edited = Arc::new(AtomicBool::new(false));
-//         let fake_server = fake_servers.next().await.unwrap();
-//         let closure_editor_edited = Arc::clone(&editor_edited);
-//         fake_server
-//             .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
-//                 let task_editor_edited = Arc::clone(&closure_editor_edited);
-//                 async move {
-//                     let hint_text = if params.text_document.uri
-//                         == lsp::Url::from_file_path("/a/main.rs").unwrap()
-//                     {
-//                         "main hint"
-//                     } else if params.text_document.uri
-//                         == lsp::Url::from_file_path("/a/other.rs").unwrap()
-//                     {
-//                         "other hint"
-//                     } else {
-//                         panic!("unexpected uri: {:?}", params.text_document.uri);
-//                     };
-
-//                     let positions = [
-//                         lsp::Position::new(0, 2),
-//                         lsp::Position::new(4, 2),
-//                         lsp::Position::new(22, 2),
-//                         lsp::Position::new(44, 2),
-//                         lsp::Position::new(56, 2),
-//                         lsp::Position::new(67, 2),
-//                     ];
-//                     let out_of_range_hint = lsp::InlayHint {
-//                         position: lsp::Position::new(
-//                             params.range.start.line + 99,
-//                             params.range.start.character + 99,
-//                         ),
-//                         label: lsp::InlayHintLabel::String(
-//                             "out of excerpt range, should be ignored".to_string(),
-//                         ),
-//                         kind: None,
-//                         text_edits: None,
-//                         tooltip: None,
-//                         padding_left: None,
-//                         padding_right: None,
-//                         data: None,
-//                     };
-
-//                     let edited = task_editor_edited.load(Ordering::Acquire);
-//                     Ok(Some(
-//                         std::iter::once(out_of_range_hint)
-//                             .chain(positions.into_iter().enumerate().map(|(i, position)| {
-//                                 lsp::InlayHint {
-//                                     position,
-//                                     label: lsp::InlayHintLabel::String(format!(
-//                                         "{hint_text}{} #{i}",
-//                                         if edited { "(edited)" } else { "" },
-//                                     )),
-//                                     kind: None,
-//                                     text_edits: None,
-//                                     tooltip: None,
-//                                     padding_left: None,
-//                                     padding_right: None,
-//                                     data: None,
-//                                 }
-//                             }))
-//                             .collect(),
-//                     ))
-//                 }
-//             })
-//             .next()
-//             .await;
-//         cx.foreground().run_until_parked();
-
-//         editor.update(cx, |editor, cx| {
-//             assert_eq!(
-//                 vec!["main hint #0".to_string(), "other hint #0".to_string()],
-//                 cached_hint_labels(editor),
-//                 "Cache should update for both excerpts despite hints display was disabled"
-//             );
-//             assert!(
-//                 visible_hint_labels(editor, cx).is_empty(),
-//                 "All hints are disabled and should not be shown despite being present in the cache"
-//             );
-//             assert_eq!(
-//                 editor.inlay_hint_cache().version,
-//                 2,
-//                 "Cache should update once per excerpt query"
-//             );
-//         });
-
-//         editor.update(cx, |editor, cx| {
-//             editor.buffer().update(cx, |multibuffer, cx| {
-//                 multibuffer.remove_excerpts(buffer_2_excerpts, cx)
-//             })
-//         });
-//         cx.foreground().run_until_parked();
-//         editor.update(cx, |editor, cx| {
-//             assert_eq!(
-//                 vec!["main hint #0".to_string()],
-//                 cached_hint_labels(editor),
-//                 "For the removed excerpt, should clean corresponding cached hints"
-//             );
-//             assert!(
-//                 visible_hint_labels(editor, cx).is_empty(),
-//                 "All hints are disabled and should not be shown despite being present in the cache"
-//             );
-//             assert_eq!(
-//                 editor.inlay_hint_cache().version,
-//                 3,
-//                 "Excerpt removal should trigger a cache update"
-//             );
-//         });
-
-//         update_test_language_settings(cx, |settings| {
-//             settings.defaults.inlay_hints = Some(InlayHintSettings {
-//                 enabled: true,
-//                 show_type_hints: true,
-//                 show_parameter_hints: true,
-//                 show_other_hints: true,
-//             })
-//         });
-//         cx.foreground().run_until_parked();
-//         editor.update(cx, |editor, cx| {
-//             let expected_hints = vec!["main hint #0".to_string()];
-//             assert_eq!(
-//                 expected_hints,
-//                 cached_hint_labels(editor),
-//                 "Hint display settings change should not change the cache"
-//             );
-//             assert_eq!(
-//                 expected_hints,
-//                 visible_hint_labels(editor, cx),
-//                 "Settings change should make cached hints visible"
-//             );
-//             assert_eq!(
-//                 editor.inlay_hint_cache().version,
-//                 4,
-//                 "Settings change should trigger a cache update"
-//             );
-//         });
-//     }
-
-//     #[gpui::test]
-//     async fn test_inside_char_boundary_range_hints(cx: &mut gpui::TestAppContext) {
-//         init_test(cx, |settings| {
-//             settings.defaults.inlay_hints = Some(InlayHintSettings {
-//                 enabled: true,
-//                 show_type_hints: true,
-//                 show_parameter_hints: true,
-//                 show_other_hints: true,
-//             })
-//         });
-
-//         let mut language = Language::new(
-//             LanguageConfig {
-//                 name: "Rust".into(),
-//                 path_suffixes: vec!["rs".to_string()],
-//                 ..Default::default()
-//             },
-//             Some(tree_sitter_rust::language()),
-//         );
-//         let mut fake_servers = language
-//             .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
-//                 capabilities: lsp::ServerCapabilities {
-//                     inlay_hint_provider: Some(lsp::OneOf::Left(true)),
-//                     ..Default::default()
-//                 },
-//                 ..Default::default()
-//             }))
-//             .await;
-//         let fs = FakeFs::new(cx.background());
-//         fs.insert_tree(
-//             "/a",
-//             json!({
-//                 "main.rs": format!(r#"fn main() {{\n{}\n}}"#, format!("let i = {};\n", "√".repeat(10)).repeat(500)),
-//                 "other.rs": "// Test file",
-//             }),
-//         )
-//         .await;
-//         let project = Project::test(fs, ["/a".as_ref()], cx).await;
-//         project.update(cx, |project, _| project.languages().add(Arc::new(language)));
-//         let workspace = cx
-//             .add_window(|cx| Workspace::test_new(project.clone(), cx))
-//             .root(cx);
-//         let worktree_id = workspace.update(cx, |workspace, cx| {
-//             workspace.project().read_with(cx, |project, cx| {
-//                 project.worktrees(cx).next().unwrap().read(cx).id()
-//             })
-//         });
-
-//         let _buffer = project
-//             .update(cx, |project, cx| {
-//                 project.open_local_buffer("/a/main.rs", cx)
-//             })
-//             .await
-//             .unwrap();
-//         cx.foreground().run_until_parked();
-//         cx.foreground().start_waiting();
-//         let fake_server = fake_servers.next().await.unwrap();
-//         let editor = workspace
-//             .update(cx, |workspace, cx| {
-//                 workspace.open_path((worktree_id, "main.rs"), None, true, cx)
-//             })
-//             .await
-//             .unwrap()
-//             .downcast::<Editor>()
-//             .unwrap();
-//         let lsp_request_count = Arc::new(AtomicU32::new(0));
-//         let closure_lsp_request_count = Arc::clone(&lsp_request_count);
-//         fake_server
-//             .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
-//                 let task_lsp_request_count = Arc::clone(&closure_lsp_request_count);
-//                 async move {
-//                     assert_eq!(
-//                         params.text_document.uri,
-//                         lsp::Url::from_file_path("/a/main.rs").unwrap(),
-//                     );
-//                     let query_start = params.range.start;
-//                     let i = Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::Release) + 1;
-//                     Ok(Some(vec![lsp::InlayHint {
-//                         position: query_start,
-//                         label: lsp::InlayHintLabel::String(i.to_string()),
-//                         kind: None,
-//                         text_edits: None,
-//                         tooltip: None,
-//                         padding_left: None,
-//                         padding_right: None,
-//                         data: None,
-//                     }]))
-//                 }
-//             })
-//             .next()
-//             .await;
-
-//         cx.foreground().run_until_parked();
-//         editor.update(cx, |editor, cx| {
-//             editor.change_selections(None, cx, |s| {
-//                 s.select_ranges([Point::new(10, 0)..Point::new(10, 0)])
-//             })
-//         });
-//         cx.foreground().run_until_parked();
-//         editor.update(cx, |editor, cx| {
-//             let expected_hints = vec!["1".to_string()];
-//             assert_eq!(expected_hints, cached_hint_labels(editor));
-//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-//             assert_eq!(editor.inlay_hint_cache().version, 1);
-//         });
-//     }
-
-//     #[gpui::test]
-//     async fn test_toggle_inlay_hints(cx: &mut gpui::TestAppContext) {
-//         init_test(cx, |settings| {
-//             settings.defaults.inlay_hints = Some(InlayHintSettings {
-//                 enabled: false,
-//                 show_type_hints: true,
-//                 show_parameter_hints: true,
-//                 show_other_hints: true,
-//             })
-//         });
-
-//         let (file_with_hints, editor, fake_server) = prepare_test_objects(cx).await;
-
-//         editor.update(cx, |editor, cx| {
-//             editor.toggle_inlay_hints(&crate::ToggleInlayHints, cx)
-//         });
-//         cx.foreground().start_waiting();
-//         let lsp_request_count = Arc::new(AtomicU32::new(0));
-//         let closure_lsp_request_count = Arc::clone(&lsp_request_count);
-//         fake_server
-//             .handle_request::<lsp::request::InlayHintRequest, _, _>(move |params, _| {
-//                 let task_lsp_request_count = Arc::clone(&closure_lsp_request_count);
-//                 async move {
-//                     assert_eq!(
-//                         params.text_document.uri,
-//                         lsp::Url::from_file_path(file_with_hints).unwrap(),
-//                     );
-
-//                     let i = Arc::clone(&task_lsp_request_count).fetch_add(1, Ordering::SeqCst) + 1;
-//                     Ok(Some(vec![lsp::InlayHint {
-//                         position: lsp::Position::new(0, i),
-//                         label: lsp::InlayHintLabel::String(i.to_string()),
-//                         kind: None,
-//                         text_edits: None,
-//                         tooltip: None,
-//                         padding_left: None,
-//                         padding_right: None,
-//                         data: None,
-//                     }]))
-//                 }
-//             })
-//             .next()
-//             .await;
-//         cx.foreground().run_until_parked();
-//         editor.update(cx, |editor, cx| {
-//             let expected_hints = vec!["1".to_string()];
-//             assert_eq!(
-//                 expected_hints,
-//                 cached_hint_labels(editor),
-//                 "Should display inlays after toggle despite them disabled in settings"
-//             );
-//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-//             assert_eq!(
-//                 editor.inlay_hint_cache().version,
-//                 1,
-//                 "First toggle should be cache's first update"
-//             );
-//         });
-
-//         editor.update(cx, |editor, cx| {
-//             editor.toggle_inlay_hints(&crate::ToggleInlayHints, cx)
-//         });
-//         cx.foreground().run_until_parked();
-//         editor.update(cx, |editor, cx| {
-//             assert!(
-//                 cached_hint_labels(editor).is_empty(),
-//                 "Should clear hints after 2nd toggle"
-//             );
-//             assert!(visible_hint_labels(editor, cx).is_empty());
-//             assert_eq!(editor.inlay_hint_cache().version, 2);
-//         });
-
-//         update_test_language_settings(cx, |settings| {
-//             settings.defaults.inlay_hints = Some(InlayHintSettings {
-//                 enabled: true,
-//                 show_type_hints: true,
-//                 show_parameter_hints: true,
-//                 show_other_hints: true,
-//             })
-//         });
-//         cx.foreground().run_until_parked();
-//         editor.update(cx, |editor, cx| {
-//             let expected_hints = vec!["2".to_string()];
-//             assert_eq!(
-//                 expected_hints,
-//                 cached_hint_labels(editor),
-//                 "Should query LSP hints for the 2nd time after enabling hints in settings"
-//             );
-//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-//             assert_eq!(editor.inlay_hint_cache().version, 3);
-//         });
-
-//         editor.update(cx, |editor, cx| {
-//             editor.toggle_inlay_hints(&crate::ToggleInlayHints, cx)
-//         });
-//         cx.foreground().run_until_parked();
-//         editor.update(cx, |editor, cx| {
-//             assert!(
-//                 cached_hint_labels(editor).is_empty(),
-//                 "Should clear hints after enabling in settings and a 3rd toggle"
-//             );
-//             assert!(visible_hint_labels(editor, cx).is_empty());
-//             assert_eq!(editor.inlay_hint_cache().version, 4);
-//         });
-
-//         editor.update(cx, |editor, cx| {
-//             editor.toggle_inlay_hints(&crate::ToggleInlayHints, cx)
-//         });
-//         cx.foreground().run_until_parked();
-//         editor.update(cx, |editor, cx| {
-//             let expected_hints = vec!["3".to_string()];
-//             assert_eq!(
-//                 expected_hints,
-//                 cached_hint_labels(editor),
-//                 "Should query LSP hints for the 3rd time after enabling hints in settings and toggling them back on"
-//             );
-//             assert_eq!(expected_hints, visible_hint_labels(editor, cx));
-//             assert_eq!(editor.inlay_hint_cache().version, 5);
-//         });
-//     }
-
-//     pub(crate) fn init_test(cx: &mut TestAppContext, f: impl Fn(&mut AllLanguageSettingsContent)) {
-//         cx.foreground().forbid_parking();
-
-//         cx.update(|cx| {
-//             cx.set_global(SettingsStore::test(cx));
-//             theme::init(cx);
-//             client::init_settings(cx);
-//             language::init(cx);
-//             Project::init_settings(cx);
-//             workspace::init_settings(cx);
-//             crate::init(cx);
-//         });
-
-//         update_test_language_settings(cx, f);
-//     }
-
-//     async fn prepare_test_objects(
-//         cx: &mut TestAppContext,
-//     ) -> (&'static str, ViewHandle<Editor>, FakeLanguageServer) {
-//         let mut language = Language::new(
-//             LanguageConfig {
-//                 name: "Rust".into(),
-//                 path_suffixes: vec!["rs".to_string()],
-//                 ..Default::default()
-//             },
-//             Some(tree_sitter_rust::language()),
-//         );
-//         let mut fake_servers = language
-//             .set_fake_lsp_adapter(Arc::new(FakeLspAdapter {
-//                 capabilities: lsp::ServerCapabilities {
-//                     inlay_hint_provider: Some(lsp::OneOf::Left(true)),
-//                     ..Default::default()
-//                 },
-//                 ..Default::default()
-//             }))
-//             .await;
-
-//         let fs = FakeFs::new(cx.background());
-//         fs.insert_tree(
-//             "/a",
-//             json!({
-//                 "main.rs": "fn main() { a } // and some long comment to ensure inlays are not trimmed out",
-//                 "other.rs": "// Test file",
-//             }),
-//         )
-//         .await;
-
-//         let project = Project::test(fs, ["/a".as_ref()], cx).await;
-//         project.update(cx, |project, _| project.languages().add(Arc::new(language)));
-//         let workspace = cx
-//             .add_window(|cx| Workspace::test_new(project.clone(), cx))
-//             .root(cx);
-//         let worktree_id = workspace.update(cx, |workspace, cx| {
-//             workspace.project().read_with(cx, |project, cx| {
-//                 project.worktrees(cx).next().unwrap().read(cx).id()
-//             })
-//         });
-
-//         let _buffer = project
-//             .update(cx, |project, cx| {
-//                 project.open_local_buffer("/a/main.rs", cx)
-//             })
-//             .await
-//             .unwrap();
-//         cx.foreground().run_until_parked();
-//         cx.foreground().start_waiting();
-//         let fake_server = fake_servers.next().await.unwrap();
-//         let editor = workspace
-//             .update(cx, |workspace, cx| {
-//                 workspace.open_path((worktree_id, "main.rs"), None, true, cx)
-//             })
-//             .await
-//             .unwrap()
-//             .downcast::<Editor>()
-//             .unwrap();
-
-//         editor.update(cx, |editor, cx| {
-//             assert!(cached_hint_labels(editor).is_empty());
-//             assert!(visible_hint_labels(editor, cx).is_empty());
-//             assert_eq!(editor.inlay_hint_cache().version, 0);
-//         });
-
-//         ("/a/main.rs", editor, fake_server)
-//     }
-
-//     pub fn cached_hint_labels(editor: &Editor) -> Vec<String> {
-//         let mut labels = Vec::new();
-//         for (_, excerpt_hints) in &editor.inlay_hint_cache().hints {
-//             let excerpt_hints = excerpt_hints.read();
-//             for id in &excerpt_hints.ordered_hints {
-//                 labels.push(excerpt_hints.hints_by_id[id].text());
-//             }
-//         }
-
-//         labels.sort();
-//         labels
-//     }
-
-//     pub fn visible_hint_labels(editor: &Editor, cx: &ViewContext<'_, '_, Editor>) -> Vec<String> {
-//         let mut hints = editor
-//             .visible_inlay_hints(cx)
-//             .into_iter()
-//             .map(|hint| hint.text.to_string())
-//             .collect::<Vec<_>>();
-//         hints.sort();
-//         hints
-//     }
-// }

crates/gpui2/src/platform/test/platform.rs 🔗

@@ -182,7 +182,8 @@ impl Platform for TestPlatform {
     }
 
     fn should_auto_hide_scrollbars(&self) -> bool {
-        unimplemented!()
+        // todo()
+        true
     }
 
     fn write_to_clipboard(&self, _item: crate::ClipboardItem) {

crates/workspace2/src/workspace2.rs 🔗

@@ -3448,27 +3448,27 @@ impl Workspace {
         })
     }
 
-    // todo!()
-    //     #[cfg(any(test, feature = "test-support"))]
-    //     pub fn test_new(project: ModelHandle<Project>, cx: &mut ViewContext<Self>) -> Self {
-    //         use node_runtime::FakeNodeRuntime;
-
-    //         let client = project.read(cx).client();
-    //         let user_store = project.read(cx).user_store();
-
-    //         let workspace_store = cx.add_model(|cx| WorkspaceStore::new(client.clone(), cx));
-    //         let app_state = Arc::new(AppState {
-    //             languages: project.read(cx).languages().clone(),
-    //             workspace_store,
-    //             client,
-    //             user_store,
-    //             fs: project.read(cx).fs().clone(),
-    //             build_window_options: |_, _, _| Default::default(),
-    //             initialize_workspace: |_, _, _, _| Task::ready(Ok(())),
-    //             node_runtime: FakeNodeRuntime::new(),
-    //         });
-    //         Self::new(0, project, app_state, cx)
-    //     }
+    #[cfg(any(test, feature = "test-support"))]
+    pub fn test_new(project: Model<Project>, cx: &mut ViewContext<Self>) -> Self {
+        use gpui::Context;
+        use node_runtime::FakeNodeRuntime;
+
+        let client = project.read(cx).client();
+        let user_store = project.read(cx).user_store();
+
+        let workspace_store = cx.build_model(|cx| WorkspaceStore::new(client.clone(), cx));
+        let app_state = Arc::new(AppState {
+            languages: project.read(cx).languages().clone(),
+            workspace_store,
+            client,
+            user_store,
+            fs: project.read(cx).fs().clone(),
+            build_window_options: |_, _, _| Default::default(),
+            initialize_workspace: |_, _, _, _| Task::ready(Ok(())),
+            node_runtime: FakeNodeRuntime::new(),
+        });
+        Self::new(0, project, app_state, cx)
+    }
 
     //     fn render_dock(&self, position: DockPosition, cx: &WindowContext) -> Option<AnyElement<Self>> {
     //         let dock = match position {