Fix slash command argument completion bugs (#16233)

Release Notes:

- N/A

---------

Co-authored-by: Mikayla Maki <mikayla@zed.dev>
This commit is contained in:
Kirill Bulatov 2024-08-14 19:36:55 +03:00 committed by GitHub
parent 6365000b68
commit 8df21f7bcd
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
8 changed files with 67 additions and 27 deletions

View file

@ -150,6 +150,7 @@ impl SlashCommandCompletionProvider {
arguments: &[String], arguments: &[String],
command_range: Range<Anchor>, command_range: Range<Anchor>,
argument_range: Range<Anchor>, argument_range: Range<Anchor>,
last_argument_range: Range<Anchor>,
cx: &mut WindowContext, cx: &mut WindowContext,
) -> Task<Result<Vec<project::Completion>>> { ) -> Task<Result<Vec<project::Completion>>> {
let new_cancel_flag = Arc::new(AtomicBool::new(false)); let new_cancel_flag = Arc::new(AtomicBool::new(false));
@ -180,24 +181,30 @@ impl SlashCommandCompletionProvider {
.map(|(editor, workspace)| { .map(|(editor, workspace)| {
Arc::new({ Arc::new({
let mut completed_arguments = arguments.clone(); let mut completed_arguments = arguments.clone();
completed_arguments.pop(); if new_argument.replace_previous_arguments {
completed_arguments.clear();
} else {
completed_arguments.pop();
}
completed_arguments.push(new_argument.new_text.clone()); 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();
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, &completed_arguments,
workspace.clone(), true,
cx, workspace.clone(),
); cx,
}) );
.ok(); })
.ok();
}
} }
}) as Arc<_> }) as Arc<_>
}) })
@ -211,7 +218,11 @@ impl SlashCommandCompletionProvider {
} }
project::Completion { project::Completion {
old_range: argument_range.clone(), old_range: if new_argument.replace_previous_arguments {
argument_range.clone()
} else {
last_argument_range.clone()
},
label: new_argument.label, label: new_argument.label,
new_text, new_text,
documentation: None, documentation: None,
@ -237,7 +248,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, arguments, command_range, argument_range)) = let Some((name, arguments, command_range, last_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);
@ -254,31 +265,46 @@ impl CompletionProvider for SlashCommandCompletionProvider {
..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() { let (arguments, last_argument_range) = if let Some(argument) = call.arguments.last()
let start = {
let last_arg_start =
buffer.anchor_after(Point::new(position.row, argument.start as u32)); buffer.anchor_after(Point::new(position.row, argument.start as u32));
let first_arg_start = call.arguments.first().expect("we have the last element");
let first_arg_start =
buffer.anchor_after(Point::new(position.row, first_arg_start.start as u32));
let arguments = call let arguments = call
.arguments .arguments
.iter() .iter()
.filter_map(|argument| Some(line.get(argument.clone())?.to_string())) .filter_map(|argument| Some(line.get(argument.clone())?.to_string()))
.collect::<Vec<_>>(); .collect::<Vec<_>>();
(Some(arguments), start..buffer_position) let argument_range = first_arg_start..buffer_position;
(
Some((arguments, argument_range)),
last_arg_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));
(None, start..buffer_position) (None, start..buffer_position)
}; };
Some((name, arguments, command_range, argument_range)) Some((name, arguments, command_range, last_argument_range))
}) })
else { else {
return Task::ready(Ok(Vec::new())); return Task::ready(Ok(Vec::new()));
}; };
if let Some(arguments) = arguments { if let Some((arguments, argument_range)) = arguments {
self.complete_command_argument(&name, &arguments, command_range, argument_range, cx) self.complete_command_argument(
&name,
&arguments,
command_range,
argument_range,
last_argument_range,
cx,
)
} else { } else {
self.complete_command_name(&name, command_range, argument_range, cx) self.complete_command_name(&name, command_range, last_argument_range, cx)
} }
} }

View file

@ -150,6 +150,7 @@ impl SlashCommand for DiagnosticsSlashCommand {
label: completion.clone().into(), label: completion.clone().into(),
new_text: completion, new_text: completion,
run_command: true, run_command: true,
replace_previous_arguments: false,
}) })
.collect()) .collect())
}) })

View file

@ -182,6 +182,7 @@ impl SlashCommand for DocsSlashCommand {
label: item.clone().into(), label: item.clone().into(),
new_text: item.to_string(), new_text: item.to_string(),
run_command: true, run_command: true,
replace_previous_arguments: false,
}) })
.collect() .collect()
} }
@ -194,6 +195,7 @@ impl SlashCommand for DocsSlashCommand {
label: "No available docs providers.".into(), label: "No available docs providers.".into(),
new_text: String::new(), new_text: String::new(),
run_command: false, run_command: false,
replace_previous_arguments: false,
}]); }]);
} }
@ -203,6 +205,7 @@ impl SlashCommand for DocsSlashCommand {
label: provider.to_string().into(), label: provider.to_string().into(),
new_text: provider.to_string(), new_text: provider.to_string(),
run_command: false, run_command: false,
replace_previous_arguments: false,
}) })
.collect()) .collect())
} }
@ -234,6 +237,7 @@ impl SlashCommand for DocsSlashCommand {
label: format!("{package_name} (unindexed)").into(), label: format!("{package_name} (unindexed)").into(),
new_text: format!("{package_name}"), new_text: format!("{package_name}"),
run_command: true, run_command: true,
replace_previous_arguments: false,
}) })
.collect::<Vec<_>>(); .collect::<Vec<_>>();
items.extend(workspace_crate_completions); items.extend(workspace_crate_completions);
@ -247,6 +251,7 @@ impl SlashCommand for DocsSlashCommand {
.into(), .into(),
new_text: provider.to_string(), new_text: provider.to_string(),
run_command: false, run_command: false,
replace_previous_arguments: false,
}]); }]);
} }

View file

@ -164,7 +164,8 @@ impl SlashCommand for FileSlashCommand {
Some(ArgumentCompletion { Some(ArgumentCompletion {
label, label,
new_text: text, new_text: text,
run_command: false, run_command: true,
replace_previous_arguments: false,
}) })
}) })
.collect()) .collect())

View file

@ -35,7 +35,7 @@ impl SlashCommand for PromptSlashCommand {
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(); let query = arguments.to_owned().join(" ");
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
@ -46,6 +46,7 @@ impl SlashCommand for PromptSlashCommand {
label: prompt_title.clone().into(), label: prompt_title.clone().into(),
new_text: prompt_title, new_text: prompt_title,
run_command: true, run_command: true,
replace_previous_arguments: true,
}) })
}) })
.collect()) .collect())
@ -59,12 +60,13 @@ impl SlashCommand for PromptSlashCommand {
_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) = arguments.first() else { let title = arguments.to_owned().join(" ");
if title.trim().is_empty() {
return Task::ready(Err(anyhow!("missing prompt name"))); return Task::ready(Err(anyhow!("missing prompt name")));
}; };
let store = PromptStore::global(cx); let store = PromptStore::global(cx);
let title = SharedString::from(title.to_string()); let title = SharedString::from(title.clone());
let prompt = cx.background_executor().spawn({ let prompt = cx.background_executor().spawn({
let title = title.clone(); let title = title.clone();
async move { async move {

View file

@ -76,6 +76,7 @@ impl SlashCommand for TabSlashCommand {
Some(ArgumentCompletion { Some(ArgumentCompletion {
label: path_string.clone().into(), label: path_string.clone().into(),
new_text: path_string, new_text: path_string,
replace_previous_arguments: false,
run_command, run_command,
}) })
}); });
@ -83,6 +84,7 @@ impl SlashCommand for TabSlashCommand {
Ok(Some(ArgumentCompletion { 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(),
replace_previous_arguments: false,
run_command: true, run_command: true,
}) })
.into_iter() .into_iter()

View file

@ -23,6 +23,8 @@ pub struct ArgumentCompletion {
pub new_text: String, pub new_text: String,
/// Whether the command should be run when accepting this completion. /// Whether the command should be run when accepting this completion.
pub run_command: bool, pub run_command: bool,
/// Whether to replace the all arguments, or whether to treat this as an independent argument.
pub replace_previous_arguments: bool,
} }
pub trait SlashCommand: 'static + Send + Sync { pub trait SlashCommand: 'static + Send + Sync {

View file

@ -66,6 +66,7 @@ impl SlashCommand for ExtensionSlashCommand {
.map(|completion| ArgumentCompletion { .map(|completion| ArgumentCompletion {
label: completion.label.into(), label: completion.label.into(),
new_text: completion.new_text, new_text: completion.new_text,
replace_previous_arguments: false,
run_command: completion.run_command, run_command: completion.run_command,
}) })
.collect(), .collect(),