Fix completion labels becoming overly large due to LSP completion items with newlines (#23407)

Reworks https://github.com/zed-industries/zed/pull/23030 and
https://github.com/zed-industries/zed/pull/15087
Closes https://github.com/zed-industries/zed/issues/23352
Closes https://github.com/zed-industries/zed/issues/23310 

Zed's completion items use `label` from LSP completion items as a base
to show in the list:
d290da7dac/crates/project/src/lsp_store.rs (L4371-L4374)

Besides that, certain language plugins append `detail` or
`label_details.description` as a suffix:
d290da7dac/crates/languages/src/vtsls.rs (L178-L188)

Either of these 3 properties may return `\n` (or multiple) in it,
spoiling Zed's completion menu, which uses `UniformList` to render those
items: a uniform list uses common, minimum possible height for each
element, and `\n` bloats that overly.

Good approach would be to use something else:
https://github.com/zed-industries/zed/issues/21403 but that has its own
drawbacks and relatively hard to use instead (?).

We could follow VSCode's approach and move away all but `label` from
`CodeLabel.text` to the side, where the documentation is, but that does
not solve the issue with `details` having newlines.

So, for now, sanitize all labels and remove any newlines from them. If
newlines are found, also replace whitespace sequences if there's more
than 1 in a row.

Later, this approach can be improved similarly to how Helix and Zed's
inline completions do: rendering a "ghost" text, showing the
completion's edit applied to the editor.

Release Notes:

- Fixed completion labels becoming overly large due to LSP completion
items with newlines
This commit is contained in:
Kirill Bulatov 2025-01-21 17:55:41 +02:00 committed by GitHub
parent 94189e1784
commit 75c5344754
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 338 additions and 15 deletions

View file

@ -212,16 +212,14 @@ impl LspAdapter for TypeScriptLspAdapter {
_ => None,
}?;
let one_line = |s: &str| s.replace(" ", "").replace('\n', " ");
let text = if let Some(description) = item
.label_details
.as_ref()
.and_then(|label_details| label_details.description.as_ref())
{
format!("{} {}", item.label, one_line(description))
format!("{} {}", item.label, description)
} else if let Some(detail) = &item.detail {
format!("{} {}", item.label, one_line(detail))
format!("{} {}", item.label, detail)
} else {
item.label.clone()
};

View file

@ -175,16 +175,14 @@ impl LspAdapter for VtslsLspAdapter {
_ => None,
}?;
let one_line = |s: &str| s.replace(" ", "").replace('\n', " ");
let text = if let Some(description) = item
.label_details
.as_ref()
.and_then(|label_details| label_details.description.as_ref())
{
format!("{} {}", item.label, one_line(description))
format!("{} {}", item.label, description)
} else if let Some(detail) = &item.detail {
format!("{} {}", item.label, one_line(detail))
format!("{} {}", item.label, detail)
} else {
item.label.clone()
};