From 4cd4be40a2b35597f788cb8fa52989e92b655491 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 18 Dec 2025 13:29:41 +0200 Subject: [PATCH] Keep tab stop-less snippets in completion list (#45227) Closes https://github.com/zed-industries/zed/issues/45083 cc @agu-z Release Notes: - Fixed certain rust-analyzer snippets not shown --- crates/languages/src/rust.rs | 40 ++++++++++++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/crates/languages/src/rust.rs b/crates/languages/src/rust.rs index 31d7448285969fbce005b9b7134f56c7d8362f73..f94077627248a392c0f8af07772cec06090abd22 100644 --- a/crates/languages/src/rust.rs +++ b/crates/languages/src/rust.rs @@ -355,7 +355,7 @@ impl LspAdapter for RustLspAdapter { | lsp::CompletionTextEdit::Edit(lsp::TextEdit { new_text, .. }), ) = completion.text_edit.as_ref() && let Ok(mut snippet) = snippet::Snippet::parse(new_text) - && !snippet.tabstops.is_empty() + && snippet.tabstops.len() > 1 { label = String::new(); @@ -417,7 +417,9 @@ impl LspAdapter for RustLspAdapter { 0..label.rfind('(').unwrap_or(completion.label.len()), highlight_id, )); - } else if detail_left.is_none() { + } else if detail_left.is_none() + && kind != Some(lsp::CompletionItemKind::SNIPPET) + { return None; } } @@ -1576,6 +1578,40 @@ mod tests { ], )) ); + + // Postfix completion without actual tabstops (only implicit final $0) + // The label should use completion.label so it can be filtered by "ref" + let ref_completion = adapter + .label_for_completion( + &lsp::CompletionItem { + kind: Some(lsp::CompletionItemKind::SNIPPET), + label: "ref".to_string(), + filter_text: Some("ref".to_string()), + label_details: Some(CompletionItemLabelDetails { + detail: None, + description: Some("&expr".to_string()), + }), + detail: Some("&expr".to_string()), + insert_text_format: Some(lsp::InsertTextFormat::SNIPPET), + text_edit: Some(lsp::CompletionTextEdit::Edit(lsp::TextEdit { + range: lsp::Range::default(), + new_text: "&String::new()".to_string(), + })), + ..Default::default() + }, + &language, + ) + .await; + assert!( + ref_completion.is_some(), + "ref postfix completion should have a label" + ); + let ref_label = ref_completion.unwrap(); + let filter_text = &ref_label.text[ref_label.filter_range.clone()]; + assert!( + filter_text.contains("ref"), + "filter range text '{filter_text}' should contain 'ref' for filtering to work", + ); } #[gpui::test]