From ee3245c9a594b951ff546d2fd4b12edb01020210 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 17 Jul 2025 13:11:23 +0300 Subject: [PATCH] Map the new interface # Conflicts: # crates/project/src/lsp_store.rs # Conflicts: # crates/project/src/lsp_store.rs # Conflicts: # crates/editor/src/editor.rs # crates/project/src/lsp_store.rs --- crates/editor/src/editor.rs | 15 ++-- crates/project/src/lsp_store.rs | 81 +++++++++++-------- .../project/src/lsp_store/inlay_hint_cache.rs | 56 +++++++++++++ crates/project/src/project.rs | 6 +- crates/project/src/project_tests.rs | 10 ++- crates/proto/proto/lsp.proto | 1 + 6 files changed, 127 insertions(+), 42 deletions(-) create mode 100644 crates/project/src/lsp_store/inlay_hint_cache.rs diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 29e009fdf8..a54bd68118 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -1611,7 +1611,7 @@ enum InlayHintRefreshReason { SettingsChange(InlayHintSettings), NewLinesShown, BufferEdited(HashSet>), - RefreshRequested, + RefreshRequested(LanguageServerId), ExcerptsRemoved(Vec), } @@ -1623,7 +1623,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 +1864,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(..) => { @@ -5229,7 +5232,7 @@ impl Editor { InlayHintRefreshReason::BufferEdited(buffer_languages) => { (InvalidationStrategy::BufferEdited, Some(buffer_languages)) } - InlayHintRefreshReason::RefreshRequested => { + InlayHintRefreshReason::RefreshRequested(_) => { (InvalidationStrategy::RefreshRequested, None) } }; @@ -16751,9 +16754,9 @@ impl Editor { HashSet::default(), cx, ); - cx.emit(project::Event::RefreshInlayHints); }); }); + self.refresh_inlay_hints(InlayHintRefreshReason::NewLinesShown, cx); } } diff --git a/crates/project/src/lsp_store.rs b/crates/project/src/lsp_store.rs index deebaedd74..ce3f765d24 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::InlayHintCache; 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() { @@ -988,8 +995,8 @@ 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); + 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..6b92e32c81 --- /dev/null +++ b/crates/project/src/lsp_store/inlay_hint_cache.rs @@ -0,0 +1,56 @@ +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)] +pub struct InlayHintCache { + buffer_store: Entity, + hints_for_version: Global, + hints: HashMap, + cache_version: usize, +} + +#[derive(Debug, Default)] +struct Hints { + hints: HashMap, + hints_by_chunks: BTreeMap, Option>>, + hint_updates: HashMap, Shared>>, +} + +pub struct InlayHints { + pub cache_version: usize, + pub hints: Vec, +} + +impl InlayHintCache { + 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: BufferId, + range: Range, + known_cache_version: Option, + cx: &mut Context, + ) -> Option>> { + 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 {