blob: 4c4691cca2ca196a56944288f82d0c3d79072da6 [file] [log] [blame]
//! Documentation attribute related utilities.
use std::borrow::Cow;
use hir::{HasAttrs, db::HirDatabase, resolve_doc_path_on};
/// Holds documentation
#[derive(Debug, Clone, PartialEq, Eq, Hash)]
pub struct Documentation<'db>(Cow<'db, str>);
impl<'db> Documentation<'db> {
#[inline]
pub fn new_owned(s: String) -> Self {
Documentation(Cow::Owned(s))
}
#[inline]
pub fn new_borrowed(s: &'db str) -> Self {
Documentation(Cow::Borrowed(s))
}
#[inline]
pub fn into_owned(self) -> Documentation<'static> {
Documentation::new_owned(self.0.into_owned())
}
#[inline]
pub fn as_str(&self) -> &str {
&self.0
}
}
pub trait HasDocs: HasAttrs + Copy {
fn docs(self, db: &dyn HirDatabase) -> Option<Documentation<'_>> {
let docs = match self.docs_with_rangemap(db)? {
Cow::Borrowed(docs) => Documentation::new_borrowed(docs.docs()),
Cow::Owned(docs) => Documentation::new_owned(docs.into_docs()),
};
Some(docs)
}
fn docs_with_rangemap(self, db: &dyn HirDatabase) -> Option<Cow<'_, hir::Docs>> {
self.hir_docs(db).map(Cow::Borrowed)
}
fn resolve_doc_path(
self,
db: &dyn HirDatabase,
link: &str,
ns: Option<hir::Namespace>,
is_inner_doc: hir::IsInnerDoc,
) -> Option<hir::DocLinkDef> {
resolve_doc_path_on(db, self, link, ns, is_inner_doc)
}
}
macro_rules! impl_has_docs {
($($def:ident,)*) => {$(
impl HasDocs for hir::$def {}
)*};
}
impl_has_docs![
Variant, Field, Static, Const, Trait, TypeAlias, Macro, Function, Adt, Module, Impl, Crate,
AssocItem, Struct, Union, Enum,
];
impl HasDocs for hir::ExternCrateDecl {
fn docs(self, db: &dyn HirDatabase) -> Option<Documentation<'_>> {
let crate_docs = self.resolved_crate(db)?.hir_docs(db);
let decl_docs = self.hir_docs(db);
match (decl_docs, crate_docs) {
(None, None) => None,
(Some(docs), None) | (None, Some(docs)) => {
Some(Documentation::new_borrowed(docs.docs()))
}
(Some(decl_docs), Some(crate_docs)) => {
let mut docs = String::with_capacity(
decl_docs.docs().len() + "\n\n".len() + crate_docs.docs().len(),
);
docs.push_str(decl_docs.docs());
docs.push_str("\n\n");
docs.push_str(crate_docs.docs());
Some(Documentation::new_owned(docs))
}
}
}
fn docs_with_rangemap(self, db: &dyn HirDatabase) -> Option<Cow<'_, hir::Docs>> {
let crate_docs = self.resolved_crate(db)?.hir_docs(db);
let decl_docs = self.hir_docs(db);
match (decl_docs, crate_docs) {
(None, None) => None,
(Some(docs), None) | (None, Some(docs)) => Some(Cow::Borrowed(docs)),
(Some(decl_docs), Some(crate_docs)) => {
let mut docs = decl_docs.clone();
docs.append_str("\n\n");
docs.append(crate_docs);
Some(Cow::Owned(docs))
}
}
}
}