@@ -1,5 +1,5 @@
/// Stores and updates all data received from LSP <a href="https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#textDocument_inlayHint">textDocument/inlayHint</a> requests.
-/// Has nothing to do with other inlays, e.g. copilot suggestions βΒ those are stored elsewhere.
+/// Has nothing to do with other inlays, e.g. copilot suggestions β those are stored elsewhere.
/// On every update, cache may query for more inlay hints and update inlays on the screen.
///
/// Inlays stored on screen are in [`crate::display_map::inlay_map`] and this cache is the only way to update any inlay hint data in the visible hints in the inlay map.
@@ -38,7 +38,7 @@ pub struct InlayHintCache {
pub(super) enabled: bool,
enabled_in_settings: bool,
update_tasks: HashMap<ExcerptId, TasksForRanges>,
- refresh_task: Option<Task<()>>,
+ refresh_task: Task<()>,
invalidate_debounce: Option<Duration>,
append_debounce: Option<Duration>,
lsp_request_limiter: Arc<Semaphore>,
@@ -116,13 +116,9 @@ impl InvalidationStrategy {
impl TasksForRanges {
fn new(query_ranges: QueryRanges, task: Task<()>) -> Self {
- let mut sorted_ranges = Vec::new();
- sorted_ranges.extend(query_ranges.before_visible);
- sorted_ranges.extend(query_ranges.visible);
- sorted_ranges.extend(query_ranges.after_visible);
Self {
tasks: vec![task],
- sorted_ranges,
+ sorted_ranges: query_ranges.into_sorted_query_ranges(),
}
}
@@ -135,7 +131,7 @@ impl TasksForRanges {
) {
let query_ranges = if invalidate.should_invalidate() {
self.tasks.clear();
- self.sorted_ranges.clear();
+ self.sorted_ranges = query_ranges.clone().into_sorted_query_ranges();
query_ranges
} else {
let mut non_cached_query_ranges = query_ranges;
@@ -272,7 +268,7 @@ impl InlayHintCache {
enabled_in_settings: inlay_hint_settings.enabled,
hints: HashMap::default(),
update_tasks: HashMap::default(),
- refresh_task: None,
+ refresh_task: Task::ready(()),
invalidate_debounce: debounce_value(inlay_hint_settings.edit_debounce_ms),
append_debounce: debounce_value(inlay_hint_settings.scroll_debounce_ms),
version: 0,
@@ -384,7 +380,7 @@ impl InlayHintCache {
} else {
self.append_debounce
};
- self.refresh_task = Some(cx.spawn(|editor, mut cx| async move {
+ self.refresh_task = cx.spawn(|editor, mut cx| async move {
if let Some(debounce_duration) = debounce_duration {
cx.background_executor().timer(debounce_duration).await;
}
@@ -401,7 +397,7 @@ impl InlayHintCache {
)
})
.ok();
- }));
+ });
if invalidated_hints.is_empty() {
None
@@ -580,10 +576,6 @@ impl InlayHintCache {
hints
}
- pub fn version(&self) -> usize {
- self.version
- }
-
/// Queries a certain hint from the cache for extra data via the LSP <a href="https://microsoft.github.io/language-server-protocol/specifications/lsp/3.17/specification/#inlayHint_resolve">resolve</a> request.
pub(super) fn spawn_hint_resolve(
&self,
@@ -731,6 +723,16 @@ impl QueryRanges {
fn is_empty(&self) -> bool {
self.before_visible.is_empty() && self.visible.is_empty() && self.after_visible.is_empty()
}
+
+ fn into_sorted_query_ranges(self) -> Vec<Range<text::Anchor>> {
+ let mut sorted_ranges = Vec::with_capacity(
+ self.before_visible.len() + self.visible.len() + self.after_visible.len(),
+ );
+ sorted_ranges.extend(self.before_visible);
+ sorted_ranges.extend(self.visible);
+ sorted_ranges.extend(self.after_visible);
+ sorted_ranges
+ }
}
fn determine_query_ranges(
@@ -940,7 +942,7 @@ fn fetch_and_update_hints(
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.");
+ 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
@@ -1008,12 +1010,12 @@ fn fetch_and_update_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::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| {
@@ -1181,48 +1183,39 @@ fn apply_hint_update(
.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
+ // When a hint is added to the same position where existing ones are present,
+ // do not deduplicate it: we split hint queries into non-overlapping ranges
+ // and each hint batch returned by the server should already contain unique hints.
+ i + cached_excerpt_hints.ordered_hints[i..].len() + 1
}
- Err(i) => Some(i),
+ Err(i) => 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_inlay_id = post_inc(&mut editor.next_inlay_id);
+ if editor
+ .inlay_hint_cache
+ .allowed_hint_kinds
+ .contains(&new_hint.kind)
+ {
+ if let Some(new_hint_position) =
+ multi_buffer_snapshot.anchor_in_excerpt(query.excerpt_id, new_hint.position)
{
- if let Some(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));
- }
+ 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);
+ }
+ let new_id = InlayId::Hint(new_inlay_id);
+ cached_excerpt_hints.hints_by_id.insert(new_id, new_hint);
+ if cached_excerpt_hints.ordered_hints.len() <= insert_position {
+ cached_excerpt_hints.ordered_hints.push(new_id);
+ } else {
cached_excerpt_hints
.ordered_hints
.insert(insert_position, new_id);
- cached_inlays_changed = true;
}
+
+ cached_inlays_changed = true;
}
cached_excerpt_hints.buffer_version = buffer_snapshot.version().clone();
drop(cached_excerpt_hints);
@@ -1266,15 +1259,19 @@ fn apply_hint_update(
#[cfg(test)]
pub mod tests {
use crate::editor_tests::update_test_language_settings;
+ use crate::scroll::ScrollAmount;
use crate::{scroll::Autoscroll, test::editor_lsp_test_context::rust_lang, ExcerptRange};
use futures::StreamExt;
use gpui::{Context, SemanticVersion, TestAppContext, WindowHandle};
+ use itertools::Itertools as _;
use language::{language_settings::AllLanguageSettingsContent, Capability, FakeLspAdapter};
+ use language::{Language, LanguageConfig, LanguageMatcher};
use lsp::FakeLanguageServer;
+ use parking_lot::Mutex;
use project::{FakeFs, Project};
use serde_json::json;
use settings::SettingsStore;
- use std::sync::atomic::{AtomicBool, AtomicU32, Ordering};
+ use std::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize, Ordering};
use text::Point;
use super::*;
@@ -1293,50 +1290,35 @@ pub mod tests {
show_background: false,
})
});
-
let (_, editor, fake_server) = prepare_test_objects(cx, |fake_server, file_with_hints| {
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 {
+ let i = task_lsp_request_count.fetch_add(1, Ordering::Release) + 1;
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))
+ 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,
+ }]))
}
});
})
.await;
cx.executor().run_until_parked();
- let mut edits_made = 1;
editor
.update(cx, |editor, cx| {
- let expected_hints = vec!["0".to_string()];
+ let expected_hints = vec!["1".to_string()];
assert_eq!(
expected_hints,
cached_hint_labels(editor),
@@ -1348,10 +1330,6 @@ pub mod tests {
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"
- );
})
.unwrap();
@@ -1359,13 +1337,12 @@ pub mod tests {
.update(cx, |editor, cx| {
editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
editor.handle_input("some change", cx);
- edits_made += 1;
})
.unwrap();
cx.executor().run_until_parked();
editor
.update(cx, |editor, cx| {
- let expected_hints = vec!["0".to_string(), "1".to_string()];
+ let expected_hints = vec!["2".to_string()];
assert_eq!(
expected_hints,
cached_hint_labels(editor),
@@ -1377,10 +1354,6 @@ pub mod tests {
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"
- );
})
.unwrap();
@@ -1388,11 +1361,10 @@ pub mod tests {
.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()];
+ let expected_hints = vec!["3".to_string()];
assert_eq!(
expected_hints,
cached_hint_labels(editor),
@@ -1404,10 +1376,6 @@ pub mod tests {
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"
- );
})
.unwrap();
}
@@ -1453,7 +1421,6 @@ pub mod tests {
.await;
cx.executor().run_until_parked();
- let mut edits_made = 1;
editor
.update(cx, |editor, cx| {
let expected_hints = vec!["0".to_string()];
@@ -1463,11 +1430,6 @@ pub mod tests {
"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"
- );
})
.unwrap();
@@ -1496,11 +1458,6 @@ pub mod tests {
"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"
- );
})
.unwrap();
@@ -1512,7 +1469,6 @@ pub mod tests {
});
cx.executor().run_until_parked();
- edits_made += 1;
editor
.update(cx, |editor, cx| {
let expected_hints = vec!["1".to_string()];
@@ -1522,246 +1478,223 @@ pub mod tests {
"New hints should be queried after the work task is done"
);
assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+ })
+ .unwrap();
+ }
+
+ #[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,
+ edit_debounce_ms: 0,
+ scroll_debounce_ms: 0,
+ show_type_hints: true,
+ show_parameter_hints: true,
+ show_other_hints: true,
+ show_background: false,
+ })
+ });
+
+ 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 language_registry = project.read_with(cx, |project, _| project.languages().clone());
+ let mut rs_fake_servers = None;
+ let mut md_fake_servers = None;
+ for (name, path_suffix) in [("Rust", "rs"), ("Markdown", "md")] {
+ language_registry.add(Arc::new(Language::new(
+ LanguageConfig {
+ name: name.into(),
+ matcher: LanguageMatcher {
+ path_suffixes: vec![path_suffix.to_string()],
+ ..Default::default()
+ },
+ ..Default::default()
+ },
+ Some(tree_sitter_rust::LANGUAGE.into()),
+ )));
+ let fake_servers = language_registry.register_fake_lsp(
+ name,
+ FakeLspAdapter {
+ name,
+ capabilities: lsp::ServerCapabilities {
+ inlay_hint_provider: Some(lsp::OneOf::Left(true)),
+ ..Default::default()
+ },
+ initializer: Some(Box::new({
+ move |fake_server| {
+ let rs_lsp_request_count = Arc::new(AtomicU32::new(0));
+ let md_lsp_request_count = Arc::new(AtomicU32::new(0));
+ fake_server.handle_request::<lsp::request::InlayHintRequest, _, _>(
+ move |params, _| {
+ let i = match name {
+ "Rust" => {
+ assert_eq!(
+ params.text_document.uri,
+ lsp::Url::from_file_path("/a/main.rs").unwrap(),
+ );
+ rs_lsp_request_count.fetch_add(1, Ordering::Release) + 1
+ }
+ "Markdown" => {
+ assert_eq!(
+ params.text_document.uri,
+ lsp::Url::from_file_path("/a/other.md").unwrap(),
+ );
+ md_lsp_request_count.fetch_add(1, Ordering::Release) + 1
+ }
+ unexpected => panic!("Unexpected language: {unexpected}"),
+ };
+
+ async move {
+ let query_start = params.range.start;
+ 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,
+ }]))
+ }
+ },
+ );
+ }
+ })),
+ ..Default::default()
+ },
+ );
+ match name {
+ "Rust" => rs_fake_servers = Some(fake_servers),
+ "Markdown" => md_fake_servers = Some(fake_servers),
+ _ => unreachable!(),
+ }
+ }
+
+ let rs_buffer = project
+ .update(cx, |project, cx| {
+ project.open_local_buffer("/a/main.rs", cx)
+ })
+ .await
+ .unwrap();
+ let rs_editor =
+ cx.add_window(|cx| Editor::for_buffer(rs_buffer, Some(project.clone()), cx));
+ cx.executor().run_until_parked();
+
+ let _rs_fake_server = rs_fake_servers.unwrap().next().await.unwrap();
+ cx.executor().run_until_parked();
+ rs_editor
+ .update(cx, |editor, cx| {
+ let expected_hints = vec!["1".to_string()];
assert_eq!(
- editor.inlay_hint_cache().version,
- edits_made,
- "Cache version should update once after the work task is done"
+ expected_hints,
+ cached_hint_labels(editor),
+ "Should get its first hints when opening the editor"
);
+ assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+ })
+ .unwrap();
+
+ cx.executor().run_until_parked();
+ let md_buffer = project
+ .update(cx, |project, cx| {
+ project.open_local_buffer("/a/other.md", cx)
+ })
+ .await
+ .unwrap();
+ let md_editor = cx.add_window(|cx| Editor::for_buffer(md_buffer, Some(project), cx));
+ cx.executor().run_until_parked();
+
+ let _md_fake_server = md_fake_servers.unwrap().next().await.unwrap();
+ 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),
+ "Markdown editor should have a separate version, repeating Rust editor rules"
+ );
+ assert_eq!(expected_hints, visible_hint_labels(editor, cx));
+ })
+ .unwrap();
+
+ rs_editor
+ .update(cx, |editor, cx| {
+ editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
+ editor.handle_input("some rs change", cx);
+ })
+ .unwrap();
+ cx.executor().run_until_parked();
+ rs_editor
+ .update(cx, |editor, cx| {
+ // TODO: Here, we do not get "2", because inserting another language server will trigger `RefreshInlayHints` event from the `LspStore`
+ // A project is listened in every editor, so each of them will react to this event.
+ //
+ // We do not have language server IDs for remote projects, so cannot easily say on the editor level,
+ // whether we should ignore a particular `RefreshInlayHints` event.
+ let expected_hints = vec!["3".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));
+ })
+ .unwrap();
+ md_editor
+ .update(cx, |editor, cx| {
+ let expected_hints = vec!["1".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));
})
.unwrap();
- }
- // #[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,
- // edit_debounce_ms: 0,
- // scroll_debounce_ms: 0,
- // show_type_hints: true,
- // show_parameter_hints: true,
- // show_other_hints: true,
- // show_background: false,
- // })
- // });
-
- // 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 language_registry = project.read_with(cx, |project, _| project.languages().clone());
- // let mut rs_fake_servers = None;
- // let mut md_fake_servers = None;
- // for (name, path_suffix) in [("Rust", "rs"), ("Markdown", "md")] {
- // language_registry.add(Arc::new(Language::new(
- // LanguageConfig {
- // name: name.into(),
- // matcher: LanguageMatcher {
- // path_suffixes: vec![path_suffix.to_string()],
- // ..Default::default()
- // },
- // ..Default::default()
- // },
- // Some(tree_sitter_rust::LANGUAGE.into()),
- // )));
- // let fake_servers = language_registry.register_fake_lsp(
- // name,
- // FakeLspAdapter {
- // name,
- // capabilities: lsp::ServerCapabilities {
- // inlay_hint_provider: Some(lsp::OneOf::Left(true)),
- // ..Default::default()
- // },
- // ..Default::default()
- // },
- // );
- // match name {
- // "Rust" => rs_fake_servers = Some(fake_servers),
- // "Markdown" => md_fake_servers = Some(fake_servers),
- // _ => unreachable!(),
- // }
- // }
-
- // let rs_buffer = project
- // .update(cx, |project, cx| {
- // project.open_local_buffer("/a/main.rs", cx)
- // })
- // .await
- // .unwrap();
- // let rs_editor =
- // cx.add_window(|cx| Editor::for_buffer(rs_buffer, Some(project.clone()), cx));
-
- // cx.executor().run_until_parked();
- // cx.executor().start_waiting();
- // let rs_fake_server = rs_fake_servers.unwrap().next().await.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.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"
- // );
- // })
- // .unwrap();
-
- // cx.executor().run_until_parked();
- // let md_buffer = project
- // .update(cx, |project, cx| {
- // project.open_local_buffer("/a/other.md", cx)
- // })
- // .await
- // .unwrap();
- // let md_editor = cx.add_window(|cx| Editor::for_buffer(md_buffer, Some(project), cx));
-
- // cx.executor().run_until_parked();
- // cx.executor().start_waiting();
- // let md_fake_server = md_fake_servers.unwrap().next().await.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.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 version, repeating Rust editor rules"
- // );
- // assert_eq!(expected_hints, visible_hint_labels(editor, cx));
- // assert_eq!(editor.inlay_hint_cache().version, 1);
- // })
- // .unwrap();
-
- // rs_editor
- // .update(cx, |editor, cx| {
- // editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
- // editor.handle_input("some rs change", cx);
- // })
- // .unwrap();
- // 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"
- // );
- // })
- // .unwrap();
- // 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);
- // })
- // .unwrap();
-
- // md_editor
- // .update(cx, |editor, cx| {
- // editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
- // editor.handle_input("some md change", cx);
- // })
- // .unwrap();
- // 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);
- // })
- // .unwrap();
- // 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);
- // })
- // .unwrap();
- // }
+ md_editor
+ .update(cx, |editor, cx| {
+ editor.change_selections(None, cx, |s| s.select_ranges([13..13]));
+ editor.handle_input("some md change", cx);
+ })
+ .unwrap();
+ cx.executor().run_until_parked();
+ md_editor
+ .update(cx, |editor, cx| {
+ let expected_hints = vec!["2".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));
+ })
+ .unwrap();
+ rs_editor
+ .update(cx, |editor, cx| {
+ let expected_hints = vec!["3".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));
+ })
+ .unwrap();
+ }
#[gpui::test]
async fn test_hint_setting_changes(cx: &mut gpui::TestAppContext) {
@@ -1778,14 +1711,14 @@ pub mod tests {
})
});
- let lsp_request_count = Arc::new(AtomicU32::new(0));
+ let lsp_request_count = Arc::new(AtomicUsize::new(0));
let (_, editor, fake_server) = prepare_test_objects(cx, {
let lsp_request_count = lsp_request_count.clone();
move |fake_server, file_with_hints| {
let lsp_request_count = lsp_request_count.clone();
fake_server.handle_request::<lsp::request::InlayHintRequest, _, _>(
move |params, _| {
- lsp_request_count.fetch_add(1, Ordering::SeqCst);
+ lsp_request_count.fetch_add(1, Ordering::Release);
async move {
assert_eq!(
params.text_document.uri,
@@ -1833,7 +1766,6 @@ pub mod tests {
.await;
cx.executor().run_until_parked();
- let mut edits_made = 1;
editor
.update(cx, |editor, cx| {
assert_eq!(
@@ -1843,15 +1775,15 @@ pub mod tests {
);
assert_eq!(
vec![
- "other hint".to_string(),
- "parameter hint".to_string(),
"type hint".to_string(),
+ "parameter hint".to_string(),
+ "other 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()],
+ vec!["type hint".to_string(), "other hint".to_string()],
visible_hint_labels(editor, cx)
);
let inlay_cache = editor.inlay_hint_cache();
@@ -1859,10 +1791,6 @@ pub mod tests {
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"
- );
})
.unwrap();
@@ -1880,22 +1808,17 @@ pub mod tests {
);
assert_eq!(
vec![
- "other hint".to_string(),
- "parameter hint".to_string(),
"type hint".to_string(),
+ "parameter hint".to_string(),
+ "other 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()],
+ vec!["type hint".to_string(), "other 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"
- );
})
.unwrap();
@@ -1911,15 +1834,15 @@ pub mod tests {
),
(
HashSet::from_iter([None, Some(InlayHintKind::Type)]),
- vec!["other hint".to_string(), "type hint".to_string()],
+ vec!["type hint".to_string(), "other hint".to_string()],
),
(
HashSet::from_iter([None, Some(InlayHintKind::Parameter)]),
- vec!["other hint".to_string(), "parameter hint".to_string()],
+ vec!["parameter hint".to_string(), "other hint".to_string()],
),
(
HashSet::from_iter([Some(InlayHintKind::Type), Some(InlayHintKind::Parameter)]),
- vec!["parameter hint".to_string(), "type hint".to_string()],
+ vec!["type hint".to_string(), "parameter hint".to_string()],
),
(
HashSet::from_iter([
@@ -1928,13 +1851,12 @@ pub mod tests {
Some(InlayHintKind::Parameter),
]),
vec![
- "other hint".to_string(),
- "parameter hint".to_string(),
"type hint".to_string(),
+ "parameter hint".to_string(),
+ "other hint".to_string(),
],
),
] {
- edits_made += 1;
update_test_language_settings(cx, |settings| {
settings.defaults.inlay_hints = Some(InlayHintSettings {
enabled: true,
@@ -1956,9 +1878,9 @@ pub mod tests {
);
assert_eq!(
vec![
- "other hint".to_string(),
- "parameter hint".to_string(),
"type hint".to_string(),
+ "parameter hint".to_string(),
+ "other hint".to_string(),
],
cached_hint_labels(editor),
"Should get its cached hints unchanged after the settings change for hint kinds {new_allowed_hint_kinds:?}"
@@ -1973,14 +1895,9 @@ pub mod tests {
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"
- );
}).unwrap();
}
- 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 {
@@ -2015,10 +1932,6 @@ pub mod tests {
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"
- );
})
.unwrap();
@@ -2027,22 +1940,19 @@ pub mod tests {
.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"
- );
- }).unwrap();
+ 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());
+ })
+ .unwrap();
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,