Autocomplete docs (#3126)
Release Notes: - Added documentation display for autocomplete items. - Fixed autocomplete filtering blocking the Zed UI, causing hitches and input delays with large completion lists. - Fixed hover popup link not firing if the mouse moved a slight amount while clicking. - Added support for absolute path file links in hover popup and autocomplete docs.
This commit is contained in:
commit
2323fd17b0
24 changed files with 1237 additions and 345 deletions
|
@ -10,7 +10,7 @@ use futures::future;
|
|||
use gpui::{AppContext, AsyncAppContext, ModelHandle};
|
||||
use language::{
|
||||
language_settings::{language_settings, InlayHintKind},
|
||||
point_from_lsp, point_to_lsp,
|
||||
point_from_lsp, point_to_lsp, prepare_completion_documentation,
|
||||
proto::{deserialize_anchor, deserialize_version, serialize_anchor, serialize_version},
|
||||
range_from_lsp, range_to_lsp, Anchor, Bias, Buffer, BufferSnapshot, CachedLspAdapter, CharKind,
|
||||
CodeAction, Completion, OffsetRangeExt, PointUtf16, ToOffset, ToPointUtf16, Transaction,
|
||||
|
@ -1341,7 +1341,7 @@ impl LspCommand for GetCompletions {
|
|||
async fn response_from_lsp(
|
||||
self,
|
||||
completions: Option<lsp::CompletionResponse>,
|
||||
_: ModelHandle<Project>,
|
||||
project: ModelHandle<Project>,
|
||||
buffer: ModelHandle<Buffer>,
|
||||
server_id: LanguageServerId,
|
||||
cx: AsyncAppContext,
|
||||
|
@ -1358,10 +1358,11 @@ impl LspCommand for GetCompletions {
|
|||
}
|
||||
}
|
||||
} else {
|
||||
Default::default()
|
||||
Vec::new()
|
||||
};
|
||||
|
||||
let completions = buffer.read_with(&cx, |buffer, _| {
|
||||
let completions = buffer.read_with(&cx, |buffer, cx| {
|
||||
let language_registry = project.read(cx).languages().clone();
|
||||
let language = buffer.language().cloned();
|
||||
let snapshot = buffer.snapshot();
|
||||
let clipped_position = buffer.clip_point_utf16(Unclipped(self.position), Bias::Left);
|
||||
|
@ -1370,6 +1371,14 @@ impl LspCommand for GetCompletions {
|
|||
completions
|
||||
.into_iter()
|
||||
.filter_map(move |mut lsp_completion| {
|
||||
if let Some(response_list) = &response_list {
|
||||
if let Some(item_defaults) = &response_list.item_defaults {
|
||||
if let Some(data) = &item_defaults.data {
|
||||
lsp_completion.data = Some(data.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let (old_range, mut new_text) = match lsp_completion.text_edit.as_ref() {
|
||||
// If the language server provides a range to overwrite, then
|
||||
// check that the range is valid.
|
||||
|
@ -1445,14 +1454,30 @@ impl LspCommand for GetCompletions {
|
|||
}
|
||||
};
|
||||
|
||||
let language = language.clone();
|
||||
LineEnding::normalize(&mut new_text);
|
||||
let language_registry = language_registry.clone();
|
||||
let language = language.clone();
|
||||
|
||||
Some(async move {
|
||||
let mut label = None;
|
||||
if let Some(language) = language {
|
||||
if let Some(language) = language.as_ref() {
|
||||
language.process_completion(&mut lsp_completion).await;
|
||||
label = language.label_for_completion(&lsp_completion).await;
|
||||
}
|
||||
|
||||
let documentation = if let Some(lsp_docs) = &lsp_completion.documentation {
|
||||
Some(
|
||||
prepare_completion_documentation(
|
||||
lsp_docs,
|
||||
&language_registry,
|
||||
language.clone(),
|
||||
)
|
||||
.await,
|
||||
)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
Completion {
|
||||
old_range,
|
||||
new_text,
|
||||
|
@ -1462,6 +1487,7 @@ impl LspCommand for GetCompletions {
|
|||
lsp_completion.filter_text.as_deref(),
|
||||
)
|
||||
}),
|
||||
documentation,
|
||||
server_id,
|
||||
lsp_completion,
|
||||
}
|
||||
|
|
|
@ -592,6 +592,7 @@ impl Project {
|
|||
client.add_model_request_handler(Self::handle_apply_code_action);
|
||||
client.add_model_request_handler(Self::handle_on_type_formatting);
|
||||
client.add_model_request_handler(Self::handle_inlay_hints);
|
||||
client.add_model_request_handler(Self::handle_resolve_completion_documentation);
|
||||
client.add_model_request_handler(Self::handle_resolve_inlay_hint);
|
||||
client.add_model_request_handler(Self::handle_refresh_inlay_hints);
|
||||
client.add_model_request_handler(Self::handle_reload_buffers);
|
||||
|
@ -7352,6 +7353,40 @@ impl Project {
|
|||
})
|
||||
}
|
||||
|
||||
async fn handle_resolve_completion_documentation(
|
||||
this: ModelHandle<Self>,
|
||||
envelope: TypedEnvelope<proto::ResolveCompletionDocumentation>,
|
||||
_: Arc<Client>,
|
||||
mut cx: AsyncAppContext,
|
||||
) -> Result<proto::ResolveCompletionDocumentationResponse> {
|
||||
let lsp_completion = serde_json::from_slice(&envelope.payload.lsp_completion)?;
|
||||
|
||||
let completion = this
|
||||
.read_with(&mut cx, |this, _| {
|
||||
let id = LanguageServerId(envelope.payload.language_server_id as usize);
|
||||
let Some(server) = this.language_server_for_id(id) else {
|
||||
return Err(anyhow!("No language server {id}"));
|
||||
};
|
||||
|
||||
Ok(server.request::<lsp::request::ResolveCompletionItem>(lsp_completion))
|
||||
})?
|
||||
.await?;
|
||||
|
||||
let mut is_markdown = false;
|
||||
let text = match completion.documentation {
|
||||
Some(lsp::Documentation::String(text)) => text,
|
||||
|
||||
Some(lsp::Documentation::MarkupContent(lsp::MarkupContent { kind, value })) => {
|
||||
is_markdown = kind == lsp::MarkupKind::Markdown;
|
||||
value
|
||||
}
|
||||
|
||||
_ => String::new(),
|
||||
};
|
||||
|
||||
Ok(proto::ResolveCompletionDocumentationResponse { text, is_markdown })
|
||||
}
|
||||
|
||||
async fn handle_apply_code_action(
|
||||
this: ModelHandle<Self>,
|
||||
envelope: TypedEnvelope<proto::ApplyCodeAction>,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue