@@ -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();
@@ -421,7 +421,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;
}
}
@@ -1597,6 +1599,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",
+ );
+
// Test for correct range calculation with mixed empty and non-empty tabstops.(See https://github.com/zed-industries/zed/issues/44825)
let res = adapter
.label_for_completion(