rustdoc: Automatically index crates (#13014)

This PR removes the need to use `/rustdoc --index <CRATE_NAME>` and
instead indexes the crates once they are referenced.

As soon as the first `:` is added after the crate name, the indexing
will kick off in the background and update the index as it goes.

Release Notes:

- N/A
This commit is contained in:
Marshall Bowers 2024-06-13 18:30:15 -04:00 committed by GitHub
parent e0c1ab650e
commit 86167138a9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
5 changed files with 70 additions and 102 deletions

View file

@ -13,6 +13,7 @@ use project::{Project, ProjectPath};
use rustdoc::LocalProvider;
use rustdoc::{convert_rustdoc_to_markdown, RustdocStore};
use ui::{prelude::*, ButtonLike, ElevationIndex};
use util::{maybe, ResultExt};
use workspace::Workspace;
#[derive(Debug, Clone, Copy)]
@ -118,11 +119,36 @@ impl SlashCommand for RustdocSlashCommand {
&self,
query: String,
_cancel: Arc<AtomicBool>,
_workspace: Option<WeakView<Workspace>>,
workspace: Option<WeakView<Workspace>>,
cx: &mut AppContext,
) -> Task<Result<Vec<String>>> {
let index_provider_deps = maybe!({
let workspace = workspace.ok_or_else(|| anyhow!("no workspace"))?;
let workspace = workspace
.upgrade()
.ok_or_else(|| anyhow!("workspace was dropped"))?;
let project = workspace.read(cx).project().clone();
let fs = project.read(cx).fs().clone();
let cargo_workspace_root = Self::path_to_cargo_toml(project, cx)
.and_then(|path| path.parent().map(|path| path.to_path_buf()))
.ok_or_else(|| anyhow!("no Cargo workspace root found"))?;
anyhow::Ok((fs, cargo_workspace_root))
});
let store = RustdocStore::global(cx);
cx.background_executor().spawn(async move {
if let Some((crate_name, rest)) = query.split_once(':') {
if rest.is_empty() {
if let Some((fs, cargo_workspace_root)) = index_provider_deps.log_err() {
let provider = Box::new(LocalProvider::new(fs, cargo_workspace_root));
// We don't need to hold onto this task, as the `RustdocStore` will hold it
// until it completes.
let _ = store.clone().index(crate_name.to_string(), provider);
}
}
}
let items = store.search(query).await;
Ok(items)
})
@ -147,65 +173,7 @@ impl SlashCommand for RustdocSlashCommand {
let http_client = workspace.read(cx).client().http_client();
let path_to_cargo_toml = Self::path_to_cargo_toml(project, cx);
let mut item_path = String::new();
let mut crate_name_to_index = None;
let mut args = argument.split(' ').map(|word| word.trim());
while let Some(arg) = args.next() {
if arg == "--index" {
let Some(crate_name) = args.next() else {
return Task::ready(Err(anyhow!("no crate name provided to --index")));
};
crate_name_to_index = Some(crate_name.to_string());
continue;
}
item_path.push_str(arg);
}
if let Some(crate_name_to_index) = crate_name_to_index {
let index_task = cx.background_executor().spawn({
let rustdoc_store = RustdocStore::global(cx);
let fs = fs.clone();
let crate_name_to_index = crate_name_to_index.clone();
async move {
let cargo_workspace_root = path_to_cargo_toml
.and_then(|path| path.parent().map(|path| path.to_path_buf()))
.ok_or_else(|| anyhow!("no Cargo workspace root found"))?;
let provider = Box::new(LocalProvider::new(fs, cargo_workspace_root));
rustdoc_store
.index(crate_name_to_index.clone(), provider)
.await?;
anyhow::Ok(format!("Indexed {crate_name_to_index}"))
}
});
return cx.foreground_executor().spawn(async move {
let text = index_task.await?;
let range = 0..text.len();
Ok(SlashCommandOutput {
text,
sections: vec![SlashCommandOutputSection {
range,
render_placeholder: Arc::new(move |id, unfold, _cx| {
RustdocIndexPlaceholder {
id,
unfold,
source: RustdocSource::Local,
crate_name: SharedString::from(crate_name_to_index.clone()),
}
.into_any_element()
}),
}],
run_commands_in_text: false,
})
});
}
let mut path_components = item_path.split("::");
let mut path_components = argument.split("::");
let crate_name = match path_components
.next()
.ok_or_else(|| anyhow!("missing crate name"))
@ -301,31 +269,3 @@ impl RenderOnce for RustdocPlaceholder {
.on_click(move |_, cx| unfold(cx))
}
}
#[derive(IntoElement)]
struct RustdocIndexPlaceholder {
pub id: ElementId,
pub unfold: Arc<dyn Fn(&mut WindowContext)>,
pub source: RustdocSource,
pub crate_name: SharedString,
}
impl RenderOnce for RustdocIndexPlaceholder {
fn render(self, _cx: &mut WindowContext) -> impl IntoElement {
let unfold = self.unfold;
ButtonLike::new(self.id)
.style(ButtonStyle::Filled)
.layer(ElevationIndex::ElevatedSurface)
.child(Icon::new(IconName::FileRust))
.child(Label::new(format!(
"rustdoc index ({source}): {crate_name}",
crate_name = self.crate_name,
source = match self.source {
RustdocSource::Local => "local",
RustdocSource::DocsDotRs => "docs.rs",
}
)))
.on_click(move |_, cx| unfold(cx))
}
}