Avoid re-querying language server completions when possible (#31872)

Also adds reuse of the markdown documentation cache even when
completions are re-queried, so that markdown documentation doesn't
flicker when `is_incomplete: true` (completions provided by rust
analyzer always set this)

Release Notes:

- Added support for filtering language server completions instead of
re-querying.
This commit is contained in:
Michael Sloan 2025-06-02 16:19:09 -06:00 committed by GitHub
parent b7ec437b13
commit 17cf865d1e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
17 changed files with 1221 additions and 720 deletions

View file

@ -1,10 +1,10 @@
mod signature_help;
use crate::{
CodeAction, CompletionSource, CoreCompletion, DocumentHighlight, DocumentSymbol, Hover,
HoverBlock, HoverBlockKind, InlayHint, InlayHintLabel, InlayHintLabelPart,
InlayHintLabelPartTooltip, InlayHintTooltip, Location, LocationLink, LspAction, MarkupContent,
PrepareRenameResponse, ProjectTransaction, ResolveState,
CodeAction, CompletionSource, CoreCompletion, CoreCompletionResponse, DocumentHighlight,
DocumentSymbol, Hover, HoverBlock, HoverBlockKind, InlayHint, InlayHintLabel,
InlayHintLabelPart, InlayHintLabelPartTooltip, InlayHintTooltip, Location, LocationLink,
LspAction, MarkupContent, PrepareRenameResponse, ProjectTransaction, ResolveState,
lsp_store::{LocalLspStore, LspStore},
};
use anyhow::{Context as _, Result};
@ -2095,7 +2095,7 @@ impl LspCommand for GetHover {
#[async_trait(?Send)]
impl LspCommand for GetCompletions {
type Response = Vec<CoreCompletion>;
type Response = CoreCompletionResponse;
type LspRequest = lsp::request::Completion;
type ProtoRequest = proto::GetCompletions;
@ -2127,19 +2127,22 @@ impl LspCommand for GetCompletions {
mut cx: AsyncApp,
) -> Result<Self::Response> {
let mut response_list = None;
let mut completions = if let Some(completions) = completions {
let (mut completions, mut is_incomplete) = if let Some(completions) = completions {
match completions {
lsp::CompletionResponse::Array(completions) => completions,
lsp::CompletionResponse::Array(completions) => (completions, false),
lsp::CompletionResponse::List(mut list) => {
let is_incomplete = list.is_incomplete;
let items = std::mem::take(&mut list.items);
response_list = Some(list);
items
(items, is_incomplete)
}
}
} else {
Vec::new()
(Vec::new(), false)
};
let unfiltered_completions_count = completions.len();
let language_server_adapter = lsp_store
.read_with(&mut cx, |lsp_store, _| {
lsp_store.language_server_adapter_for_id(server_id)
@ -2259,11 +2262,17 @@ impl LspCommand for GetCompletions {
});
})?;
// If completions were filtered out due to errors that may be transient, mark the result
// incomplete so that it is re-queried.
if unfiltered_completions_count != completions.len() {
is_incomplete = true;
}
language_server_adapter
.process_completions(&mut completions)
.await;
Ok(completions
let completions = completions
.into_iter()
.zip(completion_edits)
.map(|(mut lsp_completion, mut edit)| {
@ -2290,7 +2299,12 @@ impl LspCommand for GetCompletions {
},
}
})
.collect())
.collect();
Ok(CoreCompletionResponse {
completions,
is_incomplete,
})
}
fn to_proto(&self, project_id: u64, buffer: &Buffer) -> proto::GetCompletions {
@ -2332,18 +2346,20 @@ impl LspCommand for GetCompletions {
}
fn response_to_proto(
completions: Vec<CoreCompletion>,
response: CoreCompletionResponse,
_: &mut LspStore,
_: PeerId,
buffer_version: &clock::Global,
_: &mut App,
) -> proto::GetCompletionsResponse {
proto::GetCompletionsResponse {
completions: completions
completions: response
.completions
.iter()
.map(LspStore::serialize_completion)
.collect(),
version: serialize_version(buffer_version),
can_reuse: !response.is_incomplete,
}
}
@ -2360,11 +2376,16 @@ impl LspCommand for GetCompletions {
})?
.await?;
message
let completions = message
.completions
.into_iter()
.map(LspStore::deserialize_completion)
.collect()
.collect::<Result<Vec<_>>>()?;
Ok(CoreCompletionResponse {
completions,
is_incomplete: !message.can_reuse,
})
}
fn buffer_id_from_proto(message: &proto::GetCompletions) -> Result<BufferId> {