From d224ea86e863bf0e236950be6ff0d8d5e0a2f7bd Mon Sep 17 00:00:00 2001 From: Ben Kunkle Date: Tue, 14 Jul 2026 20:54:27 -0500 Subject: [PATCH] Add a bespoke LSP request path for edit prediction context (#60947) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes EP-193 Edit prediction context collection previously reused the editor goto-definition / goto-type-definition path, which resolves every LSP result into a `LocationLink` — opening a buffer per target (worktree creation, buffer registration, anchor conversion) before edit prediction gets a chance to filter. On remote projects the host additionally created a peer-visible buffer per link and the client waited on each one. This adds a bespoke request path that returns raw results first and only does the expensive work for results that survive filtering: - New `EditPredictionDefinition { path: ProjectPath, range: Range> }` boundary type. LSP wire types never leave `lsp_command.rs`; the workspace-only filter and URI→`ProjectPath` resolution happen while normalizing the LSP response, before any buffer exists, so the type encodes the workspace-only invariant. - New `GetEditPredictionDefinitions` / `GetEditPredictionTypeDefinitions` LSP commands and proto messages. Responses carry only path + UTF-16 range — the host never calls `create_buffer_for_peer` and the client never waits on remote buffers. - One public API: `Project::edit_prediction_definitions(buffer, position, include_type_definitions, cx)` fires both LSP requests concurrently and returns a merged, deduped list ("not capable" = empty). The definition/type-definition split was never used downstream, so `CacheEntry` now holds a single list and the fetch pipeline runs one task per identifier. - `edit_prediction_context` dedupes raw results before opening buffers; survivors open via `project.open_buffer(ProjectPath)` (skipping the invisible-worktree/yarn machinery of `open_local_buffer_via_lsp`, and working identically on remote projects), then clip → anchor → `MAX_TARGET_LEN`. - Removes the `workspace_only` flag from `GetDefinitions` / `GetTypeDefinitions`: edit prediction was its only user. The editor path always sent `false`, which proto3 doesn't encode, so normal goto-definition requests are wire-identical; old peers still sending `true` get unfiltered results (graceful degradation). The proto field numbers are `reserved`. Tested with a local test proving filtering precedes buffer opening (an out-of-workspace target never spawns a worktree, an oversized target is excluded from related files) and a collab test proving the remote round-trip returns correct paths/ranges without opening buffers on the client. Release Notes: - N/A --- .../tests/integration/integration_tests.rs | 103 +++++ .../src/edit_prediction_context.rs | 149 +++---- .../src/edit_prediction_context_tests.rs | 89 +++- .../src/fake_definition_lsp.rs | 34 ++ crates/project/src/lsp_command.rs | 412 +++++++++++++++--- crates/project/src/lsp_store.rs | 203 ++++++--- .../project/src/lsp_store/lsp_ext_command.rs | 1 - crates/project/src/project.rs | 34 +- crates/proto/proto/lsp.proto | 38 +- crates/proto/proto/zed.proto | 6 +- crates/proto/src/proto.rs | 30 ++ crates/rpc/src/proto_client.rs | 6 + 12 files changed, 847 insertions(+), 258 deletions(-) diff --git a/crates/collab/tests/integration/integration_tests.rs b/crates/collab/tests/integration/integration_tests.rs index 38cac226ba..403e36487b 100644 --- a/crates/collab/tests/integration/integration_tests.rs +++ b/crates/collab/tests/integration/integration_tests.rs @@ -5139,6 +5139,109 @@ async fn test_definition( }); } +#[gpui::test] +async fn test_edit_prediction_definition( + executor: BackgroundExecutor, + cx_a: &mut TestAppContext, + cx_b: &mut TestAppContext, +) { + let mut server = TestServer::start(executor.clone()).await; + let client_a = server.create_client(cx_a, "user_a").await; + let client_b = server.create_client(cx_b, "user_b").await; + server + .create_room(&mut [(&client_a, cx_a), (&client_b, cx_b)]) + .await; + let active_call_a = cx_a.read(ActiveCall::global); + + let capabilities = lsp::ServerCapabilities { + definition_provider: Some(OneOf::Left(true)), + ..lsp::ServerCapabilities::default() + }; + client_a.language_registry().add(rust_lang()); + let mut fake_language_servers = client_a.language_registry().register_fake_lsp( + "Rust", + FakeLspAdapter { + capabilities: capabilities.clone(), + ..FakeLspAdapter::default() + }, + ); + client_b.language_registry().add(rust_lang()); + client_b.language_registry().register_fake_lsp_adapter( + "Rust", + FakeLspAdapter { + capabilities, + ..FakeLspAdapter::default() + }, + ); + + client_a + .fs() + .insert_tree( + path!("/root"), + json!({ + "a.rs": "const ONE: usize = TWO;", + "b.rs": "const TWO: usize = 2;", + }), + ) + .await; + let (project_a, worktree_id) = client_a.build_local_project(path!("/root"), cx_a).await; + let project_id = active_call_a + .update(cx_a, |call, cx| call.share_project(project_a.clone(), cx)) + .await + .unwrap(); + let project_b = client_b.join_remote_project(project_id, cx_b).await; + + let (buffer_b, _handle) = project_b + .update(cx_b, |project, cx| { + project.open_buffer_with_lsp((worktree_id, rel_path("a.rs")), cx) + }) + .await + .unwrap(); + + let fake_language_server = fake_language_servers.next().await.unwrap(); + fake_language_server.set_request_handler::( + |_, _| async move { + Ok(Some(lsp::GotoDefinitionResponse::Scalar( + lsp::Location::new( + lsp::Uri::from_file_path(path!("/root/b.rs")).unwrap(), + lsp::Range::new(lsp::Position::new(0, 6), lsp::Position::new(0, 9)), + ), + ))) + }, + ); + cx_a.run_until_parked(); + cx_b.run_until_parked(); + + let definitions = project_b + .update(cx_b, |project, cx| { + project.edit_prediction_definitions(&buffer_b, 19, false, cx) + }) + .await + .unwrap(); + + cx_b.read(|cx| { + assert_eq!(definitions.len(), 1); + assert_eq!( + definitions[0].path, + ProjectPath { + worktree_id, + path: rel_path("b.rs").into(), + } + ); + assert_eq!( + definitions[0].range.start.0, + language::PointUtf16::new(0, 6) + ); + assert_eq!(definitions[0].range.end.0, language::PointUtf16::new(0, 9)); + assert!( + project_b + .read(cx) + .get_open_buffer(&definitions[0].path, cx) + .is_none() + ); + }); +} + #[gpui::test(iterations = 10)] async fn test_references( executor: BackgroundExecutor, diff --git a/crates/edit_prediction_context/src/edit_prediction_context.rs b/crates/edit_prediction_context/src/edit_prediction_context.rs index 3069c13b65..b5ab87a173 100644 --- a/crates/edit_prediction_context/src/edit_prediction_context.rs +++ b/crates/edit_prediction_context/src/edit_prediction_context.rs @@ -5,8 +5,8 @@ use futures::{FutureExt, StreamExt as _, channel::mpsc, future}; use gpui::{ App, AppContext, AsyncApp, Context, Entity, EntityId, EventEmitter, Task, TaskExt, WeakEntity, }; -use language::{Anchor, Buffer, BufferSnapshot, OffsetRangeExt as _, Point, ToOffset as _}; -use project::{LocationLink, Project, ProjectPath}; +use language::{Anchor, Bias, Buffer, BufferSnapshot, OffsetRangeExt as _, Point, ToOffset as _}; +use project::{EditPredictionDefinition, Project, ProjectPath}; use smallvec::SmallVec; use std::{ collections::hash_map, @@ -77,20 +77,15 @@ struct Identifier { enum DefinitionTask { CacheHit(Arc), - CacheMiss( - Task< - Option<( - Task>>>, - Task>>>, - )>, - >, - ), + CacheMiss { + project: WeakEntity, + task: Task>>, + }, } #[derive(Debug)] struct CacheEntry { definitions: SmallVec<[CachedDefinition; 1]>, - type_definitions: SmallVec<[CachedDefinition; 1]>, } #[derive(Clone, Debug)] @@ -312,34 +307,21 @@ impl RelatedExcerptStore { DefinitionTask::CacheHit(entry.clone()) } else { let project = this.project.clone(); - let buffer = buffer.downgrade(); - DefinitionTask::CacheMiss(cx.spawn(async move |_, cx| { - let buffer = buffer.upgrade()?; - let definitions = project - .update(cx, |project, cx| { - project.workspace_definitions( - &buffer, - identifier.range.start, - cx, - ) - }) - .ok()?; - let type_definitions = project - .update(cx, |project, cx| { - // tombi LSP for toml will open a scratch buffer with the JSON schema of - // the toml file when a goto type definition is requested - if is_tombi_lsp_in_toml(project, &buffer, cx) { - return Task::ready(Ok(None)); - } - project.workspace_type_definitions( - &buffer, - identifier.range.start, - cx, - ) - }) - .ok()?; - Some((definitions, type_definitions)) - })) + let task = project + .update(cx, |project, cx| { + // tombi LSP for toml will open a scratch buffer with the JSON schema of + // the toml file when a goto type definition is requested + let include_type_definitions = + !is_tombi_lsp_in_toml(project, &buffer, cx); + project.edit_prediction_definitions( + &buffer, + identifier.range.start, + include_type_definitions, + cx, + ) + }) + .unwrap_or_else(|_| Task::ready(Ok(Vec::new()))); + DefinitionTask::CacheMiss { project, task } }; let cx = async_cx.clone(); @@ -348,50 +330,29 @@ impl RelatedExcerptStore { DefinitionTask::CacheHit(cache_entry) => { Some((identifier, cache_entry, None)) } - DefinitionTask::CacheMiss(task) => { - let (definitions, type_definitions) = task.await?; - let (definition_locations, type_definition_locations) = - futures::join!(definitions, type_definitions); + DefinitionTask::CacheMiss { project, task } => { + let definition_locations = task.await.log_err().unwrap_or_default(); let duration = start_time.elapsed(); - let definition_locations = - definition_locations.log_err().flatten().unwrap_or_default(); - let type_definition_locations = type_definition_locations - .log_err() - .flatten() - .unwrap_or_default(); - let definitions: SmallVec<[CachedDefinition; 1]> = - definition_locations - .into_iter() - .filter_map(|location| { + future::join_all(definition_locations.into_iter().map( + |definition| { + let project = project.clone(); let mut cx = cx.clone(); - process_definition(location, &mut cx) - }) - .collect(); - - let type_definitions: SmallVec<[CachedDefinition; 1]> = - type_definition_locations - .into_iter() - .filter_map(|location| { - let mut cx = cx.clone(); - process_definition(location, &mut cx) - }) - .filter(|type_def| { - !definitions.iter().any(|def| { - def.buffer.entity_id() - == type_def.buffer.entity_id() - && def.anchor_range == type_def.anchor_range - }) - }) - .collect(); + async move { + process_definition(definition, &project, &mut cx) + .await + } + }, + )) + .await + .into_iter() + .flatten() + .collect(); Some(( identifier, - Arc::new(CacheEntry { - definitions, - type_definitions, - }), + Arc::new(CacheEntry { definitions }), Some(duration), )) } @@ -469,11 +430,7 @@ async fn rebuild_related_files( let mut snapshots = HashMap::default(); let mut worktree_root_names = HashMap::default(); for entry in new_entries.values() { - for definition in entry - .definitions - .iter() - .chain(entry.type_definitions.iter()) - { + for definition in entry.definitions.iter() { if let hash_map::Entry::Vacant(e) = snapshots.entry(definition.buffer.entity_id()) { definition .buffer @@ -510,11 +467,7 @@ async fn rebuild_related_files( .get(identifier) .copied() .unwrap_or(usize::MAX); - for definition in entry - .definitions - .iter() - .chain(entry.type_definitions.iter()) - { + for definition in entry.definitions.iter() { let Some(snapshot) = snapshots.get(&definition.buffer.entity_id()) else { continue; }; @@ -635,16 +588,24 @@ use language::ToPoint as _; const MAX_TARGET_LEN: usize = 128; -fn process_definition(location: LocationLink, cx: &mut AsyncApp) -> Option { +async fn process_definition( + definition: EditPredictionDefinition, + project: &WeakEntity, + cx: &mut AsyncApp, +) -> Option { + let EditPredictionDefinition { path, range } = definition; + let buffer = project + .update(cx, |project, cx| project.open_buffer(path.clone(), cx)) + .ok()? + .await + .log_err()?; + cx.update(|cx| { - let buffer = location.target.buffer; let buffer_snapshot = buffer.read(cx); - let file = buffer_snapshot.file()?; - let path = ProjectPath { - worktree_id: file.worktree_id(cx), - path: file.path().clone(), - }; - let anchor_range = location.target.range; + let target_start = buffer_snapshot.clip_point_utf16(range.start, Bias::Left); + let target_end = buffer_snapshot.clip_point_utf16(range.end, Bias::Left); + let anchor_range = + buffer_snapshot.anchor_after(target_start)..buffer_snapshot.anchor_before(target_end); // If the target range is large, it likely means we requested the definition of an entire module. // For individual definitions, the target range should be small as it only covers the symbol. diff --git a/crates/edit_prediction_context/src/edit_prediction_context_tests.rs b/crates/edit_prediction_context/src/edit_prediction_context_tests.rs index 5b8e9c6aec..7c98f7a992 100644 --- a/crates/edit_prediction_context/src/edit_prediction_context_tests.rs +++ b/crates/edit_prediction_context/src/edit_prediction_context_tests.rs @@ -5,10 +5,11 @@ use gpui::TestAppContext; use indoc::indoc; use language::{Point, ToPoint as _, rust_lang}; use lsp::FakeLanguageServer; -use project::{FakeFs, LocationLink, Project}; +use project::{FakeFs, LocationLink, Project, ProjectPath}; use serde_json::json; use settings::SettingsStore; use std::fmt::Write as _; +use util::rel_path::rel_path; use util::{path, test::marked_text_ranges}; #[gpui::test] @@ -561,9 +562,10 @@ async fn test_type_definition_deduplication(cx: &mut TestAppContext) { // In this project the only identifier near the cursor whose type definition // resolves is `TypeA`, and its GotoTypeDefinition returns the exact same - // location as GotoDefinition. After deduplication the CacheEntry for `TypeA` - // should have an empty `type_definitions` vec, meaning the type-definition - // path contributes nothing extra to the related-file output. + // location as GotoDefinition. After the definitions and type definitions are + // merged and deduped, the type-definition location is dropped, so it + // contributes nothing extra to the related-file output (the target location + // appears only once). fs.insert_tree( path!("/root"), json!({ @@ -638,6 +640,85 @@ async fn test_type_definition_deduplication(cx: &mut TestAppContext) { }); } +#[gpui::test] +async fn test_edit_prediction_filters_raw_definitions_before_opening_buffers( + cx: &mut TestAppContext, +) { + init_test(cx); + let fs = FakeFs::new(cx.executor()); + fs.insert_tree( + path!("/root"), + json!({ + "src": { + "main.rs": indoc! {" + // fake-definition-lsp-extra target /root/src/large.rs 0 0 0 129 + // fake-definition-lsp-extra target /root/src/valid.rs 0 3 0 9 + // fake-definition-lsp-extra target /outside.rs 0 3 0 10 + fn main() { + target(); + } + "}, + "valid.rs": "fn target() {}\n", + "large.rs": format!("{}\n", "a".repeat(MAX_TARGET_LEN + 16)), + }, + }), + ) + .await; + fs.insert_file(path!("/outside.rs"), "fn outside() {}\n".into()) + .await; + + let project = Project::test(fs.clone(), [path!("/root").as_ref()], cx).await; + let mut servers = setup_fake_lsp(&project, cx); + + let (buffer, _handle) = project + .update(cx, |project, cx| { + project.open_local_buffer_with_lsp(path!("/root/src/main.rs"), cx) + }) + .await + .unwrap(); + + let _fake_language_server = servers.next().await.unwrap(); + cx.run_until_parked(); + + let related_excerpt_store = cx.new(|cx| RelatedExcerptStore::new(&project, cx)); + related_excerpt_store.update(cx, |store, cx| { + let position = { + let buffer = buffer.read(cx); + let offset = buffer + .text() + .find("target();") + .expect("target call not found"); + buffer.anchor_before(offset) + }; + + store.set_identifier_line_count(0); + store.refresh(buffer.clone(), position, cx); + }); + + cx.executor().advance_clock(DEBOUNCE_DURATION); + related_excerpt_store.update(cx, |store, cx| { + assert_related_files( + &store.related_files(cx), + &[ + ("root/src/valid.rs", &["fn target() {}"]), + ("root/src/main.rs", &["fn main() {\n target();\n}"]), + ], + ); + }); + + let worktree_id = buffer.read_with(cx, |buffer, cx| { + buffer.file().expect("buffer has file").worktree_id(cx) + }); + let valid_path = ProjectPath { + worktree_id, + path: rel_path("src/valid.rs").into(), + }; + project.read_with(cx, |project, cx| { + assert!(project.get_open_buffer(&valid_path, cx).is_some()); + assert_eq!(project.worktrees(cx).count(), 1); + }); +} + #[gpui::test] async fn test_definitions_ranked_by_cursor_proximity(cx: &mut TestAppContext) { init_test(cx); diff --git a/crates/edit_prediction_context/src/fake_definition_lsp.rs b/crates/edit_prediction_context/src/fake_definition_lsp.rs index 5b9e528b63..d844597ef5 100644 --- a/crates/edit_prediction_context/src/fake_definition_lsp.rs +++ b/crates/edit_prediction_context/src/fake_definition_lsp.rs @@ -243,6 +243,12 @@ impl DefinitionIndex { .or_insert_with(Vec::new) .push(location); } + for (name, location) in extract_extra_definition_locations(content) { + self.definitions + .entry(name) + .or_insert_with(Vec::new) + .push(location); + } let type_annotations = extract_type_annotations(content) .into_iter() @@ -303,6 +309,34 @@ impl DefinitionIndex { } } +fn extract_extra_definition_locations(content: &str) -> Vec<(String, lsp::Location)> { + content + .lines() + .filter_map(|line| { + let mut parts = line + .trim() + .strip_prefix("// fake-definition-lsp-extra ")? + .split_whitespace(); + let name = parts.next()?.to_string(); + let path = PathBuf::from(parts.next()?); + let start_row = parts.next()?.parse().ok()?; + let start_column = parts.next()?.parse().ok()?; + let end_row = parts.next()?.parse().ok()?; + let end_column = parts.next()?.parse().ok()?; + Some(( + name, + lsp::Location::new( + Uri::from_file_path(path).ok()?, + lsp::Range::new( + lsp::Position::new(start_row, start_column), + lsp::Position::new(end_row, end_column), + ), + ), + )) + }) + .collect() +} + /// Extracts `identifier_name -> type_name` mappings from field declarations /// and function parameters. For example, `owner: Arc` produces /// `"owner" -> "Person"` by unwrapping common generic wrappers. diff --git a/crates/project/src/lsp_command.rs b/crates/project/src/lsp_command.rs index 2f05aba49b..bdc4189158 100644 --- a/crates/project/src/lsp_command.rs +++ b/crates/project/src/lsp_command.rs @@ -4,7 +4,7 @@ use crate::{ CodeAction, CompletionSource, CoreCompletion, CoreCompletionResponse, DocumentColor, DocumentHighlight, DocumentSymbol, Hover, HoverBlock, HoverBlockKind, InlayHint, InlayHintLabel, InlayHintLabelPart, InlayHintLabelPartTooltip, InlayHintTooltip, Location, - LocationLink, LspAction, LspPullDiagnostics, MarkupContent, PrepareRenameResponse, + LocationLink, LspAction, LspPullDiagnostics, MarkupContent, PrepareRenameResponse, ProjectPath, ProjectTransaction, PulledDiagnostics, ResolveState, lsp_store::{LocalLspStore, LspDocumentLink, LspFoldingRange, LspStore}, }; @@ -39,6 +39,7 @@ use std::{ cmp::Reverse, collections::hash_map, mem, ops::Range, path::Path, str::FromStr, sync::Arc, }; use text::{BufferId, LineEnding}; +use util::rel_path::RelPath; use util::{ResultExt as _, debug_panic}; pub use signature_help::SignatureHelp; @@ -189,7 +190,17 @@ pub(crate) struct PerformRename { #[derive(Debug, Clone, Copy)] pub struct GetDefinitions { pub position: PointUtf16, - pub workspace_only: bool, +} + +#[derive(Debug, Clone, PartialEq, Eq, Hash)] +pub struct EditPredictionDefinition { + pub path: ProjectPath, + pub range: Range>, +} + +#[derive(Debug, Clone, Copy)] +pub(crate) struct GetEditPredictionDefinitions { + pub position: PointUtf16, } #[derive(Debug, Clone, Copy)] @@ -200,7 +211,11 @@ pub(crate) struct GetDeclarations { #[derive(Debug, Clone, Copy)] pub(crate) struct GetTypeDefinitions { pub position: PointUtf16, - pub workspace_only: bool, +} + +#[derive(Debug, Clone, Copy)] +pub(crate) struct GetEditPredictionTypeDefinitions { + pub position: PointUtf16, } #[derive(Debug, Clone, Copy)] @@ -694,15 +709,7 @@ impl LspCommand for GetDefinitions { server_id: LanguageServerId, cx: AsyncApp, ) -> Result> { - location_links_from_lsp( - message, - lsp_store, - buffer, - server_id, - self.workspace_only, - cx, - ) - .await + location_links_from_lsp(message, lsp_store, buffer, server_id, cx).await } fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDefinition { @@ -713,7 +720,6 @@ impl LspCommand for GetDefinitions { &buffer.anchor_before(self.position), )), version: serialize_version(&buffer.version()), - workspace_only: self.workspace_only, } } @@ -734,7 +740,6 @@ impl LspCommand for GetDefinitions { .await?; Ok(Self { position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)), - workspace_only: message.workspace_only, }) } @@ -764,6 +769,106 @@ impl LspCommand for GetDefinitions { } } +#[async_trait(?Send)] +impl LspCommand for GetEditPredictionDefinitions { + type Response = Vec; + type LspRequest = lsp::request::GotoDefinition; + type ProtoRequest = proto::GetEditPredictionDefinition; + + fn display_name(&self) -> &str { + "Get edit prediction definition" + } + + fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool { + capabilities + .server_capabilities + .definition_provider + .is_some_and(|capability| match capability { + OneOf::Left(supported) => supported, + OneOf::Right(_options) => true, + }) + } + + fn to_lsp( + &self, + path: &Path, + _: &Buffer, + _: &Arc, + _: &App, + ) -> Result { + Ok(lsp::GotoDefinitionParams { + text_document_position_params: make_lsp_text_document_position(path, self.position)?, + work_done_progress_params: Default::default(), + partial_result_params: Default::default(), + }) + } + + async fn response_from_lsp( + self, + message: Option, + lsp_store: Entity, + _: Entity, + _: LanguageServerId, + cx: AsyncApp, + ) -> Result> { + edit_prediction_definitions_from_lsp(message, lsp_store, cx) + } + + fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetEditPredictionDefinition { + proto::GetEditPredictionDefinition { + project_id, + buffer_id: buffer.remote_id().into(), + position: Some(language::proto::serialize_anchor( + &buffer.anchor_before(self.position), + )), + version: serialize_version(&buffer.version()), + } + } + + async fn from_proto( + message: proto::GetEditPredictionDefinition, + _: Entity, + buffer: Entity, + mut cx: AsyncApp, + ) -> Result { + Ok(Self { + position: edit_prediction_position_from_proto( + message.position, + message.version, + buffer, + &mut cx, + ) + .await?, + }) + } + + fn response_to_proto( + response: Vec, + _: &mut LspStore, + _: PeerId, + _: &clock::Global, + _: &mut App, + ) -> proto::GetEditPredictionDefinitionResponse { + proto::GetEditPredictionDefinitionResponse { + definitions: edit_prediction_definitions_to_proto(response), + } + } + + async fn response_from_proto( + self, + message: proto::GetEditPredictionDefinitionResponse, + _: Entity, + _: Entity, + _: AsyncApp, + ) -> Result> { + edit_prediction_definitions_from_proto(message.definitions) + } + + fn buffer_id_from_proto(message: &proto::GetEditPredictionDefinition) -> Result { + BufferId::new(message.buffer_id) + } +} + #[async_trait(?Send)] impl LspCommand for GetDeclarations { type Response = Vec; @@ -807,7 +912,7 @@ impl LspCommand for GetDeclarations { server_id: LanguageServerId, cx: AsyncApp, ) -> Result> { - location_links_from_lsp(message, lsp_store, buffer, server_id, false, cx).await + location_links_from_lsp(message, lsp_store, buffer, server_id, cx).await } fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetDeclaration { @@ -909,7 +1014,7 @@ impl LspCommand for GetImplementations { server_id: LanguageServerId, cx: AsyncApp, ) -> Result> { - location_links_from_lsp(message, lsp_store, buffer, server_id, false, cx).await + location_links_from_lsp(message, lsp_store, buffer, server_id, cx).await } fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetImplementation { @@ -1008,7 +1113,7 @@ impl LspCommand for GetTypeDefinitions { server_id: LanguageServerId, cx: AsyncApp, ) -> Result> { - location_links_from_lsp(message, project, buffer, server_id, self.workspace_only, cx).await + location_links_from_lsp(message, project, buffer, server_id, cx).await } fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetTypeDefinition { @@ -1019,7 +1124,6 @@ impl LspCommand for GetTypeDefinitions { &buffer.anchor_before(self.position), )), version: serialize_version(&buffer.version()), - workspace_only: self.workspace_only, } } @@ -1040,7 +1144,6 @@ impl LspCommand for GetTypeDefinitions { .await?; Ok(Self { position: buffer.read_with(&cx, |buffer, _| position.to_point_utf16(buffer)), - workspace_only: message.workspace_only, }) } @@ -1070,6 +1173,103 @@ impl LspCommand for GetTypeDefinitions { } } +#[async_trait(?Send)] +impl LspCommand for GetEditPredictionTypeDefinitions { + type Response = Vec; + type LspRequest = lsp::request::GotoTypeDefinition; + type ProtoRequest = proto::GetEditPredictionTypeDefinition; + + fn display_name(&self) -> &str { + "Get edit prediction type definition" + } + + fn check_capabilities(&self, capabilities: AdapterServerCapabilities) -> bool { + !matches!( + &capabilities.server_capabilities.type_definition_provider, + None | Some(lsp::TypeDefinitionProviderCapability::Simple(false)) + ) + } + + fn to_lsp( + &self, + path: &Path, + _: &Buffer, + _: &Arc, + _: &App, + ) -> Result { + Ok(lsp::GotoTypeDefinitionParams { + text_document_position_params: make_lsp_text_document_position(path, self.position)?, + work_done_progress_params: Default::default(), + partial_result_params: Default::default(), + }) + } + + async fn response_from_lsp( + self, + message: Option, + lsp_store: Entity, + _: Entity, + _: LanguageServerId, + cx: AsyncApp, + ) -> Result> { + edit_prediction_definitions_from_lsp(message, lsp_store, cx) + } + + fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetEditPredictionTypeDefinition { + proto::GetEditPredictionTypeDefinition { + project_id, + buffer_id: buffer.remote_id().into(), + position: Some(language::proto::serialize_anchor( + &buffer.anchor_before(self.position), + )), + version: serialize_version(&buffer.version()), + } + } + + async fn from_proto( + message: proto::GetEditPredictionTypeDefinition, + _: Entity, + buffer: Entity, + mut cx: AsyncApp, + ) -> Result { + Ok(Self { + position: edit_prediction_position_from_proto( + message.position, + message.version, + buffer, + &mut cx, + ) + .await?, + }) + } + + fn response_to_proto( + response: Vec, + _: &mut LspStore, + _: PeerId, + _: &clock::Global, + _: &mut App, + ) -> proto::GetEditPredictionTypeDefinitionResponse { + proto::GetEditPredictionTypeDefinitionResponse { + definitions: edit_prediction_definitions_to_proto(response), + } + } + + async fn response_from_proto( + self, + message: proto::GetEditPredictionTypeDefinitionResponse, + _: Entity, + _: Entity, + _: AsyncApp, + ) -> Result> { + edit_prediction_definitions_from_proto(message.definitions) + } + + fn buffer_id_from_proto(message: &proto::GetEditPredictionTypeDefinition) -> Result { + BufferId::new(message.buffer_id) + } +} + fn language_server_for_buffer( lsp_store: &Entity, buffer: &Entity, @@ -1165,57 +1365,13 @@ pub async fn location_links_from_lsp( lsp_store: Entity, buffer: Entity, server_id: LanguageServerId, - workspace_only: bool, mut cx: AsyncApp, ) -> Result> { - let message = match message { - Some(message) => message, - None => return Ok(Vec::new()), - }; - - let mut unresolved_links = Vec::new(); - match message { - lsp::GotoDefinitionResponse::Scalar(loc) => { - unresolved_links.push((None, loc.uri, loc.range)); - } - - lsp::GotoDefinitionResponse::Array(locs) => { - unresolved_links.extend(locs.into_iter().map(|l| (None, l.uri, l.range))); - } - - lsp::GotoDefinitionResponse::Link(links) => { - unresolved_links.extend(links.into_iter().map(|l| { - ( - l.origin_selection_range, - l.target_uri, - l.target_selection_range, - ) - })); - } - } + let unresolved_links = definition_locations_from_lsp(message); let (_, language_server) = language_server_for_buffer(&lsp_store, &buffer, server_id, &mut cx)?; let mut definitions = Vec::new(); for (origin_range, target_uri, target_range) in unresolved_links { - if workspace_only - && !lsp_store.update(&mut cx, |this, cx| { - use util::paths::UrlExt as _; - let worktree_store = this.worktree_store().read(cx); - let path_style = worktree_store.path_style(); - let Ok(abs_path) = target_uri.clone().to_file_path_ext(path_style) else { - return false; - }; - worktree_store - .find_worktree(&abs_path, cx) - .is_some_and(|(worktree, _)| { - let worktree = worktree.read(cx); - worktree.is_visible() && !worktree.is_single_file() - }) - }) - { - continue; - } - let target_buffer_handle = lsp_store .update(&mut cx, |this, cx| { this.open_local_buffer_via_lsp(target_uri, language_server.server_id(), cx) @@ -1256,6 +1412,94 @@ pub async fn location_links_from_lsp( Ok(definitions) } +fn definition_locations_from_lsp( + message: Option, +) -> Vec<(Option, lsp::Uri, lsp::Range)> { + let Some(message) = message else { + return Vec::new(); + }; + + let mut locations = Vec::new(); + match message { + lsp::GotoDefinitionResponse::Scalar(location) => { + locations.push((None, location.uri, location.range)); + } + + lsp::GotoDefinitionResponse::Array(locations_from_lsp) => { + locations.extend( + locations_from_lsp + .into_iter() + .map(|location| (None, location.uri, location.range)), + ); + } + + lsp::GotoDefinitionResponse::Link(links) => { + locations.extend(links.into_iter().map(|link| { + ( + link.origin_selection_range, + link.target_uri, + link.target_selection_range, + ) + })); + } + } + locations +} + +fn edit_prediction_definitions_from_lsp( + message: Option, + lsp_store: Entity, + mut cx: AsyncApp, +) -> Result> { + let unresolved_locations = definition_locations_from_lsp(message); + lsp_store.update(&mut cx, |lsp_store, cx| { + use util::paths::UrlExt as _; + let mut definitions = Vec::new(); + let worktree_store = lsp_store.worktree_store().read(cx); + let path_style = worktree_store.path_style(); + + for (_, uri, range) in unresolved_locations { + let Ok(abs_path) = uri.to_file_path_ext(path_style) else { + continue; + }; + let Some((worktree, relative_path)) = worktree_store.find_worktree(&abs_path, cx) + else { + continue; + }; + let worktree = worktree.read(cx); + if !worktree.is_visible() || worktree.is_single_file() { + continue; + } + definitions.push(EditPredictionDefinition { + path: ProjectPath { + worktree_id: worktree.id(), + path: relative_path, + }, + range: point_from_lsp(range.start)..point_from_lsp(range.end), + }); + } + + Ok(definitions) + }) +} + +async fn edit_prediction_position_from_proto( + position: Option, + version: Vec, + buffer: Entity, + cx: &mut AsyncApp, +) -> Result { + let position = position + .and_then(deserialize_anchor) + .context("invalid position")?; + buffer + .update(cx, |buffer, _| { + buffer.wait_for_version(deserialize_version(&version)) + }) + .await?; + Ok(buffer.read_with(cx, |buffer, _| position.to_point_utf16(buffer))) +} + pub async fn location_link_from_lsp( link: lsp::LocationLink, lsp_store: &Entity, @@ -1363,6 +1607,46 @@ pub fn location_link_to_proto( } } +fn edit_prediction_definitions_to_proto( + definitions: Vec, +) -> Vec { + definitions + .into_iter() + .map(|definition| proto::EditPredictionDefinition { + worktree_id: definition.path.worktree_id.to_proto(), + path: definition.path.path.as_ref().to_proto(), + start: Some(proto::PointUtf16 { + row: definition.range.start.0.row, + column: definition.range.start.0.column, + }), + end: Some(proto::PointUtf16 { + row: definition.range.end.0.row, + column: definition.range.end.0.column, + }), + }) + .collect() +} + +fn edit_prediction_definitions_from_proto( + definitions: Vec, +) -> Result> { + definitions + .into_iter() + .map(|definition| { + let start = definition.start.context("missing definition start")?; + let end = definition.end.context("missing definition end")?; + Ok(EditPredictionDefinition { + path: ProjectPath { + worktree_id: worktree::WorktreeId::from_proto(definition.worktree_id), + path: RelPath::from_proto(&definition.path).context("invalid path")?, + }, + range: Unclipped(PointUtf16::new(start.row, start.column)) + ..Unclipped(PointUtf16::new(end.row, end.column)), + }) + }) + .collect() +} + #[async_trait(?Send)] impl LspCommand for GetReferences { type Response = Vec; diff --git a/crates/project/src/lsp_store.rs b/crates/project/src/lsp_store.rs index 0500d15c33..71d5b30cf8 100644 --- a/crates/project/src/lsp_store.rs +++ b/crates/project/src/lsp_store.rs @@ -6171,31 +6171,9 @@ impl LspStore { buffer: &Entity, position: PointUtf16, cx: &mut Context, - ) -> Task>>> { - self.definitions_with_filter(buffer, position, false, cx) - } - - pub fn workspace_definitions( - &mut self, - buffer: &Entity, - position: PointUtf16, - cx: &mut Context, - ) -> Task>>> { - self.definitions_with_filter(buffer, position, true, cx) - } - - fn definitions_with_filter( - &mut self, - buffer: &Entity, - position: PointUtf16, - workspace_only: bool, - cx: &mut Context, ) -> Task>>> { if let Some((upstream_client, project_id)) = self.upstream_client() { - let request = GetDefinitions { - position, - workspace_only, - }; + let request = GetDefinitions { position }; if !self.is_capable_for_proto_request(buffer, &request, cx) { return Task::ready(Ok(None)); } @@ -6220,11 +6198,7 @@ impl LspStore { return Ok(None); }; let actions = join_all(responses.payload.into_iter().map(|response| { - GetDefinitions { - position, - workspace_only, - } - .response_from_proto( + GetDefinitions { position }.response_from_proto( response.response, lsp_store.clone(), buffer.clone(), @@ -6247,10 +6221,7 @@ impl LspStore { let definitions_task = self.request_multiple_lsp_locally( buffer, Some(position), - GetDefinitions { - position, - workspace_only, - }, + GetDefinitions { position }, cx, ); cx.background_spawn(async move { @@ -6266,6 +6237,107 @@ impl LspStore { } } + fn edit_prediction_definitions_for_command( + &mut self, + buffer: &Entity, + request: C, + position: PointUtf16, + cx: &mut Context, + ) -> Task>> + where + C: LspCommand> + Clone, + C::ProtoRequest: proto::LspRequestMessage, + ::Response: + Into<::Response>, + ::Result: Send, + ::Params: Send, + { + if let Some((upstream_client, project_id)) = self.upstream_client() { + if !self.is_capable_for_proto_request(buffer, &request, cx) { + return Task::ready(Ok(Vec::new())); + } + + let request_timeout = ProjectSettings::get_global(cx) + .global_lsp_settings + .get_request_timeout(); + + let request_task = upstream_client.request_lsp( + project_id, + None, + request_timeout, + cx.background_executor().clone(), + request.to_proto(project_id, buffer.read(cx)), + ); + let buffer = buffer.clone(); + cx.spawn(async move |weak_lsp_store, cx| { + let Some(lsp_store) = weak_lsp_store.upgrade() else { + return Ok(Vec::new()); + }; + let Some(responses) = request_task.await? else { + return Ok(Vec::new()); + }; + let actions = join_all(responses.payload.into_iter().map(|response| { + request.clone().response_from_proto( + response.response.into(), + lsp_store.clone(), + buffer.clone(), + cx.clone(), + ) + })) + .await; + + Ok(actions + .into_iter() + .collect::>>>()? + .into_iter() + .flatten() + .collect()) + }) + } else { + let definitions_task = + self.request_multiple_lsp_locally(buffer, Some(position), request, cx); + cx.background_spawn(async move { + Ok(definitions_task + .await + .into_iter() + .flat_map(|(_, definitions)| definitions) + .collect()) + }) + } + } + + pub fn edit_prediction_definitions( + &mut self, + buffer: &Entity, + position: PointUtf16, + include_type_definitions: bool, + cx: &mut Context, + ) -> Task>> { + let definitions = self.edit_prediction_definitions_for_command( + buffer, + GetEditPredictionDefinitions { position }, + position, + cx, + ); + let type_definitions = include_type_definitions.then(|| { + self.edit_prediction_definitions_for_command( + buffer, + GetEditPredictionTypeDefinitions { position }, + position, + cx, + ) + }); + cx.background_spawn(async move { + let mut merged = definitions.await?; + if let Some(type_definitions) = type_definitions { + merged.extend(type_definitions.await?); + } + let mut seen = HashSet::default(); + merged.retain(|definition| seen.insert(definition.clone())); + Ok(merged) + }) + } + pub fn declarations( &mut self, buffer: &Entity, @@ -6340,31 +6412,9 @@ impl LspStore { buffer: &Entity, position: PointUtf16, cx: &mut Context, - ) -> Task>>> { - self.type_definitions_with_filter(buffer, position, false, cx) - } - - pub fn workspace_type_definitions( - &mut self, - buffer: &Entity, - position: PointUtf16, - cx: &mut Context, - ) -> Task>>> { - self.type_definitions_with_filter(buffer, position, true, cx) - } - - fn type_definitions_with_filter( - &mut self, - buffer: &Entity, - position: PointUtf16, - workspace_only: bool, - cx: &mut Context, ) -> Task>>> { if let Some((upstream_client, project_id)) = self.upstream_client() { - let request = GetTypeDefinitions { - position, - workspace_only, - }; + let request = GetTypeDefinitions { position }; if !self.is_capable_for_proto_request(buffer, &request, cx) { return Task::ready(Ok(None)); } @@ -6387,11 +6437,7 @@ impl LspStore { return Ok(None); }; let actions = join_all(responses.payload.into_iter().map(|response| { - GetTypeDefinitions { - position, - workspace_only, - } - .response_from_proto( + GetTypeDefinitions { position }.response_from_proto( response.response, lsp_store.clone(), buffer.clone(), @@ -6414,10 +6460,7 @@ impl LspStore { let type_definitions_task = self.request_multiple_lsp_locally( buffer, Some(position), - GetTypeDefinitions { - position, - workspace_only, - }, + GetTypeDefinitions { position }, cx, ); cx.background_spawn(async move { @@ -9615,6 +9658,22 @@ impl LspStore { ) .await?; } + Request::GetEditPredictionDefinition(get_edit_prediction_definition) => { + let position = get_edit_prediction_definition + .position + .clone() + .and_then(deserialize_anchor); + Self::query_lsp_locally::( + lsp_store, + server_id, + sender_id, + lsp_request_id, + get_edit_prediction_definition, + position, + &mut cx, + ) + .await?; + } Request::GetDeclaration(get_declaration) => { let position = get_declaration .position @@ -9647,6 +9706,22 @@ impl LspStore { ) .await?; } + Request::GetEditPredictionTypeDefinition(get_edit_prediction_type_definition) => { + let position = get_edit_prediction_type_definition + .position + .clone() + .and_then(deserialize_anchor); + Self::query_lsp_locally::( + lsp_store, + server_id, + sender_id, + lsp_request_id, + get_edit_prediction_type_definition, + position, + &mut cx, + ) + .await?; + } Request::GetImplementation(get_implementation) => { let position = get_implementation .position diff --git a/crates/project/src/lsp_store/lsp_ext_command.rs b/crates/project/src/lsp_store/lsp_ext_command.rs index dd7010275d..bb994492d0 100644 --- a/crates/project/src/lsp_store/lsp_ext_command.rs +++ b/crates/project/src/lsp_store/lsp_ext_command.rs @@ -443,7 +443,6 @@ impl LspCommand for GoToParentModule { lsp_store, buffer, server_id, - false, cx, ) .await diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 5f104cf312..722b712f15 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -99,6 +99,7 @@ use lsp::{ LanguageServerBinary, LanguageServerId, LanguageServerName, LanguageServerSelector, MessageActionItem, }; +pub use lsp_command::EditPredictionDefinition; use lsp_command::*; use lsp_store::{CompletionDocumentation, LspFormatTarget, OpenLspBufferHandle}; pub use manifest_tree::ManifestProvidersStore; @@ -4287,21 +4288,16 @@ impl Project { }) } - pub fn workspace_definitions( + pub fn edit_prediction_definitions( &mut self, buffer: &Entity, position: T, + include_type_definitions: bool, cx: &mut Context, - ) -> Task>>> { + ) -> Task>> { let position = position.to_point_utf16(buffer.read(cx)); - let guard = self.retain_remotely_created_models(cx); - let task = self.lsp_store.update(cx, |lsp_store, cx| { - lsp_store.workspace_definitions(buffer, position, cx) - }); - cx.background_spawn(async move { - let result = task.await; - drop(guard); - result + self.lsp_store.update(cx, |lsp_store, cx| { + lsp_store.edit_prediction_definitions(buffer, position, include_type_definitions, cx) }) } @@ -4341,24 +4337,6 @@ impl Project { }) } - pub fn workspace_type_definitions( - &mut self, - buffer: &Entity, - position: T, - cx: &mut Context, - ) -> Task>>> { - let position = position.to_point_utf16(buffer.read(cx)); - let guard = self.retain_remotely_created_models(cx); - let task = self.lsp_store.update(cx, |lsp_store, cx| { - lsp_store.workspace_type_definitions(buffer, position, cx) - }); - cx.background_spawn(async move { - let result = task.await; - drop(guard); - result - }) - } - pub fn implementations( &mut self, buffer: &Entity, diff --git a/crates/proto/proto/lsp.proto b/crates/proto/proto/lsp.proto index 33198b30ac..cf7c2b9766 100644 --- a/crates/proto/proto/lsp.proto +++ b/crates/proto/proto/lsp.proto @@ -8,13 +8,31 @@ message GetDefinition { uint64 buffer_id = 2; Anchor position = 3; repeated VectorClockEntry version = 4; - bool workspace_only = 5; + reserved 5; } message GetDefinitionResponse { repeated LocationLink links = 1; } +message GetEditPredictionDefinition { + uint64 project_id = 1; + uint64 buffer_id = 2; + Anchor position = 3; + repeated VectorClockEntry version = 4; +} + +message GetEditPredictionDefinitionResponse { + repeated EditPredictionDefinition definitions = 1; +} + +message EditPredictionDefinition { + uint64 worktree_id = 1; + string path = 2; + PointUtf16 start = 3; + PointUtf16 end = 4; +} + message GetDeclaration { uint64 project_id = 1; uint64 buffer_id = 2; @@ -31,12 +49,24 @@ message GetTypeDefinition { uint64 buffer_id = 2; Anchor position = 3; repeated VectorClockEntry version = 4; - bool workspace_only = 5; + reserved 5; } message GetTypeDefinitionResponse { repeated LocationLink links = 1; } + +message GetEditPredictionTypeDefinition { + uint64 project_id = 1; + uint64 buffer_id = 2; + Anchor position = 3; + repeated VectorClockEntry version = 4; +} + +message GetEditPredictionTypeDefinitionResponse { + repeated EditPredictionDefinition definitions = 1; +} + message GetImplementation { uint64 project_id = 1; uint64 buffer_id = 2; @@ -897,6 +927,8 @@ message LspQuery { GetFoldingRanges get_folding_ranges = 17; GetDocumentSymbols get_document_symbols = 18; GetDocumentLinks get_document_links = 19; + GetEditPredictionDefinition get_edit_prediction_definition = 20; + GetEditPredictionTypeDefinition get_edit_prediction_type_definition = 21; } } @@ -924,6 +956,8 @@ message LspResponse { GetFoldingRangesResponse get_folding_ranges_response = 15; GetDocumentSymbolsResponse get_document_symbols_response = 16; GetDocumentLinksResponse get_document_links_response = 17; + GetEditPredictionDefinitionResponse get_edit_prediction_definition_response = 18; + GetEditPredictionTypeDefinitionResponse get_edit_prediction_type_definition_response = 19; } uint64 server_id = 7; } diff --git a/crates/proto/proto/zed.proto b/crates/proto/proto/zed.proto index 2a196e86e8..07ec004674 100644 --- a/crates/proto/proto/zed.proto +++ b/crates/proto/proto/zed.proto @@ -59,6 +59,10 @@ message Envelope { GetDefinition get_definition = 32; GetDefinitionResponse get_definition_response = 33; + GetEditPredictionDefinition get_edit_prediction_definition = 462; + GetEditPredictionDefinitionResponse get_edit_prediction_definition_response = 463; + GetEditPredictionTypeDefinition get_edit_prediction_type_definition = 464; + GetEditPredictionTypeDefinitionResponse get_edit_prediction_type_definition_response = 465; // current max GetDeclaration get_declaration = 237; GetDeclarationResponse get_declaration_response = 238; GetTypeDefinition get_type_definition = 34; @@ -489,7 +493,7 @@ message Envelope { GitWorktreeCreatedAtResponse git_worktree_created_at_response = 458; TelemetryEvent telemetry_event = 459; ResolveCodeAction resolve_code_action = 460; - ResolveCodeActionResponse resolve_code_action_response = 461; // current max + ResolveCodeActionResponse resolve_code_action_response = 461; } reserved 87 to 88; diff --git a/crates/proto/src/proto.rs b/crates/proto/src/proto.rs index 3fbd235275..f7dd604d82 100644 --- a/crates/proto/src/proto.rs +++ b/crates/proto/src/proto.rs @@ -94,6 +94,10 @@ messages!( (GetDeclarationResponse, Background), (GetDefinition, Background), (GetDefinitionResponse, Background), + (GetEditPredictionDefinition, Background), + (GetEditPredictionDefinitionResponse, Background), + (GetEditPredictionTypeDefinition, Background), + (GetEditPredictionTypeDefinitionResponse, Background), (GetDocumentHighlights, Background), (GetDocumentHighlightsResponse, Background), (GetDocumentSymbols, Background), @@ -415,6 +419,14 @@ request_messages!( (GetCodeActions, GetCodeActionsResponse), (GetCompletions, GetCompletionsResponse), (GetDefinition, GetDefinitionResponse), + ( + GetEditPredictionDefinition, + GetEditPredictionDefinitionResponse + ), + ( + GetEditPredictionTypeDefinition, + GetEditPredictionTypeDefinitionResponse + ), (GetDeclaration, GetDeclarationResponse), (GetImplementation, GetImplementationResponse), (GetDocumentHighlights, GetDocumentHighlightsResponse), @@ -610,6 +622,16 @@ lsp_messages!( (GetCodeLens, GetCodeLensResponse, true), (GetDocumentDiagnostics, GetDocumentDiagnosticsResponse, true), (GetDefinition, GetDefinitionResponse, true), + ( + GetEditPredictionDefinition, + GetEditPredictionDefinitionResponse, + true + ), + ( + GetEditPredictionTypeDefinition, + GetEditPredictionTypeDefinitionResponse, + true + ), (GetDeclaration, GetDeclarationResponse, true), (GetTypeDefinition, GetTypeDefinitionResponse, true), (GetImplementation, GetImplementationResponse, true), @@ -650,6 +672,8 @@ entity_messages!( GetCodeLens, GetCompletions, GetDefinition, + GetEditPredictionDefinition, + GetEditPredictionTypeDefinition, GetDeclaration, GetImplementation, GetDocumentHighlights, @@ -987,6 +1011,12 @@ impl LspQuery { ("GetDocumentDiagnostics", false) } Some(lsp_query::Request::GetDefinition(_)) => ("GetDefinition", false), + Some(lsp_query::Request::GetEditPredictionDefinition(_)) => { + ("GetEditPredictionDefinition", false) + } + Some(lsp_query::Request::GetEditPredictionTypeDefinition(_)) => { + ("GetEditPredictionTypeDefinition", false) + } Some(lsp_query::Request::GetDeclaration(_)) => ("GetDeclaration", false), Some(lsp_query::Request::GetTypeDefinition(_)) => ("GetTypeDefinition", false), Some(lsp_query::Request::GetImplementation(_)) => ("GetImplementation", false), diff --git a/crates/rpc/src/proto_client.rs b/crates/rpc/src/proto_client.rs index be3d418c9e..d1cdd8d9e5 100644 --- a/crates/rpc/src/proto_client.rs +++ b/crates/rpc/src/proto_client.rs @@ -396,12 +396,18 @@ impl AnyProtoClient { Response::GetDefinitionResponse(response) => { to_any_envelope(&envelope, response) } + Response::GetEditPredictionDefinitionResponse(response) => { + to_any_envelope(&envelope, response) + } Response::GetDeclarationResponse(response) => { to_any_envelope(&envelope, response) } Response::GetTypeDefinitionResponse(response) => { to_any_envelope(&envelope, response) } + Response::GetEditPredictionTypeDefinitionResponse(response) => { + to_any_envelope(&envelope, response) + } Response::GetImplementationResponse(response) => { to_any_envelope(&envelope, response) }