Add a bespoke LSP request path for edit prediction context (#60947)

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<Unclipped<PointUtf16>> }` 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
This commit is contained in:
Ben Kunkle authored and GitHub committed 2026-07-15 01:54:27 +00:00
1 parent 31f1c84909
commit d224ea86e8
12 files changed
+847 -258

No files matched your search

@@ -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::<lsp::request::GotoDefinition, _, _>(
|_, _| 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,
@@ -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<CacheEntry>),
CacheMiss(
Task<
Option<(
Task<Result<Option<Vec<LocationLink>>>>,
Task<Result<Option<Vec<LocationLink>>>>,
)>,
>,
),
CacheMiss {
project: WeakEntity<Project>,
task: Task<Result<Vec<EditPredictionDefinition>>>,
},
}
#[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<CachedDefinition> {
async fn process_definition(
definition: EditPredictionDefinition,
project: &WeakEntity<Project>,
cx: &mut AsyncApp,
) -> Option<CachedDefinition> {
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.
@@ -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);
@@ -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<Person>` produces
/// `"owner" -> "Person"` by unwrapping common generic wrappers.
+348 -64
View File
@@ -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<Unclipped<PointUtf16>>,
}
#[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<Vec<LocationLink>> {
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<EditPredictionDefinition>;
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<LanguageServer>,
_: &App,
) -> Result<lsp::GotoDefinitionParams> {
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::GotoDefinitionResponse>,
lsp_store: Entity<LspStore>,
_: Entity<Buffer>,
_: LanguageServerId,
cx: AsyncApp,
) -> Result<Vec<EditPredictionDefinition>> {
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<LspStore>,
buffer: Entity<Buffer>,
mut cx: AsyncApp,
) -> Result<Self> {
Ok(Self {
position: edit_prediction_position_from_proto(
message.position,
message.version,
buffer,
&mut cx,
)
.await?,
})
}
fn response_to_proto(
response: Vec<EditPredictionDefinition>,
_: &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<LspStore>,
_: Entity<Buffer>,
_: AsyncApp,
) -> Result<Vec<EditPredictionDefinition>> {
edit_prediction_definitions_from_proto(message.definitions)
}
fn buffer_id_from_proto(message: &proto::GetEditPredictionDefinition) -> Result<BufferId> {
BufferId::new(message.buffer_id)
}
}
#[async_trait(?Send)]
impl LspCommand for GetDeclarations {
type Response = Vec<LocationLink>;
@@ -807,7 +912,7 @@ impl LspCommand for GetDeclarations {
server_id: LanguageServerId,
cx: AsyncApp,
) -> Result<Vec<LocationLink>> {
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<Vec<LocationLink>> {
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<Vec<LocationLink>> {
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<EditPredictionDefinition>;
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<LanguageServer>,
_: &App,
) -> Result<lsp::GotoTypeDefinitionParams> {
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::GotoDefinitionResponse>,
lsp_store: Entity<LspStore>,
_: Entity<Buffer>,
_: LanguageServerId,
cx: AsyncApp,
) -> Result<Vec<EditPredictionDefinition>> {
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<LspStore>,
buffer: Entity<Buffer>,
mut cx: AsyncApp,
) -> Result<Self> {
Ok(Self {
position: edit_prediction_position_from_proto(
message.position,
message.version,
buffer,
&mut cx,
)
.await?,
})
}
fn response_to_proto(
response: Vec<EditPredictionDefinition>,
_: &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<LspStore>,
_: Entity<Buffer>,
_: AsyncApp,
) -> Result<Vec<EditPredictionDefinition>> {
edit_prediction_definitions_from_proto(message.definitions)
}
fn buffer_id_from_proto(message: &proto::GetEditPredictionTypeDefinition) -> Result<BufferId> {
BufferId::new(message.buffer_id)
}
}
fn language_server_for_buffer(
lsp_store: &Entity<LspStore>,
buffer: &Entity<Buffer>,
@@ -1165,57 +1365,13 @@ pub async fn location_links_from_lsp(
lsp_store: Entity<LspStore>,
buffer: Entity<Buffer>,
server_id: LanguageServerId,
workspace_only: bool,
mut cx: AsyncApp,
) -> Result<Vec<LocationLink>> {
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<lsp::GotoDefinitionResponse>,
) -> Vec<(Option<lsp::Range>, 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::GotoDefinitionResponse>,
lsp_store: Entity<LspStore>,
mut cx: AsyncApp,
) -> Result<Vec<EditPredictionDefinition>> {
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<proto::Anchor>,
version: Vec<proto::VectorClockEntry>,
buffer: Entity<Buffer>,
cx: &mut AsyncApp,
) -> Result<PointUtf16> {
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<LspStore>,
@@ -1363,6 +1607,46 @@ pub fn location_link_to_proto(
}
}
fn edit_prediction_definitions_to_proto(
definitions: Vec<EditPredictionDefinition>,
) -> Vec<proto::EditPredictionDefinition> {
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<proto::EditPredictionDefinition>,
) -> Result<Vec<EditPredictionDefinition>> {
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<Location>;
+139 -64
View File
@@ -6171,31 +6171,9 @@ impl LspStore {
buffer: &Entity<Buffer>,
position: PointUtf16,
cx: &mut Context<Self>,
) -> Task<Result<Option<Vec<LocationLink>>>> {
self.definitions_with_filter(buffer, position, false, cx)
}
pub fn workspace_definitions(
&mut self,
buffer: &Entity<Buffer>,
position: PointUtf16,
cx: &mut Context<Self>,
) -> Task<Result<Option<Vec<LocationLink>>>> {
self.definitions_with_filter(buffer, position, true, cx)
}
fn definitions_with_filter(
&mut self,
buffer: &Entity<Buffer>,
position: PointUtf16,
workspace_only: bool,
cx: &mut Context<Self>,
) -> Task<Result<Option<Vec<LocationLink>>>> {
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<C>(
&mut self,
buffer: &Entity<Buffer>,
request: C,
position: PointUtf16,
cx: &mut Context<Self>,
) -> Task<Result<Vec<EditPredictionDefinition>>>
where
C: LspCommand<Response = Vec<EditPredictionDefinition>> + Clone,
C::ProtoRequest: proto::LspRequestMessage,
<C::ProtoRequest as proto::LspRequestMessage>::Response:
Into<<C::ProtoRequest as proto::RequestMessage>::Response>,
<C::LspRequest as lsp::request::Request>::Result: Send,
<C::LspRequest as lsp::request::Request>::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::<Result<Vec<Vec<_>>>>()?
.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<Buffer>,
position: PointUtf16,
include_type_definitions: bool,
cx: &mut Context<Self>,
) -> Task<Result<Vec<EditPredictionDefinition>>> {
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<Buffer>,
@@ -6340,31 +6412,9 @@ impl LspStore {
buffer: &Entity<Buffer>,
position: PointUtf16,
cx: &mut Context<Self>,
) -> Task<Result<Option<Vec<LocationLink>>>> {
self.type_definitions_with_filter(buffer, position, false, cx)
}
pub fn workspace_type_definitions(
&mut self,
buffer: &Entity<Buffer>,
position: PointUtf16,
cx: &mut Context<Self>,
) -> Task<Result<Option<Vec<LocationLink>>>> {
self.type_definitions_with_filter(buffer, position, true, cx)
}
fn type_definitions_with_filter(
&mut self,
buffer: &Entity<Buffer>,
position: PointUtf16,
workspace_only: bool,
cx: &mut Context<Self>,
) -> Task<Result<Option<Vec<LocationLink>>>> {
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::<GetEditPredictionDefinitions>(
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::<GetEditPredictionTypeDefinitions>(
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
@@ -443,7 +443,6 @@ impl LspCommand for GoToParentModule {
lsp_store,
buffer,
server_id,
false,
cx,
)
.await
+6 -28
View File
@@ -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<T: ToPointUtf16>(
pub fn edit_prediction_definitions<T: ToPointUtf16>(
&mut self,
buffer: &Entity<Buffer>,
position: T,
include_type_definitions: bool,
cx: &mut Context<Self>,
) -> Task<Result<Option<Vec<LocationLink>>>> {
) -> Task<Result<Vec<EditPredictionDefinition>>> {
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<T: ToPointUtf16>(
&mut self,
buffer: &Entity<Buffer>,
position: T,
cx: &mut Context<Self>,
) -> Task<Result<Option<Vec<LocationLink>>>> {
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<T: ToPointUtf16>(
&mut self,
buffer: &Entity<Buffer>,
+36 -2
View File
@@ -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;
}
+5 -1
View File
@@ -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;
+30
View File
@@ -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),
+6
View File
@@ -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)
}