From d8eb341f9bd702a74f9272ee9f4e96ddea7dc1b6 Mon Sep 17 00:00:00 2001 From: Michael Sloan Date: Wed, 18 Jun 2025 03:01:47 -0600 Subject: [PATCH] Fix bug where prior LSP completions can be displayed after trigger char (#32927) Bug in #31872 Closes #32774 Release Notes: - Fixed a bug in LSP completions caching where prior completions may be used when they should not, after typing a trigger char like `.` --- crates/editor/src/editor.rs | 48 ++++++++++++++++++++++++------------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index d5032d4485..c02095794c 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -5266,6 +5266,37 @@ impl Editor { .as_ref() .map_or(true, |provider| provider.filter_completions()); + let trigger_kind = match trigger { + Some(trigger) if buffer.read(cx).completion_triggers().contains(trigger) => { + CompletionTriggerKind::TRIGGER_CHARACTER + } + _ => CompletionTriggerKind::INVOKED, + }; + let completion_context = CompletionContext { + trigger_character: trigger.and_then(|trigger| { + if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER { + Some(String::from(trigger)) + } else { + None + } + }), + trigger_kind, + }; + + // Hide the current completions menu when a trigger char is typed. Without this, cached + // completions from before the trigger char may be reused (#32774). Snippet choices could + // involve trigger chars, so this is skipped in that case. + if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER && self.snippet_stack.is_empty() + { + let menu_is_open = matches!( + self.context_menu.borrow().as_ref(), + Some(CodeContextMenu::Completions(_)) + ); + if menu_is_open { + self.hide_context_menu(window, cx); + } + } + if let Some(CodeContextMenu::Completions(menu)) = self.context_menu.borrow_mut().as_mut() { if filter_completions { menu.filter(query.clone(), provider.clone(), window, cx); @@ -5296,23 +5327,6 @@ impl Editor { } }; - let trigger_kind = match trigger { - Some(trigger) if buffer.read(cx).completion_triggers().contains(trigger) => { - CompletionTriggerKind::TRIGGER_CHARACTER - } - _ => CompletionTriggerKind::INVOKED, - }; - let completion_context = CompletionContext { - trigger_character: trigger.and_then(|trigger| { - if trigger_kind == CompletionTriggerKind::TRIGGER_CHARACTER { - Some(String::from(trigger)) - } else { - None - } - }), - trigger_kind, - }; - let (word_replace_range, word_to_exclude) = if let (word_range, Some(CharKind::Word)) = buffer_snapshot.surrounding_word(buffer_position) {