assistant: Add basic glob support for expanding items in /docs
(#14370)
This PR updates the `/docs` slash command with basic globbing support for expanding docs. A `*` can be added to the item path to signify the end of a prefix match. For example: ``` # This will match any documentation items starting with `auk::`. # In this case, it will pull in the docs for each item in the crate. /docs docs-rs auk::* # This will match any documentation items starting with `auk::visitor::`, # which will pull in docs for the `visitor` module. /docs docs-rs auk::visitor::* ``` https://github.com/user-attachments/assets/5e1e21f1-241b-483f-9cd1-facc3aa76365 Release Notes: - N/A
This commit is contained in:
parent
fe3fe945a9
commit
3deb000f70
2 changed files with 70 additions and 20 deletions
|
@ -200,46 +200,65 @@ impl SlashCommand for DocsSlashCommand {
|
||||||
};
|
};
|
||||||
|
|
||||||
let args = DocsSlashCommandArgs::parse(argument);
|
let args = DocsSlashCommandArgs::parse(argument);
|
||||||
let text = cx.background_executor().spawn({
|
let task = cx.background_executor().spawn({
|
||||||
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));
|
||||||
async move {
|
async move {
|
||||||
match args {
|
let (provider, key) = match args {
|
||||||
DocsSlashCommandArgs::NoProvider => bail!("no docs provider specified"),
|
DocsSlashCommandArgs::NoProvider => bail!("no docs provider specified"),
|
||||||
DocsSlashCommandArgs::SearchPackageDocs {
|
DocsSlashCommandArgs::SearchPackageDocs {
|
||||||
provider, package, ..
|
provider, package, ..
|
||||||
} => {
|
} => (provider, package),
|
||||||
let store = store?;
|
|
||||||
let item_docs = store.load(package.clone()).await?;
|
|
||||||
|
|
||||||
anyhow::Ok((provider, package, item_docs.to_string()))
|
|
||||||
}
|
|
||||||
DocsSlashCommandArgs::SearchItemDocs {
|
DocsSlashCommandArgs::SearchItemDocs {
|
||||||
provider,
|
provider,
|
||||||
item_path,
|
item_path,
|
||||||
..
|
..
|
||||||
} => {
|
} => (provider, item_path),
|
||||||
let store = store?;
|
};
|
||||||
let item_docs = store.load(item_path.clone()).await?;
|
|
||||||
|
|
||||||
anyhow::Ok((provider, item_path, item_docs.to_string()))
|
let store = store?;
|
||||||
}
|
let (text, ranges) = if let Some((prefix, _)) = key.split_once('*') {
|
||||||
|
let docs = store.load_many_by_prefix(prefix.to_string()).await?;
|
||||||
|
|
||||||
|
let mut text = String::new();
|
||||||
|
let mut ranges = Vec::new();
|
||||||
|
|
||||||
|
for (key, docs) in docs {
|
||||||
|
let prev_len = text.len();
|
||||||
|
|
||||||
|
text.push_str(&docs.0);
|
||||||
|
text.push_str("\n");
|
||||||
|
ranges.push((key, prev_len..text.len()));
|
||||||
|
text.push_str("\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
(text, ranges)
|
||||||
|
} else {
|
||||||
|
let item_docs = store.load(key.clone()).await?;
|
||||||
|
let text = item_docs.to_string();
|
||||||
|
let range = 0..text.len();
|
||||||
|
|
||||||
|
(text, vec![(key, range)])
|
||||||
|
};
|
||||||
|
|
||||||
|
anyhow::Ok((provider, text, ranges))
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
cx.foreground_executor().spawn(async move {
|
cx.foreground_executor().spawn(async move {
|
||||||
let (provider, path, text) = text.await?;
|
let (provider, text, ranges) = task.await?;
|
||||||
let range = 0..text.len();
|
|
||||||
Ok(SlashCommandOutput {
|
Ok(SlashCommandOutput {
|
||||||
text,
|
text,
|
||||||
sections: vec![SlashCommandOutputSection {
|
sections: ranges
|
||||||
|
.into_iter()
|
||||||
|
.map(|(key, range)| SlashCommandOutputSection {
|
||||||
range,
|
range,
|
||||||
icon: IconName::FileDoc,
|
icon: IconName::FileDoc,
|
||||||
label: format!("docs ({provider}): {path}",).into(),
|
label: format!("docs ({provider}): {key}",).into(),
|
||||||
}],
|
})
|
||||||
|
.collect(),
|
||||||
run_commands_in_text: false,
|
run_commands_in_text: false,
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
|
@ -103,6 +103,15 @@ impl IndexedDocsStore {
|
||||||
.await
|
.await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub async fn load_many_by_prefix(&self, prefix: String) -> Result<Vec<(String, MarkdownDocs)>> {
|
||||||
|
self.database_future
|
||||||
|
.clone()
|
||||||
|
.await
|
||||||
|
.map_err(|err| anyhow!(err))?
|
||||||
|
.load_many_by_prefix(prefix)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
pub fn index(
|
pub fn index(
|
||||||
self: Arc<Self>,
|
self: Arc<Self>,
|
||||||
package: PackageName,
|
package: PackageName,
|
||||||
|
@ -257,6 +266,28 @@ impl IndexedDocsDatabase {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn load_many_by_prefix(&self, prefix: String) -> Task<Result<Vec<(String, MarkdownDocs)>>> {
|
||||||
|
let env = self.env.clone();
|
||||||
|
let entries = self.entries;
|
||||||
|
|
||||||
|
self.executor.spawn(async move {
|
||||||
|
let txn = env.read_txn()?;
|
||||||
|
let results = entries
|
||||||
|
.iter(&txn)?
|
||||||
|
.filter_map(|entry| {
|
||||||
|
let (key, value) = entry.ok()?;
|
||||||
|
if key.starts_with(&prefix) {
|
||||||
|
Some((key, value))
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect::<Vec<_>>();
|
||||||
|
|
||||||
|
Ok(results)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
pub fn insert(&self, key: String, docs: String) -> Task<Result<()>> {
|
pub fn insert(&self, key: String, docs: String) -> Task<Result<()>> {
|
||||||
let env = self.env.clone();
|
let env = self.env.clone();
|
||||||
let entries = self.entries;
|
let entries = self.entries;
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue