editor: Improve snippet completion to show key inline in completion and description as aside (#30603)

Closes #28028

Before:
<img width="742" alt="image"
src="https://github.com/user-attachments/assets/31723970-5420-40ea-a394-4ffa0038925c"
/>

After:
<img width="989" alt="image"
src="https://github.com/user-attachments/assets/0aebc317-a234-4e68-8304-cb479513af15"
/>


Release Notes:

- Improved snippet code completion to show key in completion menu and
description in aside.
This commit is contained in:
Smit Barmase 2025-05-12 16:58:59 -07:00 committed by GitHub
parent 229f3dab22
commit e5d497ee08
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 46 additions and 20 deletions

View file

@ -510,22 +510,25 @@ impl CompletionsMenu {
let completion_label = StyledText::new(completion.label.text.clone()) let completion_label = StyledText::new(completion.label.text.clone())
.with_default_highlights(&style.text, highlights); .with_default_highlights(&style.text, highlights);
let documentation_label = if let Some(
CompletionDocumentation::SingleLine(text), let documentation_label = match documentation {
) = documentation Some(CompletionDocumentation::SingleLine(text))
{ | Some(CompletionDocumentation::SingleLineAndMultiLinePlainText {
if text.trim().is_empty() { single_line: text,
None ..
} else { }) => {
Some( if text.trim().is_empty() {
Label::new(text.clone()) None
.ml_4() } else {
.size(LabelSize::Small) Some(
.color(Color::Muted), Label::new(text.clone())
) .ml_4()
.size(LabelSize::Small)
.color(Color::Muted),
)
}
} }
} else { _ => None,
None
}; };
let start_slot = completion let start_slot = completion
@ -597,6 +600,10 @@ impl CompletionsMenu {
.as_ref()? .as_ref()?
{ {
CompletionDocumentation::MultiLinePlainText(text) => div().child(text.clone()), CompletionDocumentation::MultiLinePlainText(text) => div().child(text.clone()),
CompletionDocumentation::SingleLineAndMultiLinePlainText {
plain_text: Some(text),
..
} => div().child(text.clone()),
CompletionDocumentation::MultiLineMarkdown(parsed) if !parsed.is_empty() => { CompletionDocumentation::MultiLineMarkdown(parsed) if !parsed.is_empty() => {
let markdown = self.markdown_element.get_or_insert_with(|| { let markdown = self.markdown_element.get_or_insert_with(|| {
cx.new(|cx| { cx.new(|cx| {
@ -627,6 +634,11 @@ impl CompletionsMenu {
CompletionDocumentation::MultiLineMarkdown(_) => return None, CompletionDocumentation::MultiLineMarkdown(_) => return None,
CompletionDocumentation::SingleLine(_) => return None, CompletionDocumentation::SingleLine(_) => return None,
CompletionDocumentation::Undocumented => return None, CompletionDocumentation::Undocumented => return None,
CompletionDocumentation::SingleLineAndMultiLinePlainText {
plain_text: None, ..
} => {
return None;
}
}; };
Some( Some(

View file

@ -19872,9 +19872,15 @@ fn snippet_completions(
filter_range: 0..matching_prefix.len(), filter_range: 0..matching_prefix.len(),
}, },
icon_path: None, icon_path: None,
documentation: snippet.description.clone().map(|description| { documentation: Some(
CompletionDocumentation::SingleLine(description.into()) CompletionDocumentation::SingleLineAndMultiLinePlainText {
}), single_line: snippet.name.clone().into(),
plain_text: snippet
.description
.clone()
.map(|description| description.into()),
},
),
insert_text_mode: None, insert_text_mode: None,
confirm: None, confirm: None,
}) })

View file

@ -9897,6 +9897,11 @@ pub enum CompletionDocumentation {
MultiLinePlainText(SharedString), MultiLinePlainText(SharedString),
/// Markdown documentation. /// Markdown documentation.
MultiLineMarkdown(SharedString), MultiLineMarkdown(SharedString),
/// Both single line and multiple lines of plain text documentation.
SingleLineAndMultiLinePlainText {
single_line: SharedString,
plain_text: Option<SharedString>,
},
} }
impl From<lsp::Documentation> for CompletionDocumentation { impl From<lsp::Documentation> for CompletionDocumentation {

View file

@ -34,10 +34,11 @@ fn file_stem_to_key(stem: &str) -> SnippetKind {
fn file_to_snippets(file_contents: VsSnippetsFile) -> Vec<Arc<Snippet>> { fn file_to_snippets(file_contents: VsSnippetsFile) -> Vec<Arc<Snippet>> {
let mut snippets = vec![]; let mut snippets = vec![];
for (prefix, snippet) in file_contents.snippets { for (name, snippet) in file_contents.snippets {
let snippet_name = name.clone();
let prefixes = snippet let prefixes = snippet
.prefix .prefix
.map_or_else(move || vec![prefix], |prefixes| prefixes.into()); .map_or_else(move || vec![snippet_name], |prefixes| prefixes.into());
let description = snippet let description = snippet
.description .description
.map(|description| description.to_string()); .map(|description| description.to_string());
@ -49,6 +50,7 @@ fn file_to_snippets(file_contents: VsSnippetsFile) -> Vec<Arc<Snippet>> {
body, body,
prefix: prefixes, prefix: prefixes,
description, description,
name,
})); }));
} }
snippets snippets
@ -59,6 +61,7 @@ pub struct Snippet {
pub prefix: Vec<String>, pub prefix: Vec<String>,
pub body: String, pub body: String,
pub description: Option<String>, pub description: Option<String>,
pub name: String,
} }
async fn process_updates( async fn process_updates(