Detailed changes
@@ -170,7 +170,7 @@ impl SlashCommandCompletionProvider {
.await?
.into_iter()
.map(|command_argument| {
- let confirm =
+ let confirm = if command_argument.run_command {
editor
.clone()
.zip(workspace.clone())
@@ -178,7 +178,7 @@ impl SlashCommandCompletionProvider {
Arc::new({
let command_range = command_range.clone();
let command_name = command_name.clone();
- let command_argument = command_argument.clone();
+ let command_argument = command_argument.new_text.clone();
move |cx: &mut WindowContext| {
editor
.update(cx, |editor, cx| {
@@ -194,15 +194,24 @@ impl SlashCommandCompletionProvider {
.ok();
}
}) as Arc<_>
- });
+ })
+ } else {
+ None
+ };
+
+ let mut new_text = command_argument.new_text.clone();
+ if !command_argument.run_command {
+ new_text.push(' ');
+ }
+
project::Completion {
old_range: argument_range.clone(),
- label: CodeLabel::plain(command_argument.clone(), None),
- new_text: command_argument.clone(),
+ label: CodeLabel::plain(command_argument.label, None),
+ new_text,
documentation: None,
server_id: LanguageServerId(0),
lsp_completion: Default::default(),
- show_new_completions_on_confirm: false,
+ show_new_completions_on_confirm: !command_argument.run_command,
confirm,
}
})
@@ -4,6 +4,7 @@ use super::{
SlashCommand, SlashCommandOutput,
};
use anyhow::{anyhow, Result};
+use assistant_slash_command::ArgumentCompletion;
use editor::Editor;
use gpui::{AppContext, Task, WeakView};
use language::LspAdapterDelegate;
@@ -33,7 +34,7 @@ impl SlashCommand for ActiveSlashCommand {
_cancel: Arc<AtomicBool>,
_workspace: Option<WeakView<Workspace>>,
_cx: &mut AppContext,
- ) -> Task<Result<Vec<String>>> {
+ ) -> Task<Result<Vec<ArgumentCompletion>>> {
Task::ready(Err(anyhow!("this command does not require argument")))
}
@@ -1,7 +1,7 @@
use super::{SlashCommand, SlashCommandOutput};
use crate::prompt_library::PromptStore;
use anyhow::{anyhow, Result};
-use assistant_slash_command::SlashCommandOutputSection;
+use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection};
use gpui::{AppContext, Task, WeakView};
use language::LspAdapterDelegate;
use std::{
@@ -36,7 +36,7 @@ impl SlashCommand for DefaultSlashCommand {
_cancellation_flag: Arc<AtomicBool>,
_workspace: Option<WeakView<Workspace>>,
_cx: &mut AppContext,
- ) -> Task<Result<Vec<String>>> {
+ ) -> Task<Result<Vec<ArgumentCompletion>>> {
Task::ready(Err(anyhow!("this command does not require argument")))
}
@@ -1,6 +1,6 @@
use super::{create_label_for_command, SlashCommand, SlashCommandOutput};
use anyhow::{anyhow, Result};
-use assistant_slash_command::SlashCommandOutputSection;
+use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection};
use fuzzy::{PathMatch, StringMatchCandidate};
use gpui::{AppContext, Model, Task, View, WeakView};
use language::{
@@ -108,7 +108,7 @@ impl SlashCommand for DiagnosticsSlashCommand {
cancellation_flag: Arc<AtomicBool>,
workspace: Option<WeakView<Workspace>>,
cx: &mut AppContext,
- ) -> Task<Result<Vec<String>>> {
+ ) -> Task<Result<Vec<ArgumentCompletion>>> {
let Some(workspace) = workspace.and_then(|workspace| workspace.upgrade()) else {
return Task::ready(Err(anyhow!("workspace was dropped")));
};
@@ -143,7 +143,14 @@ impl SlashCommand for DiagnosticsSlashCommand {
.map(|candidate| candidate.string),
);
- Ok(matches)
+ Ok(matches
+ .into_iter()
+ .map(|completion| ArgumentCompletion {
+ label: completion.clone(),
+ new_text: completion,
+ run_command: true,
+ })
+ .collect())
})
}
@@ -3,7 +3,9 @@ use std::sync::atomic::AtomicBool;
use std::sync::Arc;
use anyhow::{anyhow, bail, Result};
-use assistant_slash_command::{SlashCommand, SlashCommandOutput, SlashCommandOutputSection};
+use assistant_slash_command::{
+ ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
+};
use gpui::{AppContext, Model, Task, WeakView};
use indexed_docs::{
IndexedDocsRegistry, IndexedDocsStore, LocalProvider, PackageName, ProviderId, RustdocIndexer,
@@ -92,7 +94,7 @@ impl SlashCommand for DocsSlashCommand {
_cancel: Arc<AtomicBool>,
workspace: Option<WeakView<Workspace>>,
cx: &mut AppContext,
- ) -> Task<Result<Vec<String>>> {
+ ) -> Task<Result<Vec<ArgumentCompletion>>> {
self.ensure_rustdoc_provider_is_registered(workspace, cx);
let indexed_docs_registry = IndexedDocsRegistry::global(cx);
@@ -107,10 +109,17 @@ impl SlashCommand for DocsSlashCommand {
///
/// We will likely want to extend `complete_argument` with support for replacing just
/// a particular range of the argument when a completion is accepted.
- fn prefix_with_provider(provider: ProviderId, items: Vec<String>) -> Vec<String> {
+ fn prefix_with_provider(
+ provider: ProviderId,
+ items: Vec<String>,
+ ) -> Vec<ArgumentCompletion> {
items
.into_iter()
- .map(|item| format!("{provider} {item}"))
+ .map(|item| ArgumentCompletion {
+ label: item.clone(),
+ new_text: format!("{provider} {item}"),
+ run_command: true,
+ })
.collect()
}
@@ -119,7 +128,11 @@ impl SlashCommand for DocsSlashCommand {
let providers = indexed_docs_registry.list_providers();
Ok(providers
.into_iter()
- .map(|provider| provider.to_string())
+ .map(|provider| ArgumentCompletion {
+ label: provider.to_string(),
+ new_text: provider.to_string(),
+ run_command: false,
+ })
.collect())
}
DocsSlashCommandArgs::SearchPackageDocs {
@@ -4,7 +4,9 @@ use std::sync::atomic::AtomicBool;
use std::sync::Arc;
use anyhow::{anyhow, bail, Context, Result};
-use assistant_slash_command::{SlashCommand, SlashCommandOutput, SlashCommandOutputSection};
+use assistant_slash_command::{
+ ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
+};
use futures::AsyncReadExt;
use gpui::{AppContext, Task, WeakView};
use html_to_markdown::{convert_html_to_markdown, markdown, TagHandler};
@@ -119,7 +121,7 @@ impl SlashCommand for FetchSlashCommand {
_cancel: Arc<AtomicBool>,
_workspace: Option<WeakView<Workspace>>,
_cx: &mut AppContext,
- ) -> Task<Result<Vec<String>>> {
+ ) -> Task<Result<Vec<ArgumentCompletion>>> {
Task::ready(Ok(Vec::new()))
}
@@ -1,6 +1,6 @@
use super::{diagnostics_command::write_single_file_diagnostics, SlashCommand, SlashCommandOutput};
use anyhow::{anyhow, Result};
-use assistant_slash_command::SlashCommandOutputSection;
+use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection};
use fuzzy::PathMatch;
use gpui::{AppContext, Model, Task, View, WeakView};
use language::{BufferSnapshot, LineEnding, LspAdapterDelegate};
@@ -105,7 +105,7 @@ impl SlashCommand for FileSlashCommand {
cancellation_flag: Arc<AtomicBool>,
workspace: Option<WeakView<Workspace>>,
cx: &mut AppContext,
- ) -> Task<Result<Vec<String>>> {
+ ) -> Task<Result<Vec<ArgumentCompletion>>> {
let Some(workspace) = workspace.and_then(|workspace| workspace.upgrade()) else {
return Task::ready(Err(anyhow!("workspace was dropped")));
};
@@ -116,11 +116,17 @@ impl SlashCommand for FileSlashCommand {
.await
.into_iter()
.map(|path_match| {
- format!(
+ let text = format!(
"{}{}",
path_match.path_prefix,
path_match.path.to_string_lossy()
- )
+ );
+
+ ArgumentCompletion {
+ label: text.clone(),
+ new_text: text,
+ run_command: true,
+ }
})
.collect())
})
@@ -2,7 +2,9 @@ use std::sync::atomic::AtomicBool;
use std::sync::Arc;
use anyhow::Result;
-use assistant_slash_command::{SlashCommand, SlashCommandOutput, SlashCommandOutputSection};
+use assistant_slash_command::{
+ ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
+};
use chrono::Local;
use gpui::{AppContext, Task, WeakView};
use language::LspAdapterDelegate;
@@ -34,7 +36,7 @@ impl SlashCommand for NowSlashCommand {
_cancel: Arc<AtomicBool>,
_workspace: Option<WeakView<Workspace>>,
_cx: &mut AppContext,
- ) -> Task<Result<Vec<String>>> {
+ ) -> Task<Result<Vec<ArgumentCompletion>>> {
Task::ready(Ok(Vec::new()))
}
@@ -1,6 +1,6 @@
use super::{SlashCommand, SlashCommandOutput};
use anyhow::{anyhow, Context, Result};
-use assistant_slash_command::SlashCommandOutputSection;
+use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection};
use fs::Fs;
use gpui::{AppContext, Model, Task, WeakView};
use language::LspAdapterDelegate;
@@ -107,7 +107,7 @@ impl SlashCommand for ProjectSlashCommand {
_cancel: Arc<AtomicBool>,
_workspace: Option<WeakView<Workspace>>,
_cx: &mut AppContext,
- ) -> Task<Result<Vec<String>>> {
+ ) -> Task<Result<Vec<ArgumentCompletion>>> {
Task::ready(Err(anyhow!("this command does not require argument")))
}
@@ -1,7 +1,7 @@
use super::{SlashCommand, SlashCommandOutput};
use crate::prompt_library::PromptStore;
use anyhow::{anyhow, Context, Result};
-use assistant_slash_command::SlashCommandOutputSection;
+use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection};
use gpui::{AppContext, Task, WeakView};
use language::LspAdapterDelegate;
use std::sync::{atomic::AtomicBool, Arc};
@@ -33,13 +33,20 @@ impl SlashCommand for PromptSlashCommand {
_cancellation_flag: Arc<AtomicBool>,
_workspace: Option<WeakView<Workspace>>,
cx: &mut AppContext,
- ) -> Task<Result<Vec<String>>> {
+ ) -> Task<Result<Vec<ArgumentCompletion>>> {
let store = PromptStore::global(cx);
cx.background_executor().spawn(async move {
let prompts = store.await?.search(query).await;
Ok(prompts
.into_iter()
- .filter_map(|prompt| Some(prompt.title?.to_string()))
+ .filter_map(|prompt| {
+ let prompt_title = prompt.title?.to_string();
+ Some(ArgumentCompletion {
+ label: prompt_title.clone(),
+ new_text: prompt_title,
+ run_command: true,
+ })
+ })
.collect())
})
}
@@ -4,7 +4,7 @@ use super::{
SlashCommand, SlashCommandOutput,
};
use anyhow::Result;
-use assistant_slash_command::SlashCommandOutputSection;
+use assistant_slash_command::{ArgumentCompletion, SlashCommandOutputSection};
use gpui::{AppContext, Task, WeakView};
use language::{CodeLabel, LineEnding, LspAdapterDelegate};
use semantic_index::SemanticIndex;
@@ -46,7 +46,7 @@ impl SlashCommand for SearchSlashCommand {
_cancel: Arc<AtomicBool>,
_workspace: Option<WeakView<Workspace>>,
_cx: &mut AppContext,
- ) -> Task<Result<Vec<String>>> {
+ ) -> Task<Result<Vec<ArgumentCompletion>>> {
Task::ready(Ok(Vec::new()))
}
@@ -4,6 +4,7 @@ use super::{
SlashCommand, SlashCommandOutput,
};
use anyhow::{anyhow, Result};
+use assistant_slash_command::ArgumentCompletion;
use collections::HashMap;
use editor::Editor;
use gpui::{AppContext, Entity, Task, WeakView};
@@ -37,7 +38,7 @@ impl SlashCommand for TabsSlashCommand {
_cancel: Arc<std::sync::atomic::AtomicBool>,
_workspace: Option<WeakView<Workspace>>,
_cx: &mut AppContext,
- ) -> Task<Result<Vec<String>>> {
+ ) -> Task<Result<Vec<ArgumentCompletion>>> {
Task::ready(Err(anyhow!("this command does not require argument")))
}
@@ -2,7 +2,9 @@ use std::sync::atomic::AtomicBool;
use std::sync::Arc;
use anyhow::Result;
-use assistant_slash_command::{SlashCommand, SlashCommandOutput, SlashCommandOutputSection};
+use assistant_slash_command::{
+ ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
+};
use gpui::{AppContext, Task, WeakView};
use language::{CodeLabel, LspAdapterDelegate};
use terminal_view::{terminal_panel::TerminalPanel, TerminalView};
@@ -42,8 +44,12 @@ impl SlashCommand for TermSlashCommand {
_cancel: Arc<AtomicBool>,
_workspace: Option<WeakView<Workspace>>,
_cx: &mut AppContext,
- ) -> Task<Result<Vec<String>>> {
- Task::ready(Ok(vec![LINE_COUNT_ARG.to_string()]))
+ ) -> Task<Result<Vec<ArgumentCompletion>>> {
+ Task::ready(Ok(vec![ArgumentCompletion {
+ label: LINE_COUNT_ARG.to_string(),
+ new_text: LINE_COUNT_ARG.to_string(),
+ run_command: true,
+ }]))
}
fn run(
@@ -15,6 +15,16 @@ pub fn init(cx: &mut AppContext) {
SlashCommandRegistry::default_global(cx);
}
+#[derive(Debug)]
+pub struct ArgumentCompletion {
+ /// The label to display for this completion.
+ pub label: String,
+ /// The new text that should be inserted into the command when this completion is accepted.
+ pub new_text: String,
+ /// Whether the command should be run when accepting this completion.
+ pub run_command: bool,
+}
+
pub trait SlashCommand: 'static + Send + Sync {
fn name(&self) -> String;
fn label(&self, _cx: &AppContext) -> CodeLabel {
@@ -28,7 +38,7 @@ pub trait SlashCommand: 'static + Send + Sync {
cancel: Arc<AtomicBool>,
workspace: Option<WeakView<Workspace>>,
cx: &mut AppContext,
- ) -> Task<Result<Vec<String>>>;
+ ) -> Task<Result<Vec<ArgumentCompletion>>>;
fn requires_argument(&self) -> bool;
fn run(
self: Arc<Self>,
@@ -1,7 +1,9 @@
use std::sync::{atomic::AtomicBool, Arc};
use anyhow::{anyhow, Result};
-use assistant_slash_command::{SlashCommand, SlashCommandOutput, SlashCommandOutputSection};
+use assistant_slash_command::{
+ ArgumentCompletion, SlashCommand, SlashCommandOutput, SlashCommandOutputSection,
+};
use futures::FutureExt;
use gpui::{AppContext, Task, WeakView, WindowContext};
use language::LspAdapterDelegate;
@@ -41,7 +43,7 @@ impl SlashCommand for ExtensionSlashCommand {
_cancel: Arc<AtomicBool>,
_workspace: Option<WeakView<Workspace>>,
cx: &mut AppContext,
- ) -> Task<Result<Vec<String>>> {
+ ) -> Task<Result<Vec<ArgumentCompletion>>> {
cx.background_executor().spawn(async move {
self.extension
.call({
@@ -57,7 +59,16 @@ impl SlashCommand for ExtensionSlashCommand {
.await?
.map_err(|e| anyhow!("{}", e))?;
- anyhow::Ok(completions)
+ anyhow::Ok(
+ completions
+ .into_iter()
+ .map(|completion| ArgumentCompletion {
+ label: completion.clone(),
+ new_text: completion,
+ run_command: true,
+ })
+ .collect(),
+ )
}
.boxed()
}