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:
Kirill Bulatov 2024-08-14 17:11:51 +03:00 committed by GitHub
parent 88a12b60a9
commit 8fe2de1737
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 332 additions and 263 deletions

View file

@ -161,7 +161,7 @@ impl SlashCommand for DocsSlashCommand {
fn complete_argument(
self: Arc<Self>,
query: String,
arguments: &[String],
_cancel: Arc<AtomicBool>,
workspace: Option<WeakView<Workspace>>,
cx: &mut WindowContext,
@ -169,21 +169,18 @@ impl SlashCommand for DocsSlashCommand {
self.ensure_rust_doc_providers_are_registered(workspace, cx);
let indexed_docs_registry = IndexedDocsRegistry::global(cx);
let args = DocsSlashCommandArgs::parse(&query);
let args = DocsSlashCommandArgs::parse(arguments);
let store = args
.provider()
.ok_or_else(|| anyhow!("no docs provider specified"))
.and_then(|provider| IndexedDocsStore::try_global(provider, cx));
cx.background_executor().spawn(async move {
fn build_completions(
provider: ProviderId,
items: Vec<String>,
) -> Vec<ArgumentCompletion> {
fn build_completions(items: Vec<String>) -> Vec<ArgumentCompletion> {
items
.into_iter()
.map(|item| ArgumentCompletion {
label: item.clone().into(),
new_text: format!("{provider} {item}"),
new_text: item.to_string(),
run_command: true,
})
.collect()
@ -225,7 +222,7 @@ impl SlashCommand for DocsSlashCommand {
let suggested_packages = store.clone().suggest_packages().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
.into_iter()
.filter(|package_name| {
@ -235,8 +232,8 @@ impl SlashCommand for DocsSlashCommand {
})
.map(|package_name| ArgumentCompletion {
label: format!("{package_name} (unindexed)").into(),
new_text: format!("{provider} {package_name}"),
run_command: true,
new_text: format!("{package_name}"),
run_command: false,
})
.collect::<Vec<_>>();
items.extend(workspace_crate_completions);
@ -255,14 +252,10 @@ impl SlashCommand for DocsSlashCommand {
Ok(items)
}
DocsSlashCommandArgs::SearchItemDocs {
provider,
item_path,
..
} => {
DocsSlashCommandArgs::SearchItemDocs { item_path, .. } => {
let store = store?;
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(
self: Arc<Self>,
argument: Option<&str>,
arguments: &[String],
_workspace: WeakView<Workspace>,
_delegate: Option<Arc<dyn LspAdapterDelegate>>,
cx: &mut WindowContext,
) -> Task<Result<SlashCommandOutput>> {
let Some(argument) = argument else {
return Task::ready(Err(anyhow!("missing argument")));
if arguments.is_empty() {
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 task = cx.background_executor().spawn({
let store = args
@ -379,12 +372,18 @@ pub(crate) enum DocsSlashCommandArgs {
}
impl DocsSlashCommandArgs {
pub fn parse(argument: &str) -> Self {
let Some((provider, argument)) = argument.split_once(' ') else {
pub fn parse(arguments: &[String]) -> Self {
let Some(provider) = arguments
.get(0)
.cloned()
.filter(|arg| !arg.trim().is_empty())
else {
return Self::NoProvider;
};
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 rest.trim().is_empty() {
@ -444,16 +443,16 @@ mod tests {
#[test]
fn test_parse_docs_slash_command_args() {
assert_eq!(
DocsSlashCommandArgs::parse(""),
DocsSlashCommandArgs::parse(&["".to_string()]),
DocsSlashCommandArgs::NoProvider
);
assert_eq!(
DocsSlashCommandArgs::parse("rustdoc"),
DocsSlashCommandArgs::parse(&["rustdoc".to_string()]),
DocsSlashCommandArgs::NoProvider
);
assert_eq!(
DocsSlashCommandArgs::parse("rustdoc "),
DocsSlashCommandArgs::parse(&["rustdoc".to_string(), "".to_string()]),
DocsSlashCommandArgs::SearchPackageDocs {
provider: ProviderId("rustdoc".into()),
package: "".into(),
@ -461,7 +460,7 @@ mod tests {
}
);
assert_eq!(
DocsSlashCommandArgs::parse("gleam "),
DocsSlashCommandArgs::parse(&["gleam".to_string(), "".to_string()]),
DocsSlashCommandArgs::SearchPackageDocs {
provider: ProviderId("gleam".into()),
package: "".into(),
@ -470,7 +469,7 @@ mod tests {
);
assert_eq!(
DocsSlashCommandArgs::parse("rustdoc gpui"),
DocsSlashCommandArgs::parse(&["rustdoc".to_string(), "gpui".to_string()]),
DocsSlashCommandArgs::SearchPackageDocs {
provider: ProviderId("rustdoc".into()),
package: "gpui".into(),
@ -478,7 +477,7 @@ mod tests {
}
);
assert_eq!(
DocsSlashCommandArgs::parse("gleam gleam_stdlib"),
DocsSlashCommandArgs::parse(&["gleam".to_string(), "gleam_stdlib".to_string()]),
DocsSlashCommandArgs::SearchPackageDocs {
provider: ProviderId("gleam".into()),
package: "gleam_stdlib".into(),
@ -488,7 +487,7 @@ mod tests {
// Adding an item path delimiter indicates we can start indexing.
assert_eq!(
DocsSlashCommandArgs::parse("rustdoc gpui:"),
DocsSlashCommandArgs::parse(&["rustdoc".to_string(), "gpui:".to_string()]),
DocsSlashCommandArgs::SearchPackageDocs {
provider: ProviderId("rustdoc".into()),
package: "gpui".into(),
@ -496,7 +495,7 @@ mod tests {
}
);
assert_eq!(
DocsSlashCommandArgs::parse("gleam gleam_stdlib/"),
DocsSlashCommandArgs::parse(&["gleam".to_string(), "gleam_stdlib/".to_string()]),
DocsSlashCommandArgs::SearchPackageDocs {
provider: ProviderId("gleam".into()),
package: "gleam_stdlib".into(),
@ -505,7 +504,10 @@ mod tests {
);
assert_eq!(
DocsSlashCommandArgs::parse("rustdoc gpui::foo::bar::Baz"),
DocsSlashCommandArgs::parse(&[
"rustdoc".to_string(),
"gpui::foo::bar::Baz".to_string()
]),
DocsSlashCommandArgs::SearchItemDocs {
provider: ProviderId("rustdoc".into()),
package: "gpui".into(),
@ -513,7 +515,10 @@ mod tests {
}
);
assert_eq!(
DocsSlashCommandArgs::parse("gleam gleam_stdlib/gleam/int"),
DocsSlashCommandArgs::parse(&[
"gleam".to_string(),
"gleam_stdlib/gleam/int".to_string()
]),
DocsSlashCommandArgs::SearchItemDocs {
provider: ProviderId("gleam".into()),
package: "gleam_stdlib".into(),