@@ -125,6 +125,7 @@ pub(crate) fn create_editor(
cx,
);
editor.set_placeholder_text("Message the agent – @ to include context", cx);
+ editor.disable_word_completions();
editor.set_show_indent_guides(false, cx);
editor.set_soft_wrap();
editor.set_use_modal_editing(true);
@@ -251,7 +251,7 @@ enum MarkdownCacheKey {
pub enum CompletionsMenuSource {
Normal,
SnippetChoices,
- Words,
+ Words { ignore_threshold: bool },
}
// TODO: There should really be a wrapper around fuzzy match tasks that does this.
@@ -1030,6 +1030,7 @@ pub struct Editor {
inline_diagnostics_update: Task<()>,
inline_diagnostics_enabled: bool,
diagnostics_enabled: bool,
+ word_completions_enabled: bool,
inline_diagnostics: Vec<(Anchor, InlineDiagnostic)>,
soft_wrap_mode_override: Option<language_settings::SoftWrap>,
hard_wrap: Option<usize>,
@@ -2163,6 +2164,7 @@ impl Editor {
},
inline_diagnostics_enabled: full_mode,
diagnostics_enabled: full_mode,
+ word_completions_enabled: full_mode,
inline_value_cache: InlineValueCache::new(inlay_hint_settings.show_value_hints),
inlay_hint_cache: InlayHintCache::new(inlay_hint_settings),
gutter_hovered: false,
@@ -4892,8 +4894,15 @@ impl Editor {
});
match completions_source {
- Some(CompletionsMenuSource::Words) => {
- self.show_word_completions(&ShowWordCompletions, window, cx)
+ Some(CompletionsMenuSource::Words { .. }) => {
+ self.open_or_update_completions_menu(
+ Some(CompletionsMenuSource::Words {
+ ignore_threshold: false,
+ }),
+ None,
+ window,
+ cx,
+ );
}
Some(CompletionsMenuSource::Normal)
| Some(CompletionsMenuSource::SnippetChoices)
@@ -5401,7 +5410,14 @@ impl Editor {
window: &mut Window,
cx: &mut Context<Self>,
) {
- self.open_or_update_completions_menu(Some(CompletionsMenuSource::Words), None, window, cx);
+ self.open_or_update_completions_menu(
+ Some(CompletionsMenuSource::Words {
+ ignore_threshold: true,
+ }),
+ None,
+ window,
+ cx,
+ );
}
pub fn show_completions(
@@ -5450,9 +5466,13 @@ impl Editor {
drop(multibuffer_snapshot);
+ let mut ignore_word_threshold = false;
let provider = match requested_source {
Some(CompletionsMenuSource::Normal) | None => self.completion_provider.clone(),
- Some(CompletionsMenuSource::Words) => None,
+ Some(CompletionsMenuSource::Words { ignore_threshold }) => {
+ ignore_word_threshold = ignore_threshold;
+ None
+ }
Some(CompletionsMenuSource::SnippetChoices) => {
log::error!("bug: SnippetChoices requested_source is not handled");
None
@@ -5573,10 +5593,12 @@ impl Editor {
.as_ref()
.is_none_or(|query| !query.chars().any(|c| c.is_digit(10)));
- let omit_word_completions = match &query {
- Some(query) => query.chars().count() < completion_settings.words_min_length,
- None => completion_settings.words_min_length != 0,
- };
+ let omit_word_completions = !self.word_completions_enabled
+ || (!ignore_word_threshold
+ && match &query {
+ Some(query) => query.chars().count() < completion_settings.words_min_length,
+ None => completion_settings.words_min_length != 0,
+ });
let (mut words, provider_responses) = match &provider {
Some(provider) => {
@@ -17121,6 +17143,10 @@ impl Editor {
self.inline_diagnostics.clear();
}
+ pub fn disable_word_completions(&mut self) {
+ self.word_completions_enabled = false;
+ }
+
pub fn diagnostics_enabled(&self) -> bool {
self.diagnostics_enabled && self.mode.is_full()
}
@@ -14278,6 +14278,26 @@ async fn test_word_completions_do_not_show_before_threshold(cx: &mut TestAppCont
}
});
+ cx.update_editor(|editor, window, cx| {
+ editor.show_word_completions(&ShowWordCompletions, window, cx);
+ });
+ cx.executor().run_until_parked();
+ cx.update_editor(|editor, window, cx| {
+ if let Some(CodeContextMenu::Completions(menu)) = editor.context_menu.borrow_mut().as_ref()
+ {
+ assert_eq!(completion_menu_entries(menu), &["wowser", "wowen", "wow"], "Even though the threshold is not met, invoking word completions with an action should provide the completions");
+ } else {
+ panic!("expected completion menu to be open after the word completions are called with an action");
+ }
+
+ editor.cancel(&Cancel, window, cx);
+ });
+ cx.update_editor(|editor, _, _| {
+ if editor.context_menu.borrow_mut().is_some() {
+ panic!("expected completion menu to be hidden after canceling");
+ }
+ });
+
cx.simulate_keystroke("o");
cx.executor().run_until_parked();
cx.update_editor(|editor, _, _| {
@@ -14300,6 +14320,50 @@ async fn test_word_completions_do_not_show_before_threshold(cx: &mut TestAppCont
});
}
+#[gpui::test]
+async fn test_word_completions_disabled(cx: &mut TestAppContext) {
+ init_test(cx, |language_settings| {
+ language_settings.defaults.completions = Some(CompletionSettings {
+ words: WordsCompletionMode::Enabled,
+ words_min_length: 0,
+ lsp: true,
+ lsp_fetch_timeout_ms: 0,
+ lsp_insert_mode: LspInsertMode::Insert,
+ });
+ });
+
+ let mut cx = EditorLspTestContext::new_rust(lsp::ServerCapabilities::default(), cx).await;
+ cx.update_editor(|editor, _, _| {
+ editor.disable_word_completions();
+ });
+ cx.set_state(indoc! {"ˇ
+ wow
+ wowen
+ wowser
+ "});
+ cx.simulate_keystroke("w");
+ cx.executor().run_until_parked();
+ cx.update_editor(|editor, _, _| {
+ if editor.context_menu.borrow_mut().is_some() {
+ panic!(
+ "expected completion menu to be hidden, as words completion are disabled for this editor"
+ );
+ }
+ });
+
+ cx.update_editor(|editor, window, cx| {
+ editor.show_word_completions(&ShowWordCompletions, window, cx);
+ });
+ cx.executor().run_until_parked();
+ cx.update_editor(|editor, _, _| {
+ if editor.context_menu.borrow_mut().is_some() {
+ panic!(
+ "expected completion menu to be hidden even if called for explicitly, as words completion are disabled for this editor"
+ );
+ }
+ });
+}
+
fn gen_text_edit(params: &CompletionParams, text: &str) -> Option<lsp::CompletionTextEdit> {
let position = || lsp::Position {
line: params.text_document_position.position.line,