Further improve /tabs command and slash arguments completion (#16216)
* renames `/tabs` to `/tab` * allows to insert multiple tabs when fuzzy matching by the names * improve slash command completion API, introduce a notion of multiple arguments * properly fire off commands on arguments' completions with `run_command: true` Release Notes: - N/A --------- Co-authored-by: Marshall Bowers <marshall@zed.dev>
This commit is contained in:
parent
88a12b60a9
commit
8fe2de1737
23 changed files with 332 additions and 263 deletions
|
@ -37,7 +37,7 @@ use serde::{Deserialize, Serialize};
|
||||||
use settings::{update_settings_file, Settings, SettingsStore};
|
use settings::{update_settings_file, Settings, SettingsStore};
|
||||||
use slash_command::{
|
use slash_command::{
|
||||||
default_command, diagnostics_command, docs_command, fetch_command, file_command, now_command,
|
default_command, diagnostics_command, docs_command, fetch_command, file_command, now_command,
|
||||||
project_command, prompt_command, search_command, symbols_command, tabs_command,
|
project_command, prompt_command, search_command, symbols_command, tab_command,
|
||||||
terminal_command, workflow_command,
|
terminal_command, workflow_command,
|
||||||
};
|
};
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
@ -294,7 +294,7 @@ fn register_slash_commands(prompt_builder: Option<Arc<PromptBuilder>>, cx: &mut
|
||||||
let slash_command_registry = SlashCommandRegistry::global(cx);
|
let slash_command_registry = SlashCommandRegistry::global(cx);
|
||||||
slash_command_registry.register_command(file_command::FileSlashCommand, true);
|
slash_command_registry.register_command(file_command::FileSlashCommand, true);
|
||||||
slash_command_registry.register_command(symbols_command::OutlineSlashCommand, true);
|
slash_command_registry.register_command(symbols_command::OutlineSlashCommand, true);
|
||||||
slash_command_registry.register_command(tabs_command::TabsSlashCommand, true);
|
slash_command_registry.register_command(tab_command::TabSlashCommand, true);
|
||||||
slash_command_registry.register_command(project_command::ProjectSlashCommand, true);
|
slash_command_registry.register_command(project_command::ProjectSlashCommand, true);
|
||||||
slash_command_registry.register_command(prompt_command::PromptSlashCommand, true);
|
slash_command_registry.register_command(prompt_command::PromptSlashCommand, true);
|
||||||
slash_command_registry.register_command(default_command::DefaultSlashCommand, false);
|
slash_command_registry.register_command(default_command::DefaultSlashCommand, false);
|
||||||
|
|
|
@ -1814,7 +1814,7 @@ impl ContextEditor {
|
||||||
self.run_command(
|
self.run_command(
|
||||||
command.source_range,
|
command.source_range,
|
||||||
&command.name,
|
&command.name,
|
||||||
command.argument.as_deref(),
|
&command.arguments,
|
||||||
false,
|
false,
|
||||||
self.workspace.clone(),
|
self.workspace.clone(),
|
||||||
cx,
|
cx,
|
||||||
|
@ -2120,7 +2120,7 @@ impl ContextEditor {
|
||||||
self.run_command(
|
self.run_command(
|
||||||
command.source_range,
|
command.source_range,
|
||||||
&command.name,
|
&command.name,
|
||||||
command.argument.as_deref(),
|
&command.arguments,
|
||||||
true,
|
true,
|
||||||
workspace.clone(),
|
workspace.clone(),
|
||||||
cx,
|
cx,
|
||||||
|
@ -2134,19 +2134,13 @@ impl ContextEditor {
|
||||||
&mut self,
|
&mut self,
|
||||||
command_range: Range<language::Anchor>,
|
command_range: Range<language::Anchor>,
|
||||||
name: &str,
|
name: &str,
|
||||||
argument: Option<&str>,
|
arguments: &[String],
|
||||||
insert_trailing_newline: bool,
|
insert_trailing_newline: bool,
|
||||||
workspace: WeakView<Workspace>,
|
workspace: WeakView<Workspace>,
|
||||||
cx: &mut ViewContext<Self>,
|
cx: &mut ViewContext<Self>,
|
||||||
) {
|
) {
|
||||||
if let Some(command) = SlashCommandRegistry::global(cx).command(name) {
|
if let Some(command) = SlashCommandRegistry::global(cx).command(name) {
|
||||||
let argument = argument.map(ToString::to_string);
|
let output = command.run(arguments, workspace, self.lsp_adapter_delegate.clone(), cx);
|
||||||
let output = command.run(
|
|
||||||
argument.as_deref(),
|
|
||||||
workspace,
|
|
||||||
self.lsp_adapter_delegate.clone(),
|
|
||||||
cx,
|
|
||||||
);
|
|
||||||
self.context.update(cx, |context, cx| {
|
self.context.update(cx, |context, cx| {
|
||||||
context.insert_command_output(command_range, output, insert_trailing_newline, cx)
|
context.insert_command_output(command_range, output, insert_trailing_newline, cx)
|
||||||
});
|
});
|
||||||
|
@ -2232,7 +2226,7 @@ impl ContextEditor {
|
||||||
context_editor.run_command(
|
context_editor.run_command(
|
||||||
command.source_range.clone(),
|
command.source_range.clone(),
|
||||||
&command.name,
|
&command.name,
|
||||||
command.argument.as_deref(),
|
&command.arguments,
|
||||||
false,
|
false,
|
||||||
workspace.clone(),
|
workspace.clone(),
|
||||||
cx,
|
cx,
|
||||||
|
@ -2345,7 +2339,7 @@ impl ContextEditor {
|
||||||
self.run_command(
|
self.run_command(
|
||||||
command.source_range,
|
command.source_range,
|
||||||
&command.name,
|
&command.name,
|
||||||
command.argument.as_deref(),
|
&command.arguments,
|
||||||
false,
|
false,
|
||||||
self.workspace.clone(),
|
self.workspace.clone(),
|
||||||
cx,
|
cx,
|
||||||
|
@ -4559,11 +4553,10 @@ fn render_docs_slash_command_trailer(
|
||||||
command: PendingSlashCommand,
|
command: PendingSlashCommand,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
) -> AnyElement {
|
) -> AnyElement {
|
||||||
let Some(argument) = command.argument else {
|
if command.arguments.is_empty() {
|
||||||
return Empty.into_any();
|
return Empty.into_any();
|
||||||
};
|
}
|
||||||
|
let args = DocsSlashCommandArgs::parse(&command.arguments);
|
||||||
let args = DocsSlashCommandArgs::parse(&argument);
|
|
||||||
|
|
||||||
let Some(store) = args
|
let Some(store) = args
|
||||||
.provider()
|
.provider()
|
||||||
|
|
|
@ -1206,21 +1206,31 @@ impl Context {
|
||||||
while let Some(line) = lines.next() {
|
while let Some(line) = lines.next() {
|
||||||
if let Some(command_line) = SlashCommandLine::parse(line) {
|
if let Some(command_line) = SlashCommandLine::parse(line) {
|
||||||
let name = &line[command_line.name.clone()];
|
let name = &line[command_line.name.clone()];
|
||||||
let argument = command_line.argument.as_ref().and_then(|argument| {
|
let arguments = command_line
|
||||||
(!argument.is_empty()).then_some(&line[argument.clone()])
|
.arguments
|
||||||
});
|
.iter()
|
||||||
|
.filter_map(|argument_range| {
|
||||||
|
if argument_range.is_empty() {
|
||||||
|
None
|
||||||
|
} else {
|
||||||
|
line.get(argument_range.clone())
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.map(ToOwned::to_owned)
|
||||||
|
.collect::<SmallVec<_>>();
|
||||||
if let Some(command) = SlashCommandRegistry::global(cx).command(name) {
|
if let Some(command) = SlashCommandRegistry::global(cx).command(name) {
|
||||||
if !command.requires_argument() || argument.is_some() {
|
if !command.requires_argument() || !arguments.is_empty() {
|
||||||
let start_ix = offset + command_line.name.start - 1;
|
let start_ix = offset + command_line.name.start - 1;
|
||||||
let end_ix = offset
|
let end_ix = offset
|
||||||
+ command_line
|
+ command_line
|
||||||
.argument
|
.arguments
|
||||||
|
.last()
|
||||||
.map_or(command_line.name.end, |argument| argument.end);
|
.map_or(command_line.name.end, |argument| argument.end);
|
||||||
let source_range =
|
let source_range =
|
||||||
buffer.anchor_after(start_ix)..buffer.anchor_after(end_ix);
|
buffer.anchor_after(start_ix)..buffer.anchor_after(end_ix);
|
||||||
let pending_command = PendingSlashCommand {
|
let pending_command = PendingSlashCommand {
|
||||||
name: name.to_string(),
|
name: name.to_string(),
|
||||||
argument: argument.map(ToString::to_string),
|
arguments,
|
||||||
source_range,
|
source_range,
|
||||||
status: PendingSlashCommandStatus::Idle,
|
status: PendingSlashCommandStatus::Idle,
|
||||||
};
|
};
|
||||||
|
@ -2457,7 +2467,7 @@ impl ContextVersion {
|
||||||
#[derive(Debug, Clone)]
|
#[derive(Debug, Clone)]
|
||||||
pub struct PendingSlashCommand {
|
pub struct PendingSlashCommand {
|
||||||
pub name: String,
|
pub name: String,
|
||||||
pub argument: Option<String>,
|
pub arguments: SmallVec<[String; 3]>,
|
||||||
pub status: PendingSlashCommandStatus,
|
pub status: PendingSlashCommandStatus,
|
||||||
pub source_range: Range<language::Anchor>,
|
pub source_range: Range<language::Anchor>,
|
||||||
}
|
}
|
||||||
|
@ -3758,7 +3768,7 @@ mod tests {
|
||||||
|
|
||||||
fn complete_argument(
|
fn complete_argument(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
_query: String,
|
_arguments: &[String],
|
||||||
_cancel: Arc<AtomicBool>,
|
_cancel: Arc<AtomicBool>,
|
||||||
_workspace: Option<WeakView<Workspace>>,
|
_workspace: Option<WeakView<Workspace>>,
|
||||||
_cx: &mut WindowContext,
|
_cx: &mut WindowContext,
|
||||||
|
@ -3772,7 +3782,7 @@ mod tests {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
_argument: Option<&str>,
|
_arguments: &[String],
|
||||||
_workspace: WeakView<Workspace>,
|
_workspace: WeakView<Workspace>,
|
||||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||||
_cx: &mut WindowContext,
|
_cx: &mut WindowContext,
|
||||||
|
|
|
@ -28,7 +28,7 @@ pub mod project_command;
|
||||||
pub mod prompt_command;
|
pub mod prompt_command;
|
||||||
pub mod search_command;
|
pub mod search_command;
|
||||||
pub mod symbols_command;
|
pub mod symbols_command;
|
||||||
pub mod tabs_command;
|
pub mod tab_command;
|
||||||
pub mod terminal_command;
|
pub mod terminal_command;
|
||||||
pub mod workflow_command;
|
pub mod workflow_command;
|
||||||
|
|
||||||
|
@ -41,8 +41,8 @@ pub(crate) struct SlashCommandCompletionProvider {
|
||||||
pub(crate) struct SlashCommandLine {
|
pub(crate) struct SlashCommandLine {
|
||||||
/// The range within the line containing the command name.
|
/// The range within the line containing the command name.
|
||||||
pub name: Range<usize>,
|
pub name: Range<usize>,
|
||||||
/// The range within the line containing the command argument.
|
/// Ranges within the line containing the command arguments.
|
||||||
pub argument: Option<Range<usize>>,
|
pub arguments: Vec<Range<usize>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl SlashCommandCompletionProvider {
|
impl SlashCommandCompletionProvider {
|
||||||
|
@ -115,7 +115,7 @@ impl SlashCommandCompletionProvider {
|
||||||
editor.run_command(
|
editor.run_command(
|
||||||
command_range.clone(),
|
command_range.clone(),
|
||||||
&command_name,
|
&command_name,
|
||||||
None,
|
&[],
|
||||||
true,
|
true,
|
||||||
workspace.clone(),
|
workspace.clone(),
|
||||||
cx,
|
cx,
|
||||||
|
@ -147,7 +147,7 @@ impl SlashCommandCompletionProvider {
|
||||||
fn complete_command_argument(
|
fn complete_command_argument(
|
||||||
&self,
|
&self,
|
||||||
command_name: &str,
|
command_name: &str,
|
||||||
argument: String,
|
arguments: &[String],
|
||||||
command_range: Range<Anchor>,
|
command_range: Range<Anchor>,
|
||||||
argument_range: Range<Anchor>,
|
argument_range: Range<Anchor>,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
|
@ -159,7 +159,7 @@ impl SlashCommandCompletionProvider {
|
||||||
let commands = SlashCommandRegistry::global(cx);
|
let commands = SlashCommandRegistry::global(cx);
|
||||||
if let Some(command) = commands.command(command_name) {
|
if let Some(command) = commands.command(command_name) {
|
||||||
let completions = command.complete_argument(
|
let completions = command.complete_argument(
|
||||||
argument,
|
arguments,
|
||||||
new_cancel_flag.clone(),
|
new_cancel_flag.clone(),
|
||||||
self.workspace.clone(),
|
self.workspace.clone(),
|
||||||
cx,
|
cx,
|
||||||
|
@ -167,35 +167,37 @@ impl SlashCommandCompletionProvider {
|
||||||
let command_name: Arc<str> = command_name.into();
|
let command_name: Arc<str> = command_name.into();
|
||||||
let editor = self.editor.clone();
|
let editor = self.editor.clone();
|
||||||
let workspace = self.workspace.clone();
|
let workspace = self.workspace.clone();
|
||||||
|
let arguments = arguments.to_vec();
|
||||||
cx.background_executor().spawn(async move {
|
cx.background_executor().spawn(async move {
|
||||||
Ok(completions
|
Ok(completions
|
||||||
.await?
|
.await?
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|command_argument| {
|
.map(|new_argument| {
|
||||||
let confirm = if command_argument.run_command {
|
let confirm = if new_argument.run_command {
|
||||||
editor
|
editor
|
||||||
.clone()
|
.clone()
|
||||||
.zip(workspace.clone())
|
.zip(workspace.clone())
|
||||||
.map(|(editor, workspace)| {
|
.map(|(editor, workspace)| {
|
||||||
Arc::new({
|
Arc::new({
|
||||||
|
let mut completed_arguments = arguments.clone();
|
||||||
|
completed_arguments.pop();
|
||||||
|
completed_arguments.push(new_argument.new_text.clone());
|
||||||
|
|
||||||
let command_range = command_range.clone();
|
let command_range = command_range.clone();
|
||||||
let command_name = command_name.clone();
|
let command_name = command_name.clone();
|
||||||
let command_argument = command_argument.new_text.clone();
|
move |_: CompletionIntent, cx: &mut WindowContext| {
|
||||||
move |intent: CompletionIntent, cx: &mut WindowContext| {
|
editor
|
||||||
if intent.is_complete() {
|
.update(cx, |editor, cx| {
|
||||||
editor
|
editor.run_command(
|
||||||
.update(cx, |editor, cx| {
|
command_range.clone(),
|
||||||
editor.run_command(
|
&command_name,
|
||||||
command_range.clone(),
|
&completed_arguments,
|
||||||
&command_name,
|
true,
|
||||||
Some(&command_argument),
|
workspace.clone(),
|
||||||
true,
|
cx,
|
||||||
workspace.clone(),
|
);
|
||||||
cx,
|
})
|
||||||
);
|
.ok();
|
||||||
})
|
|
||||||
.ok();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}) as Arc<_>
|
}) as Arc<_>
|
||||||
})
|
})
|
||||||
|
@ -203,27 +205,26 @@ impl SlashCommandCompletionProvider {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut new_text = command_argument.new_text.clone();
|
let mut new_text = new_argument.new_text.clone();
|
||||||
if !command_argument.run_command {
|
if !new_argument.run_command {
|
||||||
new_text.push(' ');
|
new_text.push(' ');
|
||||||
}
|
}
|
||||||
|
|
||||||
project::Completion {
|
project::Completion {
|
||||||
old_range: argument_range.clone(),
|
old_range: argument_range.clone(),
|
||||||
label: command_argument.label,
|
label: new_argument.label,
|
||||||
new_text,
|
new_text,
|
||||||
documentation: None,
|
documentation: None,
|
||||||
server_id: LanguageServerId(0),
|
server_id: LanguageServerId(0),
|
||||||
lsp_completion: Default::default(),
|
lsp_completion: Default::default(),
|
||||||
show_new_completions_on_confirm: !command_argument.run_command,
|
show_new_completions_on_confirm: !new_argument.run_command,
|
||||||
confirm,
|
confirm,
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.collect())
|
.collect())
|
||||||
})
|
})
|
||||||
} else {
|
} else {
|
||||||
cx.background_executor()
|
Task::ready(Ok(Vec::new()))
|
||||||
.spawn(async move { Ok(Vec::new()) })
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -236,7 +237,7 @@ impl CompletionProvider for SlashCommandCompletionProvider {
|
||||||
_: editor::CompletionContext,
|
_: editor::CompletionContext,
|
||||||
cx: &mut ViewContext<Editor>,
|
cx: &mut ViewContext<Editor>,
|
||||||
) -> Task<Result<Vec<project::Completion>>> {
|
) -> Task<Result<Vec<project::Completion>>> {
|
||||||
let Some((name, argument, command_range, argument_range)) =
|
let Some((name, arguments, command_range, argument_range)) =
|
||||||
buffer.update(cx, |buffer, _cx| {
|
buffer.update(cx, |buffer, _cx| {
|
||||||
let position = buffer_position.to_point(buffer);
|
let position = buffer_position.to_point(buffer);
|
||||||
let line_start = Point::new(position.row, 0);
|
let line_start = Point::new(position.row, 0);
|
||||||
|
@ -247,30 +248,35 @@ impl CompletionProvider for SlashCommandCompletionProvider {
|
||||||
let command_range_start = Point::new(position.row, call.name.start as u32 - 1);
|
let command_range_start = Point::new(position.row, call.name.start as u32 - 1);
|
||||||
let command_range_end = Point::new(
|
let command_range_end = Point::new(
|
||||||
position.row,
|
position.row,
|
||||||
call.argument.as_ref().map_or(call.name.end, |arg| arg.end) as u32,
|
call.arguments.last().map_or(call.name.end, |arg| arg.end) as u32,
|
||||||
);
|
);
|
||||||
let command_range = buffer.anchor_after(command_range_start)
|
let command_range = buffer.anchor_after(command_range_start)
|
||||||
..buffer.anchor_after(command_range_end);
|
..buffer.anchor_after(command_range_end);
|
||||||
|
|
||||||
let name = line[call.name.clone()].to_string();
|
let name = line[call.name.clone()].to_string();
|
||||||
|
let (arguments, argument_range) = if let Some(argument) = call.arguments.last() {
|
||||||
Some(if let Some(argument) = call.argument {
|
|
||||||
let start =
|
let start =
|
||||||
buffer.anchor_after(Point::new(position.row, argument.start as u32));
|
buffer.anchor_after(Point::new(position.row, argument.start as u32));
|
||||||
let argument = line[argument.clone()].to_string();
|
let arguments = call
|
||||||
(name, Some(argument), command_range, start..buffer_position)
|
.arguments
|
||||||
|
.iter()
|
||||||
|
.filter_map(|argument| Some(line.get(argument.clone())?.to_string()))
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
(Some(arguments), start..buffer_position)
|
||||||
} else {
|
} else {
|
||||||
let start =
|
let start =
|
||||||
buffer.anchor_after(Point::new(position.row, call.name.start as u32));
|
buffer.anchor_after(Point::new(position.row, call.name.start as u32));
|
||||||
(name, None, command_range, start..buffer_position)
|
(None, start..buffer_position)
|
||||||
})
|
};
|
||||||
|
|
||||||
|
Some((name, arguments, command_range, argument_range))
|
||||||
})
|
})
|
||||||
else {
|
else {
|
||||||
return Task::ready(Ok(Vec::new()));
|
return Task::ready(Ok(Vec::new()));
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(argument) = argument {
|
if let Some(arguments) = arguments {
|
||||||
self.complete_command_argument(&name, argument, command_range, argument_range, cx)
|
self.complete_command_argument(&name, &arguments, command_range, argument_range, cx)
|
||||||
} else {
|
} else {
|
||||||
self.complete_command_name(&name, command_range, argument_range, cx)
|
self.complete_command_name(&name, command_range, argument_range, cx)
|
||||||
}
|
}
|
||||||
|
@ -325,16 +331,23 @@ impl SlashCommandLine {
|
||||||
if let Some(call) = &mut call {
|
if let Some(call) = &mut call {
|
||||||
// The command arguments start at the first non-whitespace character
|
// The command arguments start at the first non-whitespace character
|
||||||
// after the command name, and continue until the end of the line.
|
// after the command name, and continue until the end of the line.
|
||||||
if let Some(argument) = &mut call.argument {
|
if let Some(argument) = call.arguments.last_mut() {
|
||||||
if (*argument).is_empty() && c.is_whitespace() {
|
if c.is_whitespace() {
|
||||||
argument.start = next_ix;
|
if (*argument).is_empty() {
|
||||||
|
argument.start = next_ix;
|
||||||
|
argument.end = next_ix;
|
||||||
|
} else {
|
||||||
|
argument.end = ix;
|
||||||
|
call.arguments.push(next_ix..next_ix);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
argument.end = next_ix;
|
||||||
}
|
}
|
||||||
argument.end = next_ix;
|
|
||||||
}
|
}
|
||||||
// The command name ends at the first whitespace character.
|
// The command name ends at the first whitespace character.
|
||||||
else if !call.name.is_empty() {
|
else if !call.name.is_empty() {
|
||||||
if c.is_whitespace() {
|
if c.is_whitespace() {
|
||||||
call.argument = Some(next_ix..next_ix);
|
call.arguments = vec![next_ix..next_ix];
|
||||||
} else {
|
} else {
|
||||||
call.name.end = next_ix;
|
call.name.end = next_ix;
|
||||||
}
|
}
|
||||||
|
@ -350,7 +363,7 @@ impl SlashCommandLine {
|
||||||
else if c == '/' {
|
else if c == '/' {
|
||||||
call = Some(SlashCommandLine {
|
call = Some(SlashCommandLine {
|
||||||
name: next_ix..next_ix,
|
name: next_ix..next_ix,
|
||||||
argument: None,
|
arguments: Vec::new(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
// The line can't contain anything before the slash except for whitespace.
|
// The line can't contain anything before the slash except for whitespace.
|
||||||
|
|
|
@ -32,7 +32,7 @@ impl SlashCommand for DefaultSlashCommand {
|
||||||
|
|
||||||
fn complete_argument(
|
fn complete_argument(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
_query: String,
|
_arguments: &[String],
|
||||||
_cancellation_flag: Arc<AtomicBool>,
|
_cancellation_flag: Arc<AtomicBool>,
|
||||||
_workspace: Option<WeakView<Workspace>>,
|
_workspace: Option<WeakView<Workspace>>,
|
||||||
_cx: &mut WindowContext,
|
_cx: &mut WindowContext,
|
||||||
|
@ -42,7 +42,7 @@ impl SlashCommand for DefaultSlashCommand {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
_argument: Option<&str>,
|
_arguments: &[String],
|
||||||
_workspace: WeakView<Workspace>,
|
_workspace: WeakView<Workspace>,
|
||||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
|
|
|
@ -105,7 +105,7 @@ impl SlashCommand for DiagnosticsSlashCommand {
|
||||||
|
|
||||||
fn complete_argument(
|
fn complete_argument(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
query: String,
|
arguments: &[String],
|
||||||
cancellation_flag: Arc<AtomicBool>,
|
cancellation_flag: Arc<AtomicBool>,
|
||||||
workspace: Option<WeakView<Workspace>>,
|
workspace: Option<WeakView<Workspace>>,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
|
@ -113,7 +113,7 @@ impl SlashCommand for DiagnosticsSlashCommand {
|
||||||
let Some(workspace) = workspace.and_then(|workspace| workspace.upgrade()) else {
|
let Some(workspace) = workspace.and_then(|workspace| workspace.upgrade()) else {
|
||||||
return Task::ready(Err(anyhow!("workspace was dropped")));
|
return Task::ready(Err(anyhow!("workspace was dropped")));
|
||||||
};
|
};
|
||||||
let query = query.split_whitespace().last().unwrap_or("").to_string();
|
let query = arguments.last().cloned().unwrap_or_default();
|
||||||
|
|
||||||
let paths = self.search_paths(query.clone(), cancellation_flag.clone(), &workspace, cx);
|
let paths = self.search_paths(query.clone(), cancellation_flag.clone(), &workspace, cx);
|
||||||
let executor = cx.background_executor().clone();
|
let executor = cx.background_executor().clone();
|
||||||
|
@ -157,7 +157,7 @@ impl SlashCommand for DiagnosticsSlashCommand {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
argument: Option<&str>,
|
arguments: &[String],
|
||||||
workspace: WeakView<Workspace>,
|
workspace: WeakView<Workspace>,
|
||||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
|
@ -166,7 +166,7 @@ impl SlashCommand for DiagnosticsSlashCommand {
|
||||||
return Task::ready(Err(anyhow!("workspace was dropped")));
|
return Task::ready(Err(anyhow!("workspace was dropped")));
|
||||||
};
|
};
|
||||||
|
|
||||||
let options = Options::parse(argument);
|
let options = Options::parse(arguments);
|
||||||
|
|
||||||
let task = collect_diagnostics(workspace.read(cx).project().clone(), options, cx);
|
let task = collect_diagnostics(workspace.read(cx).project().clone(), options, cx);
|
||||||
|
|
||||||
|
@ -244,25 +244,20 @@ struct Options {
|
||||||
const INCLUDE_WARNINGS_ARGUMENT: &str = "--include-warnings";
|
const INCLUDE_WARNINGS_ARGUMENT: &str = "--include-warnings";
|
||||||
|
|
||||||
impl Options {
|
impl Options {
|
||||||
fn parse(arguments_line: Option<&str>) -> Self {
|
fn parse(arguments: &[String]) -> Self {
|
||||||
arguments_line
|
let mut include_warnings = false;
|
||||||
.map(|arguments_line| {
|
let mut path_matcher = None;
|
||||||
let args = arguments_line.split_whitespace().collect::<Vec<_>>();
|
for arg in arguments {
|
||||||
let mut include_warnings = false;
|
if arg == INCLUDE_WARNINGS_ARGUMENT {
|
||||||
let mut path_matcher = None;
|
include_warnings = true;
|
||||||
for arg in args {
|
} else {
|
||||||
if arg == INCLUDE_WARNINGS_ARGUMENT {
|
path_matcher = PathMatcher::new(&[arg.to_owned()]).log_err();
|
||||||
include_warnings = true;
|
}
|
||||||
} else {
|
}
|
||||||
path_matcher = PathMatcher::new(&[arg.to_owned()]).log_err();
|
Self {
|
||||||
}
|
include_warnings,
|
||||||
}
|
path_matcher,
|
||||||
Self {
|
}
|
||||||
include_warnings,
|
|
||||||
path_matcher,
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.unwrap_or_default()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn match_candidates_for_args() -> [StringMatchCandidate; 1] {
|
fn match_candidates_for_args() -> [StringMatchCandidate; 1] {
|
||||||
|
|
|
@ -161,7 +161,7 @@ impl SlashCommand for DocsSlashCommand {
|
||||||
|
|
||||||
fn complete_argument(
|
fn complete_argument(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
query: String,
|
arguments: &[String],
|
||||||
_cancel: Arc<AtomicBool>,
|
_cancel: Arc<AtomicBool>,
|
||||||
workspace: Option<WeakView<Workspace>>,
|
workspace: Option<WeakView<Workspace>>,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
|
@ -169,21 +169,18 @@ impl SlashCommand for DocsSlashCommand {
|
||||||
self.ensure_rust_doc_providers_are_registered(workspace, cx);
|
self.ensure_rust_doc_providers_are_registered(workspace, cx);
|
||||||
|
|
||||||
let indexed_docs_registry = IndexedDocsRegistry::global(cx);
|
let indexed_docs_registry = IndexedDocsRegistry::global(cx);
|
||||||
let args = DocsSlashCommandArgs::parse(&query);
|
let args = DocsSlashCommandArgs::parse(arguments);
|
||||||
let store = args
|
let store = args
|
||||||
.provider()
|
.provider()
|
||||||
.ok_or_else(|| anyhow!("no docs provider specified"))
|
.ok_or_else(|| anyhow!("no docs provider specified"))
|
||||||
.and_then(|provider| IndexedDocsStore::try_global(provider, cx));
|
.and_then(|provider| IndexedDocsStore::try_global(provider, cx));
|
||||||
cx.background_executor().spawn(async move {
|
cx.background_executor().spawn(async move {
|
||||||
fn build_completions(
|
fn build_completions(items: Vec<String>) -> Vec<ArgumentCompletion> {
|
||||||
provider: ProviderId,
|
|
||||||
items: Vec<String>,
|
|
||||||
) -> Vec<ArgumentCompletion> {
|
|
||||||
items
|
items
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|item| ArgumentCompletion {
|
.map(|item| ArgumentCompletion {
|
||||||
label: item.clone().into(),
|
label: item.clone().into(),
|
||||||
new_text: format!("{provider} {item}"),
|
new_text: item.to_string(),
|
||||||
run_command: true,
|
run_command: true,
|
||||||
})
|
})
|
||||||
.collect()
|
.collect()
|
||||||
|
@ -225,7 +222,7 @@ impl SlashCommand for DocsSlashCommand {
|
||||||
let suggested_packages = store.clone().suggest_packages().await?;
|
let suggested_packages = store.clone().suggest_packages().await?;
|
||||||
let search_results = store.search(package).await;
|
let search_results = store.search(package).await;
|
||||||
|
|
||||||
let mut items = build_completions(provider.clone(), search_results);
|
let mut items = build_completions(search_results);
|
||||||
let workspace_crate_completions = suggested_packages
|
let workspace_crate_completions = suggested_packages
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|package_name| {
|
.filter(|package_name| {
|
||||||
|
@ -235,8 +232,8 @@ impl SlashCommand for DocsSlashCommand {
|
||||||
})
|
})
|
||||||
.map(|package_name| ArgumentCompletion {
|
.map(|package_name| ArgumentCompletion {
|
||||||
label: format!("{package_name} (unindexed)").into(),
|
label: format!("{package_name} (unindexed)").into(),
|
||||||
new_text: format!("{provider} {package_name}"),
|
new_text: format!("{package_name}"),
|
||||||
run_command: true,
|
run_command: false,
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>();
|
.collect::<Vec<_>>();
|
||||||
items.extend(workspace_crate_completions);
|
items.extend(workspace_crate_completions);
|
||||||
|
@ -255,14 +252,10 @@ impl SlashCommand for DocsSlashCommand {
|
||||||
|
|
||||||
Ok(items)
|
Ok(items)
|
||||||
}
|
}
|
||||||
DocsSlashCommandArgs::SearchItemDocs {
|
DocsSlashCommandArgs::SearchItemDocs { item_path, .. } => {
|
||||||
provider,
|
|
||||||
item_path,
|
|
||||||
..
|
|
||||||
} => {
|
|
||||||
let store = store?;
|
let store = store?;
|
||||||
let items = store.search(item_path).await;
|
let items = store.search(item_path).await;
|
||||||
Ok(build_completions(provider, items))
|
Ok(build_completions(items))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -270,16 +263,16 @@ impl SlashCommand for DocsSlashCommand {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
argument: Option<&str>,
|
arguments: &[String],
|
||||||
_workspace: WeakView<Workspace>,
|
_workspace: WeakView<Workspace>,
|
||||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
) -> Task<Result<SlashCommandOutput>> {
|
) -> Task<Result<SlashCommandOutput>> {
|
||||||
let Some(argument) = argument else {
|
if arguments.is_empty() {
|
||||||
return Task::ready(Err(anyhow!("missing argument")));
|
return Task::ready(Err(anyhow!("missing an argument")));
|
||||||
};
|
};
|
||||||
|
|
||||||
let args = DocsSlashCommandArgs::parse(argument);
|
let args = DocsSlashCommandArgs::parse(arguments);
|
||||||
let executor = cx.background_executor().clone();
|
let executor = cx.background_executor().clone();
|
||||||
let task = cx.background_executor().spawn({
|
let task = cx.background_executor().spawn({
|
||||||
let store = args
|
let store = args
|
||||||
|
@ -379,12 +372,18 @@ pub(crate) enum DocsSlashCommandArgs {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DocsSlashCommandArgs {
|
impl DocsSlashCommandArgs {
|
||||||
pub fn parse(argument: &str) -> Self {
|
pub fn parse(arguments: &[String]) -> Self {
|
||||||
let Some((provider, argument)) = argument.split_once(' ') else {
|
let Some(provider) = arguments
|
||||||
|
.get(0)
|
||||||
|
.cloned()
|
||||||
|
.filter(|arg| !arg.trim().is_empty())
|
||||||
|
else {
|
||||||
return Self::NoProvider;
|
return Self::NoProvider;
|
||||||
};
|
};
|
||||||
|
|
||||||
let provider = ProviderId(provider.into());
|
let provider = ProviderId(provider.into());
|
||||||
|
let Some(argument) = arguments.get(1) else {
|
||||||
|
return Self::NoProvider;
|
||||||
|
};
|
||||||
|
|
||||||
if let Some((package, rest)) = argument.split_once(is_item_path_delimiter) {
|
if let Some((package, rest)) = argument.split_once(is_item_path_delimiter) {
|
||||||
if rest.trim().is_empty() {
|
if rest.trim().is_empty() {
|
||||||
|
@ -444,16 +443,16 @@ mod tests {
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_docs_slash_command_args() {
|
fn test_parse_docs_slash_command_args() {
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
DocsSlashCommandArgs::parse(""),
|
DocsSlashCommandArgs::parse(&["".to_string()]),
|
||||||
DocsSlashCommandArgs::NoProvider
|
DocsSlashCommandArgs::NoProvider
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
DocsSlashCommandArgs::parse("rustdoc"),
|
DocsSlashCommandArgs::parse(&["rustdoc".to_string()]),
|
||||||
DocsSlashCommandArgs::NoProvider
|
DocsSlashCommandArgs::NoProvider
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
DocsSlashCommandArgs::parse("rustdoc "),
|
DocsSlashCommandArgs::parse(&["rustdoc".to_string(), "".to_string()]),
|
||||||
DocsSlashCommandArgs::SearchPackageDocs {
|
DocsSlashCommandArgs::SearchPackageDocs {
|
||||||
provider: ProviderId("rustdoc".into()),
|
provider: ProviderId("rustdoc".into()),
|
||||||
package: "".into(),
|
package: "".into(),
|
||||||
|
@ -461,7 +460,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
DocsSlashCommandArgs::parse("gleam "),
|
DocsSlashCommandArgs::parse(&["gleam".to_string(), "".to_string()]),
|
||||||
DocsSlashCommandArgs::SearchPackageDocs {
|
DocsSlashCommandArgs::SearchPackageDocs {
|
||||||
provider: ProviderId("gleam".into()),
|
provider: ProviderId("gleam".into()),
|
||||||
package: "".into(),
|
package: "".into(),
|
||||||
|
@ -470,7 +469,7 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
DocsSlashCommandArgs::parse("rustdoc gpui"),
|
DocsSlashCommandArgs::parse(&["rustdoc".to_string(), "gpui".to_string()]),
|
||||||
DocsSlashCommandArgs::SearchPackageDocs {
|
DocsSlashCommandArgs::SearchPackageDocs {
|
||||||
provider: ProviderId("rustdoc".into()),
|
provider: ProviderId("rustdoc".into()),
|
||||||
package: "gpui".into(),
|
package: "gpui".into(),
|
||||||
|
@ -478,7 +477,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
DocsSlashCommandArgs::parse("gleam gleam_stdlib"),
|
DocsSlashCommandArgs::parse(&["gleam".to_string(), "gleam_stdlib".to_string()]),
|
||||||
DocsSlashCommandArgs::SearchPackageDocs {
|
DocsSlashCommandArgs::SearchPackageDocs {
|
||||||
provider: ProviderId("gleam".into()),
|
provider: ProviderId("gleam".into()),
|
||||||
package: "gleam_stdlib".into(),
|
package: "gleam_stdlib".into(),
|
||||||
|
@ -488,7 +487,7 @@ mod tests {
|
||||||
|
|
||||||
// Adding an item path delimiter indicates we can start indexing.
|
// Adding an item path delimiter indicates we can start indexing.
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
DocsSlashCommandArgs::parse("rustdoc gpui:"),
|
DocsSlashCommandArgs::parse(&["rustdoc".to_string(), "gpui:".to_string()]),
|
||||||
DocsSlashCommandArgs::SearchPackageDocs {
|
DocsSlashCommandArgs::SearchPackageDocs {
|
||||||
provider: ProviderId("rustdoc".into()),
|
provider: ProviderId("rustdoc".into()),
|
||||||
package: "gpui".into(),
|
package: "gpui".into(),
|
||||||
|
@ -496,7 +495,7 @@ mod tests {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
DocsSlashCommandArgs::parse("gleam gleam_stdlib/"),
|
DocsSlashCommandArgs::parse(&["gleam".to_string(), "gleam_stdlib/".to_string()]),
|
||||||
DocsSlashCommandArgs::SearchPackageDocs {
|
DocsSlashCommandArgs::SearchPackageDocs {
|
||||||
provider: ProviderId("gleam".into()),
|
provider: ProviderId("gleam".into()),
|
||||||
package: "gleam_stdlib".into(),
|
package: "gleam_stdlib".into(),
|
||||||
|
@ -505,7 +504,10 @@ mod tests {
|
||||||
);
|
);
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
DocsSlashCommandArgs::parse("rustdoc gpui::foo::bar::Baz"),
|
DocsSlashCommandArgs::parse(&[
|
||||||
|
"rustdoc".to_string(),
|
||||||
|
"gpui::foo::bar::Baz".to_string()
|
||||||
|
]),
|
||||||
DocsSlashCommandArgs::SearchItemDocs {
|
DocsSlashCommandArgs::SearchItemDocs {
|
||||||
provider: ProviderId("rustdoc".into()),
|
provider: ProviderId("rustdoc".into()),
|
||||||
package: "gpui".into(),
|
package: "gpui".into(),
|
||||||
|
@ -513,7 +515,10 @@ mod tests {
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
DocsSlashCommandArgs::parse("gleam gleam_stdlib/gleam/int"),
|
DocsSlashCommandArgs::parse(&[
|
||||||
|
"gleam".to_string(),
|
||||||
|
"gleam_stdlib/gleam/int".to_string()
|
||||||
|
]),
|
||||||
DocsSlashCommandArgs::SearchItemDocs {
|
DocsSlashCommandArgs::SearchItemDocs {
|
||||||
provider: ProviderId("gleam".into()),
|
provider: ProviderId("gleam".into()),
|
||||||
package: "gleam_stdlib".into(),
|
package: "gleam_stdlib".into(),
|
||||||
|
|
|
@ -117,7 +117,7 @@ impl SlashCommand for FetchSlashCommand {
|
||||||
|
|
||||||
fn complete_argument(
|
fn complete_argument(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
_query: String,
|
_arguments: &[String],
|
||||||
_cancel: Arc<AtomicBool>,
|
_cancel: Arc<AtomicBool>,
|
||||||
_workspace: Option<WeakView<Workspace>>,
|
_workspace: Option<WeakView<Workspace>>,
|
||||||
_cx: &mut WindowContext,
|
_cx: &mut WindowContext,
|
||||||
|
@ -127,12 +127,12 @@ impl SlashCommand for FetchSlashCommand {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
argument: Option<&str>,
|
arguments: &[String],
|
||||||
workspace: WeakView<Workspace>,
|
workspace: WeakView<Workspace>,
|
||||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
) -> Task<Result<SlashCommandOutput>> {
|
) -> Task<Result<SlashCommandOutput>> {
|
||||||
let Some(argument) = argument else {
|
let Some(argument) = arguments.first() else {
|
||||||
return Task::ready(Err(anyhow!("missing URL")));
|
return Task::ready(Err(anyhow!("missing URL")));
|
||||||
};
|
};
|
||||||
let Some(workspace) = workspace.upgrade() else {
|
let Some(workspace) = workspace.upgrade() else {
|
||||||
|
|
|
@ -122,7 +122,7 @@ impl SlashCommand for FileSlashCommand {
|
||||||
|
|
||||||
fn complete_argument(
|
fn complete_argument(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
query: String,
|
arguments: &[String],
|
||||||
cancellation_flag: Arc<AtomicBool>,
|
cancellation_flag: Arc<AtomicBool>,
|
||||||
workspace: Option<WeakView<Workspace>>,
|
workspace: Option<WeakView<Workspace>>,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
|
@ -131,7 +131,12 @@ impl SlashCommand for FileSlashCommand {
|
||||||
return Task::ready(Err(anyhow!("workspace was dropped")));
|
return Task::ready(Err(anyhow!("workspace was dropped")));
|
||||||
};
|
};
|
||||||
|
|
||||||
let paths = self.search_paths(query, cancellation_flag, &workspace, cx);
|
let paths = self.search_paths(
|
||||||
|
arguments.last().cloned().unwrap_or_default(),
|
||||||
|
cancellation_flag,
|
||||||
|
&workspace,
|
||||||
|
cx,
|
||||||
|
);
|
||||||
let comment_id = cx.theme().syntax().highlight_id("comment").map(HighlightId);
|
let comment_id = cx.theme().syntax().highlight_id("comment").map(HighlightId);
|
||||||
cx.background_executor().spawn(async move {
|
cx.background_executor().spawn(async move {
|
||||||
Ok(paths
|
Ok(paths
|
||||||
|
@ -168,7 +173,7 @@ impl SlashCommand for FileSlashCommand {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
argument: Option<&str>,
|
arguments: &[String],
|
||||||
workspace: WeakView<Workspace>,
|
workspace: WeakView<Workspace>,
|
||||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
|
@ -177,7 +182,7 @@ impl SlashCommand for FileSlashCommand {
|
||||||
return Task::ready(Err(anyhow!("workspace was dropped")));
|
return Task::ready(Err(anyhow!("workspace was dropped")));
|
||||||
};
|
};
|
||||||
|
|
||||||
let Some(argument) = argument else {
|
let Some(argument) = arguments.first() else {
|
||||||
return Task::ready(Err(anyhow!("missing path")));
|
return Task::ready(Err(anyhow!("missing path")));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -32,7 +32,7 @@ impl SlashCommand for NowSlashCommand {
|
||||||
|
|
||||||
fn complete_argument(
|
fn complete_argument(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
_query: String,
|
_arguments: &[String],
|
||||||
_cancel: Arc<AtomicBool>,
|
_cancel: Arc<AtomicBool>,
|
||||||
_workspace: Option<WeakView<Workspace>>,
|
_workspace: Option<WeakView<Workspace>>,
|
||||||
_cx: &mut WindowContext,
|
_cx: &mut WindowContext,
|
||||||
|
@ -42,7 +42,7 @@ impl SlashCommand for NowSlashCommand {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
_argument: Option<&str>,
|
_arguments: &[String],
|
||||||
_workspace: WeakView<Workspace>,
|
_workspace: WeakView<Workspace>,
|
||||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||||
_cx: &mut WindowContext,
|
_cx: &mut WindowContext,
|
||||||
|
|
|
@ -103,7 +103,7 @@ impl SlashCommand for ProjectSlashCommand {
|
||||||
|
|
||||||
fn complete_argument(
|
fn complete_argument(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
_query: String,
|
_arguments: &[String],
|
||||||
_cancel: Arc<AtomicBool>,
|
_cancel: Arc<AtomicBool>,
|
||||||
_workspace: Option<WeakView<Workspace>>,
|
_workspace: Option<WeakView<Workspace>>,
|
||||||
_cx: &mut WindowContext,
|
_cx: &mut WindowContext,
|
||||||
|
@ -117,7 +117,7 @@ impl SlashCommand for ProjectSlashCommand {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
_argument: Option<&str>,
|
_arguments: &[String],
|
||||||
workspace: WeakView<Workspace>,
|
workspace: WeakView<Workspace>,
|
||||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
|
|
|
@ -29,12 +29,13 @@ impl SlashCommand for PromptSlashCommand {
|
||||||
|
|
||||||
fn complete_argument(
|
fn complete_argument(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
query: String,
|
arguments: &[String],
|
||||||
_cancellation_flag: Arc<AtomicBool>,
|
_cancellation_flag: Arc<AtomicBool>,
|
||||||
_workspace: Option<WeakView<Workspace>>,
|
_workspace: Option<WeakView<Workspace>>,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
) -> Task<Result<Vec<ArgumentCompletion>>> {
|
) -> Task<Result<Vec<ArgumentCompletion>>> {
|
||||||
let store = PromptStore::global(cx);
|
let store = PromptStore::global(cx);
|
||||||
|
let query = arguments.last().cloned().unwrap_or_default();
|
||||||
cx.background_executor().spawn(async move {
|
cx.background_executor().spawn(async move {
|
||||||
let prompts = store.await?.search(query).await;
|
let prompts = store.await?.search(query).await;
|
||||||
Ok(prompts
|
Ok(prompts
|
||||||
|
@ -53,12 +54,12 @@ impl SlashCommand for PromptSlashCommand {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
title: Option<&str>,
|
arguments: &[String],
|
||||||
_workspace: WeakView<Workspace>,
|
_workspace: WeakView<Workspace>,
|
||||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
) -> Task<Result<SlashCommandOutput>> {
|
) -> Task<Result<SlashCommandOutput>> {
|
||||||
let Some(title) = title else {
|
let Some(title) = arguments.first() else {
|
||||||
return Task::ready(Err(anyhow!("missing prompt name")));
|
return Task::ready(Err(anyhow!("missing prompt name")));
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -49,7 +49,7 @@ impl SlashCommand for SearchSlashCommand {
|
||||||
|
|
||||||
fn complete_argument(
|
fn complete_argument(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
_query: String,
|
_arguments: &[String],
|
||||||
_cancel: Arc<AtomicBool>,
|
_cancel: Arc<AtomicBool>,
|
||||||
_workspace: Option<WeakView<Workspace>>,
|
_workspace: Option<WeakView<Workspace>>,
|
||||||
_cx: &mut WindowContext,
|
_cx: &mut WindowContext,
|
||||||
|
@ -59,7 +59,7 @@ impl SlashCommand for SearchSlashCommand {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
argument: Option<&str>,
|
arguments: &[String],
|
||||||
workspace: WeakView<Workspace>,
|
workspace: WeakView<Workspace>,
|
||||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
|
@ -67,13 +67,13 @@ impl SlashCommand for SearchSlashCommand {
|
||||||
let Some(workspace) = workspace.upgrade() else {
|
let Some(workspace) = workspace.upgrade() else {
|
||||||
return Task::ready(Err(anyhow::anyhow!("workspace was dropped")));
|
return Task::ready(Err(anyhow::anyhow!("workspace was dropped")));
|
||||||
};
|
};
|
||||||
let Some(argument) = argument else {
|
if arguments.is_empty() {
|
||||||
return Task::ready(Err(anyhow::anyhow!("missing search query")));
|
return Task::ready(Err(anyhow::anyhow!("missing search query")));
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut limit = None;
|
let mut limit = None;
|
||||||
let mut query = String::new();
|
let mut query = String::new();
|
||||||
for part in argument.split(' ') {
|
for part in arguments {
|
||||||
if let Some(parameter) = part.strip_prefix("--") {
|
if let Some(parameter) = part.strip_prefix("--") {
|
||||||
if let Ok(count) = parameter.parse::<usize>() {
|
if let Ok(count) = parameter.parse::<usize>() {
|
||||||
limit = Some(count);
|
limit = Some(count);
|
||||||
|
|
|
@ -26,7 +26,7 @@ impl SlashCommand for OutlineSlashCommand {
|
||||||
|
|
||||||
fn complete_argument(
|
fn complete_argument(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
_query: String,
|
_arguments: &[String],
|
||||||
_cancel: Arc<AtomicBool>,
|
_cancel: Arc<AtomicBool>,
|
||||||
_workspace: Option<WeakView<Workspace>>,
|
_workspace: Option<WeakView<Workspace>>,
|
||||||
_cx: &mut WindowContext,
|
_cx: &mut WindowContext,
|
||||||
|
@ -40,7 +40,7 @@ impl SlashCommand for OutlineSlashCommand {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
_argument: Option<&str>,
|
_arguments: &[String],
|
||||||
workspace: WeakView<Workspace>,
|
workspace: WeakView<Workspace>,
|
||||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
|
|
|
@ -5,8 +5,9 @@ use super::{
|
||||||
};
|
};
|
||||||
use anyhow::{Context, Result};
|
use anyhow::{Context, Result};
|
||||||
use assistant_slash_command::ArgumentCompletion;
|
use assistant_slash_command::ArgumentCompletion;
|
||||||
use collections::HashMap;
|
use collections::{HashMap, HashSet};
|
||||||
use editor::Editor;
|
use editor::Editor;
|
||||||
|
use futures::future::join_all;
|
||||||
use gpui::{Entity, Task, WeakView};
|
use gpui::{Entity, Task, WeakView};
|
||||||
use language::{BufferSnapshot, LspAdapterDelegate};
|
use language::{BufferSnapshot, LspAdapterDelegate};
|
||||||
use std::{
|
use std::{
|
||||||
|
@ -17,13 +18,13 @@ use std::{
|
||||||
use ui::WindowContext;
|
use ui::WindowContext;
|
||||||
use workspace::Workspace;
|
use workspace::Workspace;
|
||||||
|
|
||||||
pub(crate) struct TabsSlashCommand;
|
pub(crate) struct TabSlashCommand;
|
||||||
|
|
||||||
const ALL_TABS_COMPLETION_ITEM: &str = "all";
|
const ALL_TABS_COMPLETION_ITEM: &str = "all";
|
||||||
|
|
||||||
impl SlashCommand for TabsSlashCommand {
|
impl SlashCommand for TabSlashCommand {
|
||||||
fn name(&self) -> String {
|
fn name(&self) -> String {
|
||||||
"tabs".into()
|
"tab".into()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn description(&self) -> String {
|
fn description(&self) -> String {
|
||||||
|
@ -40,51 +41,66 @@ impl SlashCommand for TabsSlashCommand {
|
||||||
|
|
||||||
fn complete_argument(
|
fn complete_argument(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
query: String,
|
arguments: &[String],
|
||||||
cancel: Arc<AtomicBool>,
|
cancel: Arc<AtomicBool>,
|
||||||
workspace: Option<WeakView<Workspace>>,
|
workspace: Option<WeakView<Workspace>>,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
) -> Task<Result<Vec<ArgumentCompletion>>> {
|
) -> Task<Result<Vec<ArgumentCompletion>>> {
|
||||||
let all_tabs_completion_item = if ALL_TABS_COMPLETION_ITEM.contains(&query) {
|
let mut has_all_tabs_completion_item = false;
|
||||||
Some(ArgumentCompletion {
|
let argument_set = arguments
|
||||||
|
.iter()
|
||||||
|
.filter(|argument| {
|
||||||
|
if has_all_tabs_completion_item || ALL_TABS_COMPLETION_ITEM == argument.as_str() {
|
||||||
|
has_all_tabs_completion_item = true;
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
true
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.cloned()
|
||||||
|
.collect::<HashSet<_>>();
|
||||||
|
if has_all_tabs_completion_item {
|
||||||
|
return Task::ready(Ok(Vec::new()));
|
||||||
|
}
|
||||||
|
let current_query = arguments.last().cloned().unwrap_or_default();
|
||||||
|
let tab_items_search =
|
||||||
|
tab_items_for_queries(workspace, &[current_query], cancel, false, cx);
|
||||||
|
cx.spawn(|_| async move {
|
||||||
|
let tab_items = tab_items_search.await?;
|
||||||
|
let run_command = tab_items.len() == 1;
|
||||||
|
let tab_completion_items = tab_items.into_iter().filter_map(|(path, ..)| {
|
||||||
|
let path_string = path.as_deref()?.to_string_lossy().to_string();
|
||||||
|
if argument_set.contains(&path_string) {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(ArgumentCompletion {
|
||||||
|
label: path_string.clone().into(),
|
||||||
|
new_text: path_string,
|
||||||
|
run_command,
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
Ok(Some(ArgumentCompletion {
|
||||||
label: ALL_TABS_COMPLETION_ITEM.into(),
|
label: ALL_TABS_COMPLETION_ITEM.into(),
|
||||||
new_text: ALL_TABS_COMPLETION_ITEM.to_owned(),
|
new_text: ALL_TABS_COMPLETION_ITEM.to_owned(),
|
||||||
run_command: true,
|
run_command: true,
|
||||||
})
|
})
|
||||||
} else {
|
.into_iter()
|
||||||
None
|
.chain(tab_completion_items)
|
||||||
};
|
.collect::<Vec<_>>())
|
||||||
let tab_items_search = tab_items_for_query(workspace, query, cancel, false, cx);
|
|
||||||
cx.spawn(|_| async move {
|
|
||||||
let tab_completion_items =
|
|
||||||
tab_items_search
|
|
||||||
.await?
|
|
||||||
.into_iter()
|
|
||||||
.filter_map(|(path, ..)| {
|
|
||||||
let path_string = path.as_deref()?.to_string_lossy().to_string();
|
|
||||||
Some(ArgumentCompletion {
|
|
||||||
label: path_string.clone().into(),
|
|
||||||
new_text: path_string,
|
|
||||||
run_command: true,
|
|
||||||
})
|
|
||||||
});
|
|
||||||
Ok(all_tabs_completion_item
|
|
||||||
.into_iter()
|
|
||||||
.chain(tab_completion_items)
|
|
||||||
.collect::<Vec<_>>())
|
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
argument: Option<&str>,
|
arguments: &[String],
|
||||||
workspace: WeakView<Workspace>,
|
workspace: WeakView<Workspace>,
|
||||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
) -> Task<Result<SlashCommandOutput>> {
|
) -> Task<Result<SlashCommandOutput>> {
|
||||||
let tab_items_search = tab_items_for_query(
|
let tab_items_search = tab_items_for_queries(
|
||||||
Some(workspace),
|
Some(workspace),
|
||||||
argument.map(ToOwned::to_owned).unwrap_or_default(),
|
arguments,
|
||||||
Arc::new(AtomicBool::new(false)),
|
Arc::new(AtomicBool::new(false)),
|
||||||
true,
|
true,
|
||||||
cx,
|
cx,
|
||||||
|
@ -129,20 +145,21 @@ impl SlashCommand for TabsSlashCommand {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn tab_items_for_query(
|
fn tab_items_for_queries(
|
||||||
workspace: Option<WeakView<Workspace>>,
|
workspace: Option<WeakView<Workspace>>,
|
||||||
mut query: String,
|
queries: &[String],
|
||||||
cancel: Arc<AtomicBool>,
|
cancel: Arc<AtomicBool>,
|
||||||
use_active_tab_for_empty_query: bool,
|
strict_match: bool,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
) -> Task<anyhow::Result<Vec<(Option<PathBuf>, BufferSnapshot, usize)>>> {
|
) -> Task<anyhow::Result<Vec<(Option<PathBuf>, BufferSnapshot, usize)>>> {
|
||||||
|
let empty_query = queries.is_empty() || queries.iter().all(|query| query.trim().is_empty());
|
||||||
|
let queries = queries.to_owned();
|
||||||
cx.spawn(|mut cx| async move {
|
cx.spawn(|mut cx| async move {
|
||||||
query.make_ascii_lowercase();
|
|
||||||
let mut open_buffers =
|
let mut open_buffers =
|
||||||
workspace
|
workspace
|
||||||
.context("no workspace")?
|
.context("no workspace")?
|
||||||
.update(&mut cx, |workspace, cx| {
|
.update(&mut cx, |workspace, cx| {
|
||||||
if use_active_tab_for_empty_query && query.trim().is_empty() {
|
if strict_match && empty_query {
|
||||||
let active_editor = workspace
|
let active_editor = workspace
|
||||||
.active_item(cx)
|
.active_item(cx)
|
||||||
.context("no active item")?
|
.context("no active item")?
|
||||||
|
@ -189,38 +206,73 @@ fn tab_items_for_query(
|
||||||
cx.background_executor()
|
cx.background_executor()
|
||||||
.spawn(async move {
|
.spawn(async move {
|
||||||
open_buffers.sort_by_key(|(_, _, timestamp)| *timestamp);
|
open_buffers.sort_by_key(|(_, _, timestamp)| *timestamp);
|
||||||
let query = query.trim();
|
if empty_query
|
||||||
if query.is_empty() || query == ALL_TABS_COMPLETION_ITEM {
|
|| queries
|
||||||
|
.iter()
|
||||||
|
.any(|query| query == ALL_TABS_COMPLETION_ITEM)
|
||||||
|
{
|
||||||
return Ok(open_buffers);
|
return Ok(open_buffers);
|
||||||
}
|
}
|
||||||
|
|
||||||
let match_candidates = open_buffers
|
let matched_items = if strict_match {
|
||||||
.iter()
|
let match_candidates = open_buffers
|
||||||
.enumerate()
|
.iter()
|
||||||
.filter_map(|(id, (full_path, ..))| {
|
.enumerate()
|
||||||
let path_string = full_path.as_deref()?.to_string_lossy().to_string();
|
.filter_map(|(id, (full_path, ..))| {
|
||||||
Some(fuzzy::StringMatchCandidate {
|
let path_string = full_path.as_deref()?.to_string_lossy().to_string();
|
||||||
id,
|
Some((id, path_string))
|
||||||
char_bag: path_string.as_str().into(),
|
|
||||||
string: path_string,
|
|
||||||
})
|
})
|
||||||
})
|
.fold(HashMap::default(), |mut candidates, (id, path_string)| {
|
||||||
.collect::<Vec<_>>();
|
candidates
|
||||||
let string_matches = fuzzy::match_strings(
|
.entry(path_string)
|
||||||
&match_candidates,
|
.or_insert_with(|| Vec::new())
|
||||||
&query,
|
.push(id);
|
||||||
true,
|
candidates
|
||||||
usize::MAX,
|
});
|
||||||
&cancel,
|
|
||||||
background_executor,
|
|
||||||
)
|
|
||||||
.await;
|
|
||||||
|
|
||||||
Ok(string_matches
|
queries
|
||||||
.into_iter()
|
.iter()
|
||||||
.filter_map(|string_match| open_buffers.get(string_match.candidate_id))
|
.filter_map(|query| match_candidates.get(query))
|
||||||
.cloned()
|
.flatten()
|
||||||
.collect())
|
.copied()
|
||||||
|
.filter_map(|id| open_buffers.get(id))
|
||||||
|
.cloned()
|
||||||
|
.collect()
|
||||||
|
} else {
|
||||||
|
let match_candidates = open_buffers
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter_map(|(id, (full_path, ..))| {
|
||||||
|
let path_string = full_path.as_deref()?.to_string_lossy().to_string();
|
||||||
|
Some(fuzzy::StringMatchCandidate {
|
||||||
|
id,
|
||||||
|
char_bag: path_string.as_str().into(),
|
||||||
|
string: path_string,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
let mut processed_matches = HashSet::default();
|
||||||
|
let file_queries = queries.iter().map(|query| {
|
||||||
|
fuzzy::match_strings(
|
||||||
|
&match_candidates,
|
||||||
|
query,
|
||||||
|
true,
|
||||||
|
usize::MAX,
|
||||||
|
&cancel,
|
||||||
|
background_executor.clone(),
|
||||||
|
)
|
||||||
|
});
|
||||||
|
|
||||||
|
join_all(file_queries)
|
||||||
|
.await
|
||||||
|
.into_iter()
|
||||||
|
.flatten()
|
||||||
|
.filter(|string_match| processed_matches.insert(string_match.candidate_id))
|
||||||
|
.filter_map(|string_match| open_buffers.get(string_match.candidate_id))
|
||||||
|
.cloned()
|
||||||
|
.collect()
|
||||||
|
};
|
||||||
|
Ok(matched_items)
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
})
|
})
|
|
@ -42,21 +42,26 @@ impl SlashCommand for TerminalSlashCommand {
|
||||||
|
|
||||||
fn complete_argument(
|
fn complete_argument(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
_query: String,
|
arguments: &[String],
|
||||||
_cancel: Arc<AtomicBool>,
|
_cancel: Arc<AtomicBool>,
|
||||||
_workspace: Option<WeakView<Workspace>>,
|
_workspace: Option<WeakView<Workspace>>,
|
||||||
_cx: &mut WindowContext,
|
_cx: &mut WindowContext,
|
||||||
) -> Task<Result<Vec<ArgumentCompletion>>> {
|
) -> Task<Result<Vec<ArgumentCompletion>>> {
|
||||||
Task::ready(Ok(vec![ArgumentCompletion {
|
let completions = if arguments.iter().any(|arg| arg == LINE_COUNT_ARG) {
|
||||||
label: LINE_COUNT_ARG.into(),
|
Vec::new()
|
||||||
new_text: LINE_COUNT_ARG.to_string(),
|
} else {
|
||||||
run_command: true,
|
vec![ArgumentCompletion {
|
||||||
}]))
|
label: LINE_COUNT_ARG.into(),
|
||||||
|
new_text: LINE_COUNT_ARG.to_string(),
|
||||||
|
run_command: false,
|
||||||
|
}]
|
||||||
|
};
|
||||||
|
Task::ready(Ok(completions))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
argument: Option<&str>,
|
arguments: &[String],
|
||||||
workspace: WeakView<Workspace>,
|
workspace: WeakView<Workspace>,
|
||||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
|
@ -75,9 +80,13 @@ impl SlashCommand for TerminalSlashCommand {
|
||||||
return Task::ready(Err(anyhow::anyhow!("no active terminal")));
|
return Task::ready(Err(anyhow::anyhow!("no active terminal")));
|
||||||
};
|
};
|
||||||
|
|
||||||
let line_count = argument
|
let mut line_count = DEFAULT_CONTEXT_LINES;
|
||||||
.and_then(|a| parse_argument(a))
|
if arguments.get(0).map(|s| s.as_str()) == Some(LINE_COUNT_ARG) {
|
||||||
.unwrap_or(DEFAULT_CONTEXT_LINES);
|
if let Some(parsed_line_count) = arguments.get(1).and_then(|s| s.parse::<usize>().ok())
|
||||||
|
{
|
||||||
|
line_count = parsed_line_count;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let lines = active_terminal
|
let lines = active_terminal
|
||||||
.read(cx)
|
.read(cx)
|
||||||
|
@ -101,13 +110,3 @@ impl SlashCommand for TerminalSlashCommand {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_argument(argument: &str) -> Option<usize> {
|
|
||||||
let mut args = argument.split(' ');
|
|
||||||
if args.next() == Some(LINE_COUNT_ARG) {
|
|
||||||
if let Some(line_count) = args.next().and_then(|s| s.parse::<usize>().ok()) {
|
|
||||||
return Some(line_count);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None
|
|
||||||
}
|
|
||||||
|
|
|
@ -42,7 +42,7 @@ impl SlashCommand for WorkflowSlashCommand {
|
||||||
|
|
||||||
fn complete_argument(
|
fn complete_argument(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
_query: String,
|
_arguments: &[String],
|
||||||
_cancel: Arc<AtomicBool>,
|
_cancel: Arc<AtomicBool>,
|
||||||
_workspace: Option<WeakView<Workspace>>,
|
_workspace: Option<WeakView<Workspace>>,
|
||||||
_cx: &mut WindowContext,
|
_cx: &mut WindowContext,
|
||||||
|
@ -52,7 +52,7 @@ impl SlashCommand for WorkflowSlashCommand {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
_argument: Option<&str>,
|
_arguments: &[String],
|
||||||
_workspace: WeakView<Workspace>,
|
_workspace: WeakView<Workspace>,
|
||||||
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
|
|
|
@ -34,7 +34,7 @@ pub trait SlashCommand: 'static + Send + Sync {
|
||||||
fn menu_text(&self) -> String;
|
fn menu_text(&self) -> String;
|
||||||
fn complete_argument(
|
fn complete_argument(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
query: String,
|
arguments: &[String],
|
||||||
cancel: Arc<AtomicBool>,
|
cancel: Arc<AtomicBool>,
|
||||||
workspace: Option<WeakView<Workspace>>,
|
workspace: Option<WeakView<Workspace>>,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
|
@ -42,7 +42,7 @@ pub trait SlashCommand: 'static + Send + Sync {
|
||||||
fn requires_argument(&self) -> bool;
|
fn requires_argument(&self) -> bool;
|
||||||
fn run(
|
fn run(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
argument: Option<&str>,
|
arguments: &[String],
|
||||||
workspace: WeakView<Workspace>,
|
workspace: WeakView<Workspace>,
|
||||||
// TODO: We're just using the `LspAdapterDelegate` here because that is
|
// TODO: We're just using the `LspAdapterDelegate` here because that is
|
||||||
// what the extension API is already expecting.
|
// what the extension API is already expecting.
|
||||||
|
|
|
@ -39,11 +39,12 @@ impl SlashCommand for ExtensionSlashCommand {
|
||||||
|
|
||||||
fn complete_argument(
|
fn complete_argument(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
query: String,
|
arguments: &[String],
|
||||||
_cancel: Arc<AtomicBool>,
|
_cancel: Arc<AtomicBool>,
|
||||||
_workspace: Option<WeakView<Workspace>>,
|
_workspace: Option<WeakView<Workspace>>,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
) -> Task<Result<Vec<ArgumentCompletion>>> {
|
) -> Task<Result<Vec<ArgumentCompletion>>> {
|
||||||
|
let arguments = arguments.to_owned();
|
||||||
cx.background_executor().spawn(async move {
|
cx.background_executor().spawn(async move {
|
||||||
self.extension
|
self.extension
|
||||||
.call({
|
.call({
|
||||||
|
@ -54,7 +55,7 @@ impl SlashCommand for ExtensionSlashCommand {
|
||||||
.call_complete_slash_command_argument(
|
.call_complete_slash_command_argument(
|
||||||
store,
|
store,
|
||||||
&this.command,
|
&this.command,
|
||||||
query.as_ref(),
|
&arguments,
|
||||||
)
|
)
|
||||||
.await?
|
.await?
|
||||||
.map_err(|e| anyhow!("{}", e))?;
|
.map_err(|e| anyhow!("{}", e))?;
|
||||||
|
@ -79,12 +80,12 @@ impl SlashCommand for ExtensionSlashCommand {
|
||||||
|
|
||||||
fn run(
|
fn run(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
argument: Option<&str>,
|
arguments: &[String],
|
||||||
_workspace: WeakView<Workspace>,
|
_workspace: WeakView<Workspace>,
|
||||||
delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
delegate: Option<Arc<dyn LspAdapterDelegate>>,
|
||||||
cx: &mut WindowContext,
|
cx: &mut WindowContext,
|
||||||
) -> Task<Result<SlashCommandOutput>> {
|
) -> Task<Result<SlashCommandOutput>> {
|
||||||
let argument = argument.map(|arg| arg.to_string());
|
let arguments = arguments.to_owned();
|
||||||
let output = cx.background_executor().spawn(async move {
|
let output = cx.background_executor().spawn(async move {
|
||||||
self.extension
|
self.extension
|
||||||
.call({
|
.call({
|
||||||
|
@ -97,12 +98,7 @@ impl SlashCommand for ExtensionSlashCommand {
|
||||||
None
|
None
|
||||||
};
|
};
|
||||||
let output = extension
|
let output = extension
|
||||||
.call_run_slash_command(
|
.call_run_slash_command(store, &this.command, &arguments, resource)
|
||||||
store,
|
|
||||||
&this.command,
|
|
||||||
argument.as_deref(),
|
|
||||||
resource,
|
|
||||||
)
|
|
||||||
.await?
|
.await?
|
||||||
.map_err(|e| anyhow!("{}", e))?;
|
.map_err(|e| anyhow!("{}", e))?;
|
||||||
|
|
||||||
|
|
|
@ -262,11 +262,11 @@ impl Extension {
|
||||||
&self,
|
&self,
|
||||||
store: &mut Store<WasmState>,
|
store: &mut Store<WasmState>,
|
||||||
command: &SlashCommand,
|
command: &SlashCommand,
|
||||||
query: &str,
|
arguments: &[String],
|
||||||
) -> Result<Result<Vec<SlashCommandArgumentCompletion>, String>> {
|
) -> Result<Result<Vec<SlashCommandArgumentCompletion>, String>> {
|
||||||
match self {
|
match self {
|
||||||
Extension::V010(ext) => {
|
Extension::V010(ext) => {
|
||||||
ext.call_complete_slash_command_argument(store, command, query)
|
ext.call_complete_slash_command_argument(store, command, arguments)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
Extension::V001(_) | Extension::V004(_) | Extension::V006(_) => Ok(Ok(Vec::new())),
|
Extension::V001(_) | Extension::V004(_) | Extension::V006(_) => Ok(Ok(Vec::new())),
|
||||||
|
@ -277,12 +277,12 @@ impl Extension {
|
||||||
&self,
|
&self,
|
||||||
store: &mut Store<WasmState>,
|
store: &mut Store<WasmState>,
|
||||||
command: &SlashCommand,
|
command: &SlashCommand,
|
||||||
argument: Option<&str>,
|
arguments: &[String],
|
||||||
resource: Option<Resource<Arc<dyn LspAdapterDelegate>>>,
|
resource: Option<Resource<Arc<dyn LspAdapterDelegate>>>,
|
||||||
) -> Result<Result<SlashCommandOutput, String>> {
|
) -> Result<Result<SlashCommandOutput, String>> {
|
||||||
match self {
|
match self {
|
||||||
Extension::V010(ext) => {
|
Extension::V010(ext) => {
|
||||||
ext.call_run_slash_command(store, command, argument, resource)
|
ext.call_run_slash_command(store, command, arguments, resource)
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
Extension::V001(_) | Extension::V004(_) | Extension::V006(_) => {
|
Extension::V001(_) | Extension::V004(_) | Extension::V006(_) => {
|
||||||
|
|
|
@ -114,7 +114,7 @@ pub trait Extension: Send + Sync {
|
||||||
fn complete_slash_command_argument(
|
fn complete_slash_command_argument(
|
||||||
&self,
|
&self,
|
||||||
_command: SlashCommand,
|
_command: SlashCommand,
|
||||||
_query: String,
|
_args: Vec<String>,
|
||||||
) -> Result<Vec<SlashCommandArgumentCompletion>, String> {
|
) -> Result<Vec<SlashCommandArgumentCompletion>, String> {
|
||||||
Ok(Vec::new())
|
Ok(Vec::new())
|
||||||
}
|
}
|
||||||
|
@ -123,7 +123,7 @@ pub trait Extension: Send + Sync {
|
||||||
fn run_slash_command(
|
fn run_slash_command(
|
||||||
&self,
|
&self,
|
||||||
_command: SlashCommand,
|
_command: SlashCommand,
|
||||||
_argument: Option<String>,
|
_args: Vec<String>,
|
||||||
_worktree: Option<&Worktree>,
|
_worktree: Option<&Worktree>,
|
||||||
) -> Result<SlashCommandOutput, String> {
|
) -> Result<SlashCommandOutput, String> {
|
||||||
Err("`run_slash_command` not implemented".to_string())
|
Err("`run_slash_command` not implemented".to_string())
|
||||||
|
@ -257,17 +257,17 @@ impl wit::Guest for Component {
|
||||||
|
|
||||||
fn complete_slash_command_argument(
|
fn complete_slash_command_argument(
|
||||||
command: SlashCommand,
|
command: SlashCommand,
|
||||||
query: String,
|
args: Vec<String>,
|
||||||
) -> Result<Vec<SlashCommandArgumentCompletion>, String> {
|
) -> Result<Vec<SlashCommandArgumentCompletion>, String> {
|
||||||
extension().complete_slash_command_argument(command, query)
|
extension().complete_slash_command_argument(command, args)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run_slash_command(
|
fn run_slash_command(
|
||||||
command: SlashCommand,
|
command: SlashCommand,
|
||||||
argument: Option<String>,
|
args: Vec<String>,
|
||||||
worktree: Option<&Worktree>,
|
worktree: Option<&Worktree>,
|
||||||
) -> Result<SlashCommandOutput, String> {
|
) -> Result<SlashCommandOutput, String> {
|
||||||
extension().run_slash_command(command, argument, worktree)
|
extension().run_slash_command(command, args, worktree)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn suggest_docs_packages(provider: String) -> Result<Vec<String>, String> {
|
fn suggest_docs_packages(provider: String) -> Result<Vec<String>, String> {
|
||||||
|
|
|
@ -130,10 +130,10 @@ world extension {
|
||||||
export labels-for-symbols: func(language-server-id: string, symbols: list<symbol>) -> result<list<option<code-label>>, string>;
|
export labels-for-symbols: func(language-server-id: string, symbols: list<symbol>) -> result<list<option<code-label>>, string>;
|
||||||
|
|
||||||
/// Returns the completions that should be shown when completing the provided slash command with the given query.
|
/// Returns the completions that should be shown when completing the provided slash command with the given query.
|
||||||
export complete-slash-command-argument: func(command: slash-command, query: string) -> result<list<slash-command-argument-completion>, string>;
|
export complete-slash-command-argument: func(command: slash-command, args: list<string>) -> result<list<slash-command-argument-completion>, string>;
|
||||||
|
|
||||||
/// Returns the output from running the provided slash command.
|
/// Returns the output from running the provided slash command.
|
||||||
export run-slash-command: func(command: slash-command, argument: option<string>, worktree: option<borrow<worktree>>) -> result<slash-command-output, string>;
|
export run-slash-command: func(command: slash-command, args: list<string>, worktree: option<borrow<worktree>>) -> result<slash-command-output, string>;
|
||||||
|
|
||||||
/// Returns a list of packages as suggestions to be included in the `/docs`
|
/// Returns a list of packages as suggestions to be included in the `/docs`
|
||||||
/// search results.
|
/// search results.
|
||||||
|
|
|
@ -154,7 +154,7 @@ impl zed::Extension for GleamExtension {
|
||||||
fn complete_slash_command_argument(
|
fn complete_slash_command_argument(
|
||||||
&self,
|
&self,
|
||||||
command: SlashCommand,
|
command: SlashCommand,
|
||||||
_query: String,
|
_arguments: Vec<String>,
|
||||||
) -> Result<Vec<SlashCommandArgumentCompletion>, String> {
|
) -> Result<Vec<SlashCommandArgumentCompletion>, String> {
|
||||||
match command.name.as_str() {
|
match command.name.as_str() {
|
||||||
"gleam-project" => Ok(vec![
|
"gleam-project" => Ok(vec![
|
||||||
|
@ -181,12 +181,12 @@ impl zed::Extension for GleamExtension {
|
||||||
fn run_slash_command(
|
fn run_slash_command(
|
||||||
&self,
|
&self,
|
||||||
command: SlashCommand,
|
command: SlashCommand,
|
||||||
argument: Option<String>,
|
args: Vec<String>,
|
||||||
worktree: Option<&zed::Worktree>,
|
worktree: Option<&zed::Worktree>,
|
||||||
) -> Result<SlashCommandOutput, String> {
|
) -> Result<SlashCommandOutput, String> {
|
||||||
match command.name.as_str() {
|
match command.name.as_str() {
|
||||||
"gleam-docs" => {
|
"gleam-docs" => {
|
||||||
let argument = argument.ok_or_else(|| "missing argument".to_string())?;
|
let argument = args.last().ok_or_else(|| "missing argument".to_string())?;
|
||||||
|
|
||||||
let mut components = argument.split('/');
|
let mut components = argument.split('/');
|
||||||
let package_name = components
|
let package_name = components
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue