From c45adce2e32651633844b9b0203c42d2f0509873 Mon Sep 17 00:00:00 2001 From: Kirill Bulatov Date: Thu, 15 Aug 2024 19:36:30 +0300 Subject: [PATCH] Run slash commands both on enter and on argument completion that requires it (#16283) Release Notes: - N/A --- crates/assistant/src/assistant_panel.rs | 6 +++- crates/assistant/src/slash_command.rs | 36 ++++++++++--------- .../src/slash_command/diagnostics_command.rs | 4 +++ .../src/slash_command/tab_command.rs | 15 +++++--- .../src/slash_command/terminal_command.rs | 4 +++ .../src/assistant_slash_command.rs | 3 ++ .../src/chat_panel/message_editor.rs | 1 - crates/editor/src/editor.rs | 17 +++++---- crates/project/src/project.rs | 9 +++-- 9 files changed, 61 insertions(+), 34 deletions(-) diff --git a/crates/assistant/src/assistant_panel.rs b/crates/assistant/src/assistant_panel.rs index 6101403c03..c5e925b8ec 100644 --- a/crates/assistant/src/assistant_panel.rs +++ b/crates/assistant/src/assistant_panel.rs @@ -2081,7 +2081,7 @@ impl ContextEditor { } editor.insert(&format!("/{name}"), cx); - if command.requires_argument() { + if command.accepts_arguments() { editor.insert(" ", cx); editor.show_completions(&ShowCompletions::default(), cx); } @@ -2094,6 +2094,10 @@ impl ContextEditor { } pub fn confirm_command(&mut self, _: &ConfirmCommand, cx: &mut ViewContext) { + if self.editor.read(cx).has_active_completions_menu() { + return; + } + let selections = self.editor.read(cx).selections.disjoint_anchors(); let mut commands_by_range = HashMap::default(); let workspace = self.workspace.clone(); diff --git a/crates/assistant/src/slash_command.rs b/crates/assistant/src/slash_command.rs index 9f0d24ea26..d938225dab 100644 --- a/crates/assistant/src/slash_command.rs +++ b/crates/assistant/src/slash_command.rs @@ -97,20 +97,25 @@ impl SlashCommandCompletionProvider { let command = commands.command(&mat.string)?; let mut new_text = mat.string.clone(); let requires_argument = command.requires_argument(); - if requires_argument { + let accepts_arguments = command.accepts_arguments(); + if requires_argument || accepts_arguments { new_text.push(' '); } - let confirm = editor.clone().zip(workspace.clone()).and_then( - |(editor, workspace)| { - (!requires_argument).then(|| { + let confirm = + editor + .clone() + .zip(workspace.clone()) + .map(|(editor, workspace)| { let command_name = mat.string.clone(); let command_range = command_range.clone(); let editor = editor.clone(); let workspace = workspace.clone(); Arc::new( move |intent: CompletionIntent, cx: &mut WindowContext| { - if intent.is_complete() { + if !requires_argument + && (!accepts_arguments || intent.is_complete()) + { editor .update(cx, |editor, cx| { editor.run_command( @@ -123,12 +128,13 @@ impl SlashCommandCompletionProvider { ); }) .ok(); + false + } else { + requires_argument || accepts_arguments } }, ) as Arc<_> - }) - }, - ); + }); Some(project::Completion { old_range: name_range.clone(), documentation: Some(Documentation::SingleLine(command.description())), @@ -136,7 +142,6 @@ impl SlashCommandCompletionProvider { label: command.label(cx), server_id: LanguageServerId(0), lsp_completion: Default::default(), - show_new_completions_on_confirm: requires_argument, confirm, }) }) @@ -175,7 +180,7 @@ impl SlashCommandCompletionProvider { .await? .into_iter() .map(|new_argument| { - let confirm = if new_argument.run_command { + let confirm = editor .clone() .zip(workspace.clone()) @@ -192,7 +197,7 @@ impl SlashCommandCompletionProvider { let command_range = command_range.clone(); let command_name = command_name.clone(); move |intent: CompletionIntent, cx: &mut WindowContext| { - if intent.is_complete() { + if new_argument.run_command || intent.is_complete() { editor .update(cx, |editor, cx| { editor.run_command( @@ -205,13 +210,13 @@ impl SlashCommandCompletionProvider { ); }) .ok(); + false + } else { + !new_argument.run_command } } }) as Arc<_> - }) - } else { - None - }; + }); let mut new_text = new_argument.new_text.clone(); if !new_argument.run_command { @@ -229,7 +234,6 @@ impl SlashCommandCompletionProvider { documentation: None, server_id: LanguageServerId(0), lsp_completion: Default::default(), - show_new_completions_on_confirm: !new_argument.run_command, confirm, } }) diff --git a/crates/assistant/src/slash_command/diagnostics_command.rs b/crates/assistant/src/slash_command/diagnostics_command.rs index a23d0a098b..46342fc945 100644 --- a/crates/assistant/src/slash_command/diagnostics_command.rs +++ b/crates/assistant/src/slash_command/diagnostics_command.rs @@ -103,6 +103,10 @@ impl SlashCommand for DiagnosticsSlashCommand { false } + fn accepts_arguments(&self) -> bool { + true + } + fn complete_argument( self: Arc, arguments: &[String], diff --git a/crates/assistant/src/slash_command/tab_command.rs b/crates/assistant/src/slash_command/tab_command.rs index 392f7c65d8..de590ef837 100644 --- a/crates/assistant/src/slash_command/tab_command.rs +++ b/crates/assistant/src/slash_command/tab_command.rs @@ -39,6 +39,10 @@ impl SlashCommand for TabSlashCommand { false } + fn accepts_arguments(&self) -> bool { + true + } + fn complete_argument( self: Arc, arguments: &[String], @@ -94,15 +98,16 @@ impl SlashCommand for TabSlashCommand { }) }); - let active_item_completion = active_item_path.as_deref().map(|active_item_path| { - let path_string = active_item_path.to_string_lossy().to_string(); - ArgumentCompletion { + let active_item_completion = active_item_path + .as_deref() + .map(|active_item_path| active_item_path.to_string_lossy().to_string()) + .filter(|path_string| !argument_set.contains(path_string)) + .map(|path_string| ArgumentCompletion { label: path_string.clone().into(), new_text: path_string, replace_previous_arguments: false, run_command, - } - }); + }); Ok(active_item_completion .into_iter() diff --git a/crates/assistant/src/slash_command/terminal_command.rs b/crates/assistant/src/slash_command/terminal_command.rs index 6b899493fb..04baabd396 100644 --- a/crates/assistant/src/slash_command/terminal_command.rs +++ b/crates/assistant/src/slash_command/terminal_command.rs @@ -40,6 +40,10 @@ impl SlashCommand for TerminalSlashCommand { false } + fn accepts_arguments(&self) -> bool { + true + } + fn complete_argument( self: Arc, _arguments: &[String], diff --git a/crates/assistant_slash_command/src/assistant_slash_command.rs b/crates/assistant_slash_command/src/assistant_slash_command.rs index 00671338df..1a9cad5611 100644 --- a/crates/assistant_slash_command/src/assistant_slash_command.rs +++ b/crates/assistant_slash_command/src/assistant_slash_command.rs @@ -42,6 +42,9 @@ pub trait SlashCommand: 'static + Send + Sync { cx: &mut WindowContext, ) -> Task>>; fn requires_argument(&self) -> bool; + fn accepts_arguments(&self) -> bool { + self.requires_argument() + } fn run( self: Arc, arguments: &[String], diff --git a/crates/collab_ui/src/chat_panel/message_editor.rs b/crates/collab_ui/src/chat_panel/message_editor.rs index 35502a3b14..9e1530be31 100644 --- a/crates/collab_ui/src/chat_panel/message_editor.rs +++ b/crates/collab_ui/src/chat_panel/message_editor.rs @@ -314,7 +314,6 @@ impl MessageEditor { server_id: LanguageServerId(0), // TODO: Make this optional or something? lsp_completion: Default::default(), // TODO: Make this optional or something? confirm: None, - show_new_completions_on_confirm: false, } }) .collect() diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index 84a81cb9c9..915a07fbd7 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -4379,11 +4379,11 @@ impl Editor { this.refresh_inline_completion(true, cx); }); - if let Some(confirm) = completion.confirm.as_ref() { - (confirm)(intent, cx); - } - - if completion.show_new_completions_on_confirm { + let show_new_completions_on_confirm = completion + .confirm + .as_ref() + .map_or(false, |confirm| confirm(intent, cx)); + if show_new_completions_on_confirm { self.show_completions(&ShowCompletions { trigger: None }, cx); } @@ -11926,6 +11926,12 @@ impl Editor { let bounds = self.last_bounds?; Some(element::gutter_bounds(bounds, self.gutter_dimensions)) } + + pub fn has_active_completions_menu(&self) -> bool { + self.context_menu.read().as_ref().map_or(false, |menu| { + menu.visible() && matches!(menu, ContextMenu::Completions(_)) + }) + } } fn hunks_for_selections( @@ -12141,7 +12147,6 @@ fn snippet_completions( ..Default::default() }, confirm: None, - show_new_completions_on_confirm: false, }) }) .collect() diff --git a/crates/project/src/project.rs b/crates/project/src/project.rs index 0b08071ca2..af5cc97c4f 100644 --- a/crates/project/src/project.rs +++ b/crates/project/src/project.rs @@ -450,9 +450,10 @@ pub struct Completion { /// The raw completion provided by the language server. pub lsp_completion: lsp::CompletionItem, /// An optional callback to invoke when this completion is confirmed. - pub confirm: Option>, - /// If true, the editor will show a new completion menu after this completion is confirmed. - pub show_new_completions_on_confirm: bool, + /// Returns, whether new completions should be retriggered after the current one. + /// If `true` is returned, the editor will show a new completion menu after this completion is confirmed. + /// if no confirmation is provided or `false` is returned, the completion will be committed. + pub confirm: Option bool>>, } impl std::fmt::Debug for Completion { @@ -9128,7 +9129,6 @@ impl Project { filter_range: Default::default(), }, confirm: None, - show_new_completions_on_confirm: false, }, false, cx, @@ -10765,7 +10765,6 @@ async fn populate_labels_for_completions( documentation, lsp_completion, confirm: None, - show_new_completions_on_confirm: false, }) } }