@@ -2445,19 +2445,43 @@ fn render_docs_slash_command_trailer(
return Empty.into_any();
};
- if !store.is_indexing(&package) {
+ let mut children = Vec::new();
+
+ if store.is_indexing(&package) {
+ children.push(
+ div()
+ .id(("crates-being-indexed", row.0))
+ .child(Icon::new(IconName::ArrowCircle).with_animation(
+ "arrow-circle",
+ Animation::new(Duration::from_secs(4)).repeat(),
+ |icon, delta| icon.transform(Transformation::rotate(percentage(delta))),
+ ))
+ .tooltip({
+ let package = package.clone();
+ move |cx| Tooltip::text(format!("Indexing {package}…"), cx)
+ })
+ .into_any_element(),
+ );
+ }
+
+ if let Some(latest_error) = store.latest_error_for_package(&package) {
+ children.push(
+ div()
+ .id(("latest-error", row.0))
+ .child(Icon::new(IconName::ExclamationTriangle).color(Color::Warning))
+ .tooltip(move |cx| Tooltip::text(format!("failed to index: {latest_error}"), cx))
+ .into_any_element(),
+ )
+ }
+
+ let is_indexing = store.is_indexing(&package);
+ let latest_error = store.latest_error_for_package(&package);
+
+ if !is_indexing && latest_error.is_none() {
return Empty.into_any();
}
- div()
- .id(("crates-being-indexed", row.0))
- .child(Icon::new(IconName::ArrowCircle).with_animation(
- "arrow-circle",
- Animation::new(Duration::from_secs(4)).repeat(),
- |icon, delta| icon.transform(Transformation::rotate(percentage(delta))),
- ))
- .tooltip(move |cx| Tooltip::text(format!("Indexing {package}…"), cx))
- .into_any_element()
+ h_flex().gap_2().children(children).into_any_element()
}
fn make_lsp_adapter_delegate(
@@ -158,6 +158,11 @@ impl RustdocProvider for LocalProvider {
) -> Result<Option<String>> {
let mut local_cargo_doc_path = self.cargo_workspace_root.join("target/doc");
local_cargo_doc_path.push(crate_name.as_ref());
+
+ if !self.fs.is_dir(&local_cargo_doc_path).await {
+ bail!("docs directory for '{crate_name}' does not exist. run `cargo doc`");
+ }
+
if let Some(item) = item {
local_cargo_doc_path.push(item.url_path());
} else {
@@ -57,6 +57,7 @@ pub struct IndexedDocsStore {
provider: Box<dyn IndexedDocsProvider + Send + Sync + 'static>,
indexing_tasks_by_package:
RwLock<HashMap<PackageName, Shared<Task<Result<(), Arc<anyhow::Error>>>>>>,
+ latest_errors_by_package: RwLock<HashMap<PackageName, Arc<str>>>,
}
impl IndexedDocsStore {
@@ -86,9 +87,14 @@ impl IndexedDocsStore {
database_future,
provider,
indexing_tasks_by_package: RwLock::new(HashMap::default()),
+ latest_errors_by_package: RwLock::new(HashMap::default()),
}
}
+ pub fn latest_error_for_package(&self, package: &PackageName) -> Option<Arc<str>> {
+ self.latest_errors_by_package.read().get(package).cloned()
+ }
+
/// Returns whether the package with the given name is currently being indexed.
pub fn is_indexing(&self, package: &PackageName) -> bool {
self.indexing_tasks_by_package.read().contains_key(package)
@@ -125,16 +131,31 @@ impl IndexedDocsStore {
}
});
- let index_task = async {
- let database = this
- .database_future
- .clone()
- .await
- .map_err(|err| anyhow!(err))?;
- this.provider.index(package, database).await
+ let index_task = {
+ let package = package.clone();
+ async {
+ let database = this
+ .database_future
+ .clone()
+ .await
+ .map_err(|err| anyhow!(err))?;
+ this.provider.index(package, database).await
+ }
};
- index_task.await.map_err(Arc::new)
+ let result = index_task.await.map_err(Arc::new);
+ match &result {
+ Ok(_) => {
+ this.latest_errors_by_package.write().remove(&package);
+ }
+ Err(err) => {
+ this.latest_errors_by_package
+ .write()
+ .insert(package, err.to_string().into());
+ }
+ }
+
+ result
}
})
.shared();