Fix slash command argument completion bugs (#16233)
Release Notes: - N/A --------- Co-authored-by: Mikayla Maki <mikayla@zed.dev>
This commit is contained in:
parent
6365000b68
commit
8df21f7bcd
8 changed files with 67 additions and 27 deletions
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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())
|
||||||
})
|
})
|
||||||
|
|
|
@ -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,
|
||||||
}]);
|
}]);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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())
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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()
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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(),
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue