diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 8690f06ba6a59c443e217f3266c6062be7939ac2..ddf47435680047dd172d15527e84dd36e0636558 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -4997,10 +4997,34 @@ impl Editor { .clone(); cx.stop_propagation(); + let snapshot = self.buffer.read(cx).snapshot(cx); + let newest_anchor = self.selections.newest_anchor(); + let snippet; let new_text; if completion.is_snippet() { - snippet = Some(Snippet::parse(&completion.new_text).log_err()?); + // lsp returns function definition with placeholders in "new_text" + // when configured from language server, even when renaming a function + // + // in such cases, we use the label instead + // https://github.com/zed-industries/zed/issues/29982 + let snippet_source = completion + .label() + .filter(|label| { + completion.kind() == Some(CompletionItemKind::FUNCTION) + && label != &completion.new_text + }) + .and_then(|label| { + let cursor_offset = newest_anchor.head().to_offset(&snapshot); + let next_char_is_not_whitespace = snapshot + .chars_at(cursor_offset) + .next() + .map_or(true, |ch| !ch.is_whitespace()); + next_char_is_not_whitespace.then_some(label) + }) + .unwrap_or(completion.new_text.clone()); + + snippet = Some(Snippet::parse(&snippet_source).log_err()?); new_text = snippet.as_ref().unwrap().text.clone(); } else { snippet = None; @@ -5009,11 +5033,8 @@ impl Editor { let replace_range = choose_completion_range(&completion, intent, &buffer_handle, cx); let buffer = buffer_handle.read(cx); - let snapshot = self.buffer.read(cx).snapshot(cx); let replace_range_multibuffer = { - let excerpt = snapshot - .excerpt_containing(self.selections.newest_anchor().range()) - .unwrap(); + let excerpt = snapshot.excerpt_containing(newest_anchor.range()).unwrap(); let multibuffer_anchor = snapshot .anchor_in_excerpt(excerpt.id(), buffer.anchor_before(replace_range.start)) .unwrap() @@ -5023,7 +5044,6 @@ impl Editor { multibuffer_anchor.start.to_offset(&snapshot) ..multibuffer_anchor.end.to_offset(&snapshot) }; - let newest_anchor = self.selections.newest_anchor(); if newest_anchor.head().buffer_id != Some(buffer.remote_id()) { return None; } diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 30b7e11cbb81dbc9ef8a8e6b910d9dbdbccda7ba..35477765f4fbf4d85386817cf5c4c11447e149bc 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -5187,15 +5187,25 @@ impl ProjectItem for Buffer { } impl Completion { + pub fn kind(&self) -> Option { + self.source + // `lsp::CompletionListItemDefaults` has no `kind` field + .lsp_completion(false) + .and_then(|lsp_completion| lsp_completion.kind) + } + + pub fn label(&self) -> Option { + self.source + .lsp_completion(false) + .map(|lsp_completion| lsp_completion.label.clone()) + } + /// A key that can be used to sort completions when displaying /// them to the user. pub fn sort_key(&self) -> (usize, &str) { const DEFAULT_KIND_KEY: usize = 3; let kind_key = self - .source - // `lsp::CompletionListItemDefaults` has no `kind` field - .lsp_completion(false) - .and_then(|lsp_completion| lsp_completion.kind) + .kind() .and_then(|lsp_completion_kind| match lsp_completion_kind { lsp::CompletionItemKind::KEYWORD => Some(0), lsp::CompletionItemKind::VARIABLE => Some(1),