diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 29e009fdf8..06894bf1f9 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -126,7 +126,7 @@ use language::{ Language, OffsetRangeExt, Point, Runnable, RunnableRange, Selection, SelectionGoal, TextObject, TransactionId, TreeSitterOptions, WordsQuery, language_settings::{ - self, InlayHintSettings, LspInsertMode, RewrapBehavior, WordsCompletionMode, + self, InlayHintKind, InlayHintSettings, LspInsertMode, RewrapBehavior, WordsCompletionMode, all_language_settings, language_settings, }, point_from_lsp, point_to_lsp, text_diff_with_options, @@ -150,18 +150,18 @@ use project::{ BreakpointWithPosition, CodeAction, Completion, CompletionIntent, CompletionResponse, CompletionSource, DisableAiSettings, DocumentHighlight, InlayHint, Location, LocationLink, PrepareRenameResponse, Project, ProjectItem, ProjectPath, ProjectTransaction, TaskSourceKind, - debugger::breakpoint_store::Breakpoint, debugger::{ breakpoint_store::{ - BreakpointEditAction, BreakpointSessionState, BreakpointState, BreakpointStore, - BreakpointStoreEvent, + Breakpoint, BreakpointEditAction, BreakpointSessionState, BreakpointState, + BreakpointStore, BreakpointStoreEvent, }, session::{Session, SessionEvent}, }, git_store::{GitStoreEvent, RepositoryEvent}, lsp_store::{CompletionDocumentation, FormatTrigger, LspFormatTarget, OpenLspBufferHandle}, - project_settings::{DiagnosticSeverity, GoToDiagnosticSeverityFilter}, - project_settings::{GitGutterSetting, ProjectSettings}, + project_settings::{ + DiagnosticSeverity, GitGutterSetting, GoToDiagnosticSeverityFilter, ProjectSettings, + }, }; use rand::{seq::SliceRandom, thread_rng}; use rpc::{ErrorCode, ErrorExt, proto::PeerId}; @@ -1177,9 +1177,137 @@ pub struct Editor { selection_drag_state: SelectionDragState, next_color_inlay_id: usize, colors: Option, + inlay_hints: Option, folding_newlines: Task<()>, } +#[derive(Debug)] +struct LspInlayHintData { + enabled: bool, + modifiers_override: bool, + enabled_in_settings: bool, + allowed_hint_kinds: HashSet>, + invalidate_debounce: Option, + append_debounce: Option, + inlays_for_version: Option, + inlays: HashMap>, + inlay_tasks: HashMap, Task<()>>>, +} + +impl LspInlayHintData { + fn new(settings: InlayHintSettings) -> Self { + Self { + modifiers_override: false, + enabled: settings.enabled, + enabled_in_settings: settings.enabled, + inlays: HashMap::default(), + inlays_for_version: None, + inlay_tasks: HashMap::default(), + invalidate_debounce: debounce_value(settings.edit_debounce_ms), + append_debounce: debounce_value(settings.scroll_debounce_ms), + allowed_hint_kinds: settings.enabled_inlay_hint_kinds(), + } + } + + fn modifiers_override(&mut self, new_override: bool) -> Option { + if self.modifiers_override == new_override { + return None; + } + self.modifiers_override = new_override; + if (self.enabled && self.modifiers_override) || (!self.enabled && !self.modifiers_override) + { + self.clear(); + Some(false) + } else { + Some(true) + } + } + + fn toggle(&mut self, enabled: bool) -> bool { + if self.enabled == enabled { + return false; + } + self.enabled = enabled; + self.modifiers_override = false; + if !enabled { + self.clear(); + } + true + } + + fn clear(&mut self) { + self.inlay_tasks.clear(); + // TODO kb splice!? + self.inlays.clear(); + } + + /// Checks inlay hint settings for enabled hint kinds and general enabled state. + /// Generates corresponding inlay_map splice updates on settings changes. + /// Does not update inlay hint cache state on disabling or inlay hint kinds change: only reenabling forces new LSP queries. + fn update_settings( + &mut self, + multi_buffer: &Entity, + new_hint_settings: InlayHintSettings, + visible_hints: Vec, + cx: &mut App, + ) -> ControlFlow> { + let old_enabled = self.enabled; + // If the setting for inlay hints has changed, update `enabled`. This condition avoids inlay + // hint visibility changes when other settings change (such as theme). + // + // Another option might be to store whether the user has manually toggled inlay hint + // visibility, and prefer this. This could lead to confusion as it means inlay hint + // visibility would not change when updating the setting if they were ever toggled. + if new_hint_settings.enabled != self.enabled_in_settings { + self.enabled = new_hint_settings.enabled; + self.enabled_in_settings = new_hint_settings.enabled; + self.modifiers_override = false; + }; + self.invalidate_debounce = debounce_value(new_hint_settings.edit_debounce_ms); + self.append_debounce = debounce_value(new_hint_settings.scroll_debounce_ms); + let new_allowed_hint_kinds = new_hint_settings.enabled_inlay_hint_kinds(); + match (old_enabled, self.enabled) { + (false, false) => { + self.allowed_hint_kinds = new_allowed_hint_kinds; + ControlFlow::Break(None) + } + (true, true) => { + if new_allowed_hint_kinds == self.allowed_hint_kinds { + ControlFlow::Break(None) + } else { + todo!("TODO kb") + } + } + (true, false) => { + self.modifiers_override = false; + self.allowed_hint_kinds = new_allowed_hint_kinds; + if self.inlays.is_empty() { + ControlFlow::Break(None) + } else { + self.clear(); + ControlFlow::Break(Some(InlaySplice { + to_remove: visible_hints.iter().map(|inlay| inlay.id).collect(), + to_insert: Vec::new(), + })) + } + } + (false, true) => { + self.modifiers_override = false; + self.allowed_hint_kinds = new_allowed_hint_kinds; + ControlFlow::Continue(()) + } + } + } +} + +fn debounce_value(debounce_ms: u64) -> Option { + if debounce_ms > 0 { + Some(Duration::from_millis(debounce_ms)) + } else { + None + } +} + #[derive(Copy, Clone, Debug, PartialEq, Eq, Default)] enum NextScrollCursorCenterTopBottom { #[default] @@ -1611,7 +1739,7 @@ enum InlayHintRefreshReason { SettingsChange(InlayHintSettings), NewLinesShown, BufferEdited(HashSet>), - RefreshRequested, + RefreshRequested(LanguageServerId), ExcerptsRemoved(Vec), } @@ -1623,7 +1751,7 @@ impl InlayHintRefreshReason { Self::SettingsChange(_) => "settings change", Self::NewLinesShown => "new lines shown", Self::BufferEdited(_) => "buffer edited", - Self::RefreshRequested => "refresh requested", + Self::RefreshRequested(_) => "refresh requested", Self::ExcerptsRemoved(_) => "excerpts removed", } } @@ -1864,8 +1992,11 @@ impl Editor { project::Event::RefreshCodeLens => { // we always query lens with actions, without storing them, always refreshing them } - project::Event::RefreshInlayHints => { - editor.refresh_inlay_hints(InlayHintRefreshReason::RefreshRequested, cx); + project::Event::RefreshInlayHints(server_id) => { + editor.refresh_inlay_hints( + InlayHintRefreshReason::RefreshRequested(*server_id), + cx, + ); } project::Event::LanguageServerAdded(..) | project::Event::LanguageServerRemoved(..) => { @@ -2230,6 +2361,7 @@ impl Editor { tasks_update_task: None, pull_diagnostics_task: Task::ready(()), colors: None, + inlay_hints: None, next_color_inlay_id: 0, linked_edit_ranges: Default::default(), in_project_search: false, @@ -2378,6 +2510,7 @@ impl Editor { editor.minimap = editor.create_minimap(EditorSettings::get_global(cx).minimap, window, cx); editor.colors = Some(LspColorData::new(cx)); + editor.inlay_hints = Some(LspInlayHintData::new(inlay_hint_settings)); editor.update_lsp_data(false, None, window, cx); } @@ -5229,7 +5362,7 @@ impl Editor { InlayHintRefreshReason::BufferEdited(buffer_languages) => { (InvalidationStrategy::BufferEdited, Some(buffer_languages)) } - InlayHintRefreshReason::RefreshRequested => { + InlayHintRefreshReason::RefreshRequested(_) => { (InvalidationStrategy::RefreshRequested, None) } }; @@ -5248,6 +5381,209 @@ impl Editor { } } + fn refresh_inlay_hints_2(&mut self, reason: InlayHintRefreshReason, cx: &mut Context) { + if !self.mode.is_full() || self.semantics_provider.is_none() { + return; + } + + { + let Some(inlay_hints) = self.inlay_hints.as_mut() else { + return; + }; + + let reason_description = reason.description(); + let ignore_debounce = matches!( + reason, + InlayHintRefreshReason::SettingsChange(_) + | InlayHintRefreshReason::Toggle(_) + | InlayHintRefreshReason::ExcerptsRemoved(_) + | InlayHintRefreshReason::ModifiersChanged(_) + ); + let (invalidate_cache, required_languages) = match reason { + InlayHintRefreshReason::ModifiersChanged(enabled) => { + match inlay_hints.modifiers_override(enabled) { + Some(enabled) => { + if enabled { + (InvalidationStrategy::RefreshRequested, None) + } else { + self.splice_inlays( + &self + .visible_inlay_hints(cx) + .iter() + .map(|inlay| inlay.id) + .collect::>(), + Vec::new(), + cx, + ); + return; + } + } + None => return, + } + } + InlayHintRefreshReason::Toggle(enabled) => { + if inlay_hints.toggle(enabled) { + if enabled { + (InvalidationStrategy::RefreshRequested, None) + } else { + self.splice_inlays( + &self + .visible_inlay_hints(cx) + .iter() + .map(|inlay| inlay.id) + .collect::>(), + Vec::new(), + cx, + ); + return; + } + } else { + return; + } + } + InlayHintRefreshReason::SettingsChange(new_settings) => { + return; + } + InlayHintRefreshReason::ExcerptsRemoved(excerpts_removed) => { + if let Some(InlaySplice { + to_remove, + to_insert, + }) = self.inlay_hint_cache.remove_excerpts(&excerpts_removed) + { + self.splice_inlays(&to_remove, to_insert, cx); + } + self.display_map.update(cx, |display_map, _| { + display_map.remove_inlays_for_excerpts(&excerpts_removed) + }); + return; + } + InlayHintRefreshReason::NewLinesShown => (InvalidationStrategy::None, None), + InlayHintRefreshReason::BufferEdited(buffer_languages) => { + (InvalidationStrategy::BufferEdited, Some(buffer_languages)) + } + InlayHintRefreshReason::RefreshRequested(_) => { + (InvalidationStrategy::RefreshRequested, None) + } + }; + } + + let multi_buffer_snapshot = self.buffer().read(cx).snapshot(cx); + let Some(semantics_provider) = self.semantics_provider.clone() else { + return; + }; + for (excerpt_id, (buffer, buffer_version, range)) in self.visible_excerpts(None, cx) { + let Some(excerpt_text_anchor_range) = + multi_buffer_snapshot.context_range_for_excerpt(excerpt_id) + else { + continue; + }; + let buffer_id = buffer.read(cx).remote_id(); + let buffer_snapshot = buffer.read(cx).snapshot(); + let buffer_anchor_range = + buffer_snapshot.anchor_before(range.start)..buffer_snapshot.anchor_after(range.end); + + let new_hints = + semantics_provider.inlay_hints_2(buffer, buffer_anchor_range.clone(), cx); + let Some(inlay_hints) = self.inlay_hints.as_mut() else { + return; + }; + if let Some((hints_range, new_hints)) = new_hints { + let buffer_hints = inlay_hints.inlay_tasks.entry(buffer_id).or_default(); + buffer_hints.insert( + hints_range.clone(), + cx.spawn(async move |editor, cx| { + let new_hints = new_hints.await; + editor + .update(cx, |editor, cx| { + let multi_buffer_snapshot = editor.buffer.read(cx).snapshot(cx); + let Some(buffer_snapshot) = + multi_buffer_snapshot.buffer_for_excerpt(excerpt_id) + else { + return; + }; + + let mut update_data = None; + if let Some(inlay_hints) = editor.inlay_hints.as_mut() { + let inlay_tasks = + inlay_hints.inlay_tasks.entry(buffer_id).or_default(); + match new_hints { + Ok(new_hints) => { + if inlay_hints.inlays_for_version.as_ref().is_some_and( + |inlays_for_version| { + !inlays_for_version + .changed_since(&buffer_version) + }, + ) { + let hints_to_remove = if inlay_hints + .inlays_for_version + .as_ref() + .is_some_and(|inlays_for_version| { + buffer_version + .changed_since(&inlays_for_version) + }) { + inlay_hints + .inlays + .remove(&buffer_id) + .unwrap_or_default() + } else { + Vec::new() + }; + let hints_to_insert = new_hints + .into_iter() + .filter_map(|lsp_hint| { + if buffer_anchor_range + .start + .cmp( + &lsp_hint.position, + buffer_snapshot, + ) + .is_ge() + && buffer_anchor_range + .end + .cmp( + &lsp_hint.position, + buffer_snapshot, + ) + .is_le() + { + let position = multi_buffer_snapshot + .anchor_in_excerpt( + excerpt_id, + lsp_hint.position, + )?; + return Some(Inlay::hint( + post_inc(&mut editor.next_inlay_id), + position, + &lsp_hint, + )); + } + None + }) + .collect(); + update_data = + Some((hints_to_remove, hints_to_insert)); + inlay_hints.inlays_for_version = + Some(buffer_version); + } + } + // TODO kb who should log and clean up the errored state? Could we do that with `lsp_store_cx.spawn`? + Err(_) => {} + } + + inlay_tasks.remove(&hints_range); + } + + if let Some((hints_to_remove, hints_to_insert)) = update_data { + editor.splice_inlays(&hints_to_remove, hints_to_insert, cx); + } + }) + .ok(); + }), + ); + } + } + } + fn visible_inlay_hints(&self, cx: &Context) -> Vec { self.display_map .read(cx) @@ -16751,9 +17087,9 @@ impl Editor { HashSet::default(), cx, ); - cx.emit(project::Event::RefreshInlayHints); }); }); + self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx); } } @@ -21874,6 +22210,18 @@ pub trait SemanticsProvider { cx: &mut App, ) -> Option>>>; + fn inlay_hints_2( + &self, + buffer: Entity, + range: Range, + cx: &mut App, + ) -> Option<( + Range, + Shared, Arc>>>, + )> { + todo!("TODO kb") + } + fn resolve_inlay_hint( &self, hint: InlayHint, diff --git a/crates/project/src/lsp_store.rs b/crates/project/src/lsp_store.rs index deebaedd74..c5e0b726d3 100644 --- a/crates/project/src/lsp_store.rs +++ b/crates/project/src/lsp_store.rs @@ -14,6 +14,9 @@ pub mod json_language_server_ext; pub mod lsp_ext_command; pub mod rust_analyzer_ext; +mod inlay_hint_cache; + +use self::inlay_hint_cache::BufferInlayHints; use crate::{ CodeAction, ColorPresentation, Completion, CompletionResponse, CompletionSource, CoreCompletion, DocumentColor, Hover, InlayHint, LocationLink, LspAction, LspPullDiagnostics, @@ -564,7 +567,7 @@ impl LocalLspStore { } fn setup_lsp_messages( - this: WeakEntity, + lsp_store: WeakEntity, fs: Arc, language_server: &LanguageServer, delegate: Arc, @@ -575,7 +578,7 @@ impl LocalLspStore { language_server .on_notification::({ let adapter = adapter.clone(); - let this = this.clone(); + let this = lsp_store.clone(); move |mut params, cx| { let adapter = adapter.clone(); if let Some(this) = this.upgrade() { @@ -619,7 +622,7 @@ impl LocalLspStore { .on_request::({ let adapter = adapter.adapter.clone(); let delegate = delegate.clone(); - let this = this.clone(); + let this = lsp_store.clone(); let fs = fs.clone(); move |params, cx| { let adapter = adapter.clone(); @@ -667,7 +670,7 @@ impl LocalLspStore { language_server .on_request::({ - let this = this.clone(); + let this = lsp_store.clone(); move |_, cx| { let this = this.clone(); let cx = cx.clone(); @@ -695,7 +698,7 @@ impl LocalLspStore { // to these requests when initializing. language_server .on_request::({ - let this = this.clone(); + let this = lsp_store.clone(); move |params, cx| { let this = this.clone(); let mut cx = cx.clone(); @@ -716,7 +719,7 @@ impl LocalLspStore { language_server .on_request::({ - let lsp_store = this.clone(); + let lsp_store = lsp_store.clone(); move |params, cx| { let lsp_store = lsp_store.clone(); let mut cx = cx.clone(); @@ -745,7 +748,7 @@ impl LocalLspStore { language_server .on_request::({ - let lsp_store = this.clone(); + let lsp_store = lsp_store.clone(); move |params, cx| { let lsp_store = lsp_store.clone(); let mut cx = cx.clone(); @@ -774,7 +777,7 @@ impl LocalLspStore { language_server .on_request::({ - let this = this.clone(); + let this = lsp_store.clone(); move |params, cx| { let mut cx = cx.clone(); let this = this.clone(); @@ -793,18 +796,22 @@ impl LocalLspStore { language_server .on_request::({ - let this = this.clone(); + let lsp_store = lsp_store.clone(); move |(), cx| { - let this = this.clone(); + let this = lsp_store.clone(); let mut cx = cx.clone(); async move { - this.update(&mut cx, |this, cx| { - cx.emit(LspStoreEvent::RefreshInlayHints); - this.downstream_client.as_ref().map(|(client, project_id)| { - client.send(proto::RefreshInlayHints { - project_id: *project_id, + this.update(&mut cx, |lsp_store, cx| { + cx.emit(LspStoreEvent::RefreshInlayHints(server_id)); + lsp_store + .downstream_client + .as_ref() + .map(|(client, project_id)| { + client.send(proto::RefreshInlayHints { + project_id: *project_id, + server_id: server_id.to_proto(), + }) }) - }) })? .transpose()?; Ok(()) @@ -815,7 +822,7 @@ impl LocalLspStore { language_server .on_request::({ - let this = this.clone(); + let this = lsp_store.clone(); move |(), cx| { let this = this.clone(); let mut cx = cx.clone(); @@ -837,7 +844,7 @@ impl LocalLspStore { language_server .on_request::({ - let this = this.clone(); + let this = lsp_store.clone(); move |(), cx| { let this = this.clone(); let mut cx = cx.clone(); @@ -863,7 +870,7 @@ impl LocalLspStore { language_server .on_request::({ - let this = this.clone(); + let this = lsp_store.clone(); let name = name.to_string(); move |params, cx| { let this = this.clone(); @@ -901,7 +908,7 @@ impl LocalLspStore { .detach(); language_server .on_notification::({ - let this = this.clone(); + let this = lsp_store.clone(); let name = name.to_string(); move |params, cx| { let this = this.clone(); @@ -933,7 +940,7 @@ impl LocalLspStore { language_server .on_notification::({ - let this = this.clone(); + let this = lsp_store.clone(); move |params, cx| { if let Some(this) = this.upgrade() { this.update(cx, |this, cx| { @@ -952,7 +959,7 @@ impl LocalLspStore { language_server .on_notification::({ - let this = this.clone(); + let this = lsp_store.clone(); move |params, cx| { if let Some(this) = this.upgrade() { this.update(cx, |_, cx| { @@ -970,7 +977,7 @@ impl LocalLspStore { language_server .on_notification::({ - let this = this.clone(); + let this = lsp_store.clone(); move |params, cx| { let mut cx = cx.clone(); if let Some(this) = this.upgrade() { @@ -987,9 +994,9 @@ impl LocalLspStore { }) .detach(); - json_language_server_ext::register_requests(this.clone(), language_server); - rust_analyzer_ext::register_notifications(this.clone(), language_server); - clangd_ext::register_notifications(this, language_server, adapter); + json_language_server_ext::register_requests(lsp_store.clone(), language_server); + rust_analyzer_ext::register_notifications(lsp_store.clone(), language_server); + clangd_ext::register_notifications(lsp_store, language_server, adapter); } fn shutdown_language_servers_on_quit( @@ -3491,6 +3498,7 @@ pub struct LspStore { pub(super) lsp_server_capabilities: HashMap, lsp_document_colors: HashMap, lsp_code_lens: HashMap, + inlay_hint_data: HashMap, running_lsp_requests: HashMap>)>, } @@ -3540,7 +3548,7 @@ pub enum LspStoreEvent { new_language: Option>, }, Notification(String), - RefreshInlayHints, + RefreshInlayHints(LanguageServerId), RefreshCodeLens, DiagnosticsUpdated { server_id: LanguageServerId, @@ -3762,6 +3770,7 @@ impl LspStore { lsp_server_capabilities: HashMap::default(), lsp_document_colors: HashMap::default(), lsp_code_lens: HashMap::default(), + inlay_hint_data: HashMap::default(), running_lsp_requests: HashMap::default(), active_entry: None, _maintain_workspace_config, @@ -3824,6 +3833,7 @@ impl LspStore { lsp_server_capabilities: HashMap::default(), lsp_document_colors: HashMap::default(), lsp_code_lens: HashMap::default(), + inlay_hint_data: HashMap::default(), running_lsp_requests: HashMap::default(), active_entry: None, @@ -4125,6 +4135,7 @@ impl LspStore { if refcount == 0 { lsp_store.lsp_document_colors.remove(&buffer_id); lsp_store.lsp_code_lens.remove(&buffer_id); + lsp_store.inlay_hint_data.remove(&buffer_id); let local = lsp_store.as_local_mut().unwrap(); local.registered_buffers.remove(&buffer_id); local.buffers_opened_in_servers.remove(&buffer_id); @@ -9524,7 +9535,7 @@ impl LspStore { if let Some(work) = status.pending_work.remove(&token) && !work.is_disk_based_diagnostics_progress { - cx.emit(LspStoreEvent::RefreshInlayHints); + cx.emit(LspStoreEvent::RefreshInlayHints(language_server_id)); } cx.notify(); } @@ -9656,12 +9667,14 @@ impl LspStore { } async fn handle_refresh_inlay_hints( - this: Entity, - _: TypedEnvelope, + lsp_store: Entity, + envelope: TypedEnvelope, mut cx: AsyncApp, ) -> Result { - this.update(&mut cx, |_, cx| { - cx.emit(LspStoreEvent::RefreshInlayHints); + lsp_store.update(&mut cx, |_, cx| { + cx.emit(LspStoreEvent::RefreshInlayHints( + LanguageServerId::from_proto(envelope.payload.server_id), + )); })?; Ok(proto::Ack {}) } @@ -10913,7 +10926,7 @@ impl LspStore { language_server.name(), Some(key.worktree_id), )); - cx.emit(LspStoreEvent::RefreshInlayHints); + cx.emit(LspStoreEvent::RefreshInlayHints(server_id)); let server_capabilities = language_server.capabilities(); if let Some((downstream_client, project_id)) = self.downstream_client.as_ref() { @@ -11517,6 +11530,10 @@ impl LspStore { buffer_servers.remove(&for_server); } } + + for inlay_hint_cache in self.inlay_hint_data.values_mut() { + inlay_hint_cache.remove_server_data(for_server); + } } pub fn result_id( diff --git a/crates/project/src/lsp_store/inlay_hint_cache.rs b/crates/project/src/lsp_store/inlay_hint_cache.rs new file mode 100644 index 0000000000..d69c4c3038 --- /dev/null +++ b/crates/project/src/lsp_store/inlay_hint_cache.rs @@ -0,0 +1,57 @@ +use std::{collections::BTreeMap, ops::Range}; + +use clock::Global; +use collections::HashMap; +use futures::future::Shared; +use gpui::{Context, Entity, Task}; +use language::BufferRow; +use lsp::LanguageServerId; +use text::BufferId; + +use crate::{InlayHint, buffer_store::BufferStore}; + +#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct InlayHintId(usize); + +#[derive(Debug, Default)] +pub struct BufferInlayHints { + all_hints: HashMap, + hints: HashMap, + chunks_for_version: Global, + cache_version: usize, +} + +#[derive(Debug, Default)] +struct HintChunks { + hints_by_chunks: BTreeMap, Option>>, + chunk_updates: HashMap, Shared>>>, +} + +#[derive(Debug, PartialEq, Eq, Clone, Copy)] +pub enum HintFetchStrategy { + IgnoreCache, + UseCache { known_cache_version: Option }, +} + +impl BufferInlayHints { + pub fn remove_server_data(&mut self, for_server: LanguageServerId) -> bool { + let removed = self.hints.remove(&for_server).is_some(); + if removed { + self.cache_version += 1; + } + removed + } + + pub fn hints( + &self, + buffer_store: Entity, + buffer: BufferId, + strategy: HintFetchStrategy, + range: impl text::ToOffset, + cx: &mut Context, + ) -> Option<(Range, Shared>>)> { + todo!("TODO kb") + } + // we want to store the cache version outbound, so they can query with it: we can return nothing (`Option`) if the version matches + // we can get a new server up/down, so we want to re-query for them things, ignoring the cache version +} diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 9fd4eed641..8f4417774c 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -322,7 +322,7 @@ pub enum Event { HostReshared, Reshared, Rejoined, - RefreshInlayHints, + RefreshInlayHints(LanguageServerId), RefreshCodeLens, RevealInProjectPanel(ProjectEntryId), SnippetEdit(BufferId, Vec<(lsp::Range, Snippet)>), @@ -2922,7 +2922,9 @@ impl Project { return; }; } - LspStoreEvent::RefreshInlayHints => cx.emit(Event::RefreshInlayHints), + LspStoreEvent::RefreshInlayHints(server_id) => { + cx.emit(Event::RefreshInlayHints(*server_id)) + } LspStoreEvent::RefreshCodeLens => cx.emit(Event::RefreshCodeLens), LspStoreEvent::LanguageServerPrompt(prompt) => { cx.emit(Event::LanguageServerPrompt(prompt.clone())) diff --git a/crates/project/src/project_tests.rs b/crates/project/src/project_tests.rs index 6dcd07482e..b4f87c4afa 100644 --- a/crates/project/src/project_tests.rs +++ b/crates/project/src/project_tests.rs @@ -1804,7 +1804,10 @@ async fn test_disk_based_diagnostics_progress(cx: &mut gpui::TestAppContext) { fake_server .start_progress(format!("{}/0", progress_token)) .await; - assert_eq!(events.next().await.unwrap(), Event::RefreshInlayHints); + assert_eq!( + events.next().await.unwrap(), + Event::RefreshInlayHints(fake_server.server.server_id()) + ); assert_eq!( events.next().await.unwrap(), Event::DiskBasedDiagnosticsStarted { @@ -1943,7 +1946,10 @@ async fn test_restarting_server_with_diagnostics_running(cx: &mut gpui::TestAppC Some(worktree_id) ) ); - assert_eq!(events.next().await.unwrap(), Event::RefreshInlayHints); + assert_eq!( + events.next().await.unwrap(), + Event::RefreshInlayHints(fake_server.server.server_id()) + ); fake_server.start_progress(progress_token).await; assert_eq!( events.next().await.unwrap(), diff --git a/crates/proto/proto/lsp.proto b/crates/proto/proto/lsp.proto index 473ef5c38c..97b07ecf76 100644 --- a/crates/proto/proto/lsp.proto +++ b/crates/proto/proto/lsp.proto @@ -465,6 +465,7 @@ message ResolveInlayHintResponse { message RefreshInlayHints { uint64 project_id = 1; + uint64 server_id = 2; } message CodeLens {