Use more LSP data when falling back to regular completions label (#23909)
Closes https://github.com/zed-industries/zed/issues/23590 Closes https://x.com/steeve/status/1865129235536568555 Before: <img width="773" alt="before" src="https://github.com/user-attachments/assets/129a8d12-9298-4bf5-8f2d-b3292c2562bf" /> After: <img width="768" alt="after" src="https://github.com/user-attachments/assets/e0516fb3-b02a-48be-8923-63bba05fdb69" /> The list obviously needs some solution for the cut-off part of the completion label, but this is the reality for all extensions' completions too, so one step at a time. Release Notes: - Improved default completion label fallback
This commit is contained in:
parent
48dba9a9d9
commit
9e4555797d
3 changed files with 62 additions and 12 deletions
|
@ -11282,7 +11282,7 @@ async fn test_completions_resolve_updates_labels_if_filter_text_matches(
|
||||||
cx.simulate_keystroke(".");
|
cx.simulate_keystroke(".");
|
||||||
|
|
||||||
let item1 = lsp::CompletionItem {
|
let item1 = lsp::CompletionItem {
|
||||||
label: "id".to_string(),
|
label: "method id()".to_string(),
|
||||||
filter_text: Some("id".to_string()),
|
filter_text: Some("id".to_string()),
|
||||||
detail: None,
|
detail: None,
|
||||||
documentation: None,
|
documentation: None,
|
||||||
|
@ -11332,7 +11332,7 @@ async fn test_completions_resolve_updates_labels_if_filter_text_matches(
|
||||||
.iter()
|
.iter()
|
||||||
.map(|completion| &completion.label.text)
|
.map(|completion| &completion.label.text)
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
vec!["id", "other"]
|
vec!["method id()", "other"]
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
|
CodeContextMenu::CodeActions(_) => panic!("Should show the completions menu"),
|
||||||
|
@ -11387,7 +11387,7 @@ async fn test_completions_resolve_updates_labels_if_filter_text_matches(
|
||||||
.iter()
|
.iter()
|
||||||
.map(|completion| &completion.label.text)
|
.map(|completion| &completion.label.text)
|
||||||
.collect::<Vec<_>>(),
|
.collect::<Vec<_>>(),
|
||||||
vec!["method id()", "other"],
|
vec!["method id() Now resolved!", "other"],
|
||||||
"Should update first completion label, but not second as the filter text did not match."
|
"Should update first completion label, but not second as the filter text did not match."
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1750,6 +1750,58 @@ impl Grammar {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CodeLabel {
|
impl CodeLabel {
|
||||||
|
pub fn fallback_for_completion(
|
||||||
|
item: &lsp::CompletionItem,
|
||||||
|
language: Option<&Language>,
|
||||||
|
) -> Self {
|
||||||
|
let highlight_id = item.kind.and_then(|kind| {
|
||||||
|
let grammar = language?.grammar()?;
|
||||||
|
use lsp::CompletionItemKind as Kind;
|
||||||
|
match kind {
|
||||||
|
Kind::CLASS => grammar.highlight_id_for_name("type"),
|
||||||
|
Kind::CONSTANT => grammar.highlight_id_for_name("constant"),
|
||||||
|
Kind::CONSTRUCTOR => grammar.highlight_id_for_name("constructor"),
|
||||||
|
Kind::ENUM => grammar
|
||||||
|
.highlight_id_for_name("enum")
|
||||||
|
.or_else(|| grammar.highlight_id_for_name("type")),
|
||||||
|
Kind::FIELD => grammar.highlight_id_for_name("property"),
|
||||||
|
Kind::FUNCTION => grammar.highlight_id_for_name("function"),
|
||||||
|
Kind::INTERFACE => grammar.highlight_id_for_name("type"),
|
||||||
|
Kind::METHOD => grammar
|
||||||
|
.highlight_id_for_name("function.method")
|
||||||
|
.or_else(|| grammar.highlight_id_for_name("function")),
|
||||||
|
Kind::OPERATOR => grammar.highlight_id_for_name("operator"),
|
||||||
|
Kind::PROPERTY => grammar.highlight_id_for_name("property"),
|
||||||
|
Kind::STRUCT => grammar.highlight_id_for_name("type"),
|
||||||
|
Kind::VARIABLE => grammar.highlight_id_for_name("variable"),
|
||||||
|
Kind::KEYWORD => grammar.highlight_id_for_name("keyword"),
|
||||||
|
_ => None,
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let label = &item.label;
|
||||||
|
let label_length = label.len();
|
||||||
|
let runs = highlight_id
|
||||||
|
.map(|highlight_id| vec![(0..label_length, highlight_id)])
|
||||||
|
.unwrap_or_default();
|
||||||
|
let text = if let Some(detail) = &item.detail {
|
||||||
|
format!("{label} {detail}")
|
||||||
|
} else if let Some(description) = item
|
||||||
|
.label_details
|
||||||
|
.as_ref()
|
||||||
|
.and_then(|label_details| label_details.description.as_ref())
|
||||||
|
{
|
||||||
|
format!("{label} {description}")
|
||||||
|
} else {
|
||||||
|
label.clone()
|
||||||
|
};
|
||||||
|
Self {
|
||||||
|
text,
|
||||||
|
runs,
|
||||||
|
filter_range: 0..label_length,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn plain(text: String, filter_text: Option<&str>) -> Self {
|
pub fn plain(text: String, filter_text: Option<&str>) -> Self {
|
||||||
let mut result = Self {
|
let mut result = Self {
|
||||||
runs: Vec::new(),
|
runs: Vec::new(),
|
||||||
|
|
|
@ -4380,7 +4380,8 @@ impl LspStore {
|
||||||
|
|
||||||
// NB: Zed does not have `details` inside the completion resolve capabilities, but certain language servers violate the spec and do not return `details` immediately, e.g. https://github.com/yioneko/vtsls/issues/213
|
// NB: Zed does not have `details` inside the completion resolve capabilities, but certain language servers violate the spec and do not return `details` immediately, e.g. https://github.com/yioneko/vtsls/issues/213
|
||||||
// So we have to update the label here anyway...
|
// So we have to update the label here anyway...
|
||||||
let mut new_label = match snapshot.language() {
|
let language = snapshot.language();
|
||||||
|
let mut new_label = match language {
|
||||||
Some(language) => {
|
Some(language) => {
|
||||||
adapter
|
adapter
|
||||||
.labels_for_completions(&[completion_item.clone()], language)
|
.labels_for_completions(&[completion_item.clone()], language)
|
||||||
|
@ -4391,9 +4392,9 @@ impl LspStore {
|
||||||
.pop()
|
.pop()
|
||||||
.flatten()
|
.flatten()
|
||||||
.unwrap_or_else(|| {
|
.unwrap_or_else(|| {
|
||||||
CodeLabel::plain(
|
CodeLabel::fallback_for_completion(
|
||||||
completion_item.label,
|
&completion_item,
|
||||||
completion_item.filter_text.as_deref(),
|
language.map(|language| language.as_ref()),
|
||||||
)
|
)
|
||||||
});
|
});
|
||||||
ensure_uniform_list_compatible_label(&mut new_label);
|
ensure_uniform_list_compatible_label(&mut new_label);
|
||||||
|
@ -8079,10 +8080,7 @@ async fn populate_labels_for_completions(
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut label = label.unwrap_or_else(|| {
|
let mut label = label.unwrap_or_else(|| {
|
||||||
CodeLabel::plain(
|
CodeLabel::fallback_for_completion(&lsp_completion, language.as_deref())
|
||||||
lsp_completion.label.clone(),
|
|
||||||
lsp_completion.filter_text.as_deref(),
|
|
||||||
)
|
|
||||||
});
|
});
|
||||||
ensure_uniform_list_compatible_label(&mut label);
|
ensure_uniform_list_compatible_label(&mut label);
|
||||||
|
|
||||||
|
@ -8883,7 +8881,7 @@ fn include_text(server: &lsp::LanguageServer) -> Option<bool> {
|
||||||
/// Completion items are displayed in a `UniformList`.
|
/// Completion items are displayed in a `UniformList`.
|
||||||
/// Usually, those items are single-line strings, but in LSP responses,
|
/// Usually, those items are single-line strings, but in LSP responses,
|
||||||
/// completion items `label`, `detail` and `label_details.description` may contain newlines or long spaces.
|
/// completion items `label`, `detail` and `label_details.description` may contain newlines or long spaces.
|
||||||
/// Many language plugins construct these items by joining these parts together, and we may fall back to `CodeLabel::plain` that uses `label`.
|
/// Many language plugins construct these items by joining these parts together, and we may use `CodeLabel::fallback_for_completion` that uses `label` at least.
|
||||||
/// All that may lead to a newline being inserted into resulting `CodeLabel.text`, which will force `UniformList` to bloat each entry to occupy more space,
|
/// All that may lead to a newline being inserted into resulting `CodeLabel.text`, which will force `UniformList` to bloat each entry to occupy more space,
|
||||||
/// breaking the completions menu presentation.
|
/// breaking the completions menu presentation.
|
||||||
///
|
///
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue