Stop waiting for part of LSP responses on remote Collab clients' part (#36557)

Instead of holding a connection for potentially long LSP queries (e.g.
rust-analyzer might take minutes to look up a definition), disconnect
right after sending the initial request and handle the follow-up
responses later.

As a bonus, this allows to cancel previously sent request on the local
Collab clients' side due to this, as instead of holding and serving the
old connection, local clients now can stop previous requests, if needed.

Current PR does not convert all LSP requests to the new paradigm, but
the problematic ones, deprecating `MultiLspQuery` and moving all its
requests to the new paradigm.

Release Notes:

- Improved resource usage when querying LSP over Collab

---------

Co-authored-by: David Kleingeld <git@davidsk.dev>
Co-authored-by: Mikayla Maki <mikayla@zed.dev>
Co-authored-by: David Kleingeld <davidsk@zed.dev>
This commit is contained in:
Kirill Bulatov 2025-08-21 09:24:34 +03:00 committed by GitHub
parent c731bb6d91
commit 5dcb90858e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 1395 additions and 681 deletions

View file

@ -3444,8 +3444,7 @@ impl LspCommand for GetCodeLens {
capabilities
.server_capabilities
.code_lens_provider
.as_ref()
.is_some_and(|code_lens_options| code_lens_options.resolve_provider.unwrap_or(false))
.is_some()
}
fn to_lsp(

File diff suppressed because it is too large Load diff

View file

@ -3415,7 +3415,7 @@ impl Project {
buffer: &Entity<Buffer>,
position: T,
cx: &mut Context<Self>,
) -> Task<Result<Vec<LocationLink>>> {
) -> 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| {
@ -3433,7 +3433,7 @@ impl Project {
buffer: &Entity<Buffer>,
position: T,
cx: &mut Context<Self>,
) -> Task<Result<Vec<LocationLink>>> {
) -> 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| {
@ -3451,7 +3451,7 @@ impl Project {
buffer: &Entity<Buffer>,
position: T,
cx: &mut Context<Self>,
) -> Task<Result<Vec<LocationLink>>> {
) -> 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| {
@ -3469,7 +3469,7 @@ impl Project {
buffer: &Entity<Buffer>,
position: T,
cx: &mut Context<Self>,
) -> Task<Result<Vec<LocationLink>>> {
) -> 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| {
@ -3487,7 +3487,7 @@ impl Project {
buffer: &Entity<Buffer>,
position: T,
cx: &mut Context<Self>,
) -> Task<Result<Vec<Location>>> {
) -> Task<Result<Option<Vec<Location>>>> {
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| {
@ -3585,23 +3585,12 @@ impl Project {
})
}
pub fn signature_help<T: ToPointUtf16>(
&self,
buffer: &Entity<Buffer>,
position: T,
cx: &mut Context<Self>,
) -> Task<Vec<SignatureHelp>> {
self.lsp_store.update(cx, |lsp_store, cx| {
lsp_store.signature_help(buffer, position, cx)
})
}
pub fn hover<T: ToPointUtf16>(
&self,
buffer: &Entity<Buffer>,
position: T,
cx: &mut Context<Self>,
) -> Task<Vec<Hover>> {
) -> Task<Option<Vec<Hover>>> {
let position = position.to_point_utf16(buffer.read(cx));
self.lsp_store
.update(cx, |lsp_store, cx| lsp_store.hover(buffer, position, cx))
@ -3637,7 +3626,7 @@ impl Project {
range: Range<T>,
kinds: Option<Vec<CodeActionKind>>,
cx: &mut Context<Self>,
) -> Task<Result<Vec<CodeAction>>> {
) -> Task<Result<Option<Vec<CodeAction>>>> {
let buffer = buffer_handle.read(cx);
let range = buffer.anchor_before(range.start)..buffer.anchor_before(range.end);
self.lsp_store.update(cx, |lsp_store, cx| {
@ -3650,7 +3639,7 @@ impl Project {
buffer: &Entity<Buffer>,
range: Range<T>,
cx: &mut Context<Self>,
) -> Task<Result<Vec<CodeAction>>> {
) -> Task<Result<Option<Vec<CodeAction>>>> {
let snapshot = buffer.read(cx).snapshot();
let range = range.to_point(&snapshot);
let range_start = snapshot.anchor_before(range.start);
@ -3668,16 +3657,18 @@ impl Project {
let mut code_lens_actions = code_lens_actions
.await
.map_err(|e| anyhow!("code lens fetch failed: {e:#}"))?;
code_lens_actions.retain(|code_lens_action| {
range
.start
.cmp(&code_lens_action.range.start, &snapshot)
.is_ge()
&& range
.end
.cmp(&code_lens_action.range.end, &snapshot)
.is_le()
});
if let Some(code_lens_actions) = &mut code_lens_actions {
code_lens_actions.retain(|code_lens_action| {
range
.start
.cmp(&code_lens_action.range.start, &snapshot)
.is_ge()
&& range
.end
.cmp(&code_lens_action.range.end, &snapshot)
.is_le()
});
}
Ok(code_lens_actions)
})
}

View file

@ -3005,6 +3005,7 @@ async fn test_definition(cx: &mut gpui::TestAppContext) {
let mut definitions = project
.update(cx, |project, cx| project.definitions(&buffer, 22, cx))
.await
.unwrap()
.unwrap();
// Assert no new language server started
@ -3519,7 +3520,7 @@ async fn test_apply_code_actions_with_commands(cx: &mut gpui::TestAppContext) {
.next()
.await;
let action = actions.await.unwrap()[0].clone();
let action = actions.await.unwrap().unwrap()[0].clone();
let apply = project.update(cx, |project, cx| {
project.apply_code_action(buffer.clone(), action, true, cx)
});
@ -6110,6 +6111,7 @@ async fn test_multiple_language_server_hovers(cx: &mut gpui::TestAppContext) {
hover_task
.await
.into_iter()
.flatten()
.map(|hover| hover.contents.iter().map(|block| &block.text).join("|"))
.sorted()
.collect::<Vec<_>>(),
@ -6183,6 +6185,7 @@ async fn test_hovers_with_empty_parts(cx: &mut gpui::TestAppContext) {
hover_task
.await
.into_iter()
.flatten()
.map(|hover| hover.contents.iter().map(|block| &block.text).join("|"))
.sorted()
.collect::<Vec<_>>(),
@ -6261,7 +6264,7 @@ async fn test_code_actions_only_kinds(cx: &mut gpui::TestAppContext) {
.await
.expect("The code action request should have been triggered");
let code_actions = code_actions_task.await.unwrap();
let code_actions = code_actions_task.await.unwrap().unwrap();
assert_eq!(code_actions.len(), 1);
assert_eq!(
code_actions[0].lsp_action.action_kind(),
@ -6420,6 +6423,7 @@ async fn test_multiple_language_server_actions(cx: &mut gpui::TestAppContext) {
code_actions_task
.await
.unwrap()
.unwrap()
.into_iter()
.map(|code_action| code_action.lsp_action.title().to_owned())
.sorted()