ide_db/
documentation.rs

1//! Documentation attribute related utilities.
2use std::borrow::Cow;
3
4use hir::{HasAttrs, db::HirDatabase, resolve_doc_path_on};
5
6/// Holds documentation
7#[derive(Debug, Clone, PartialEq, Eq, Hash)]
8pub struct Documentation<'db>(Cow<'db, str>);
9
10impl<'db> Documentation<'db> {
11    #[inline]
12    pub fn new_owned(s: String) -> Self {
13        Documentation(Cow::Owned(s))
14    }
15
16    #[inline]
17    pub fn new_borrowed(s: &'db str) -> Self {
18        Documentation(Cow::Borrowed(s))
19    }
20
21    #[inline]
22    pub fn into_owned(self) -> Documentation<'static> {
23        Documentation::new_owned(self.0.into_owned())
24    }
25
26    #[inline]
27    pub fn as_str(&self) -> &str {
28        &self.0
29    }
30}
31
32pub trait HasDocs: HasAttrs + Copy {
33    fn docs(self, db: &dyn HirDatabase) -> Option<Documentation<'_>> {
34        let docs = match self.docs_with_rangemap(db)? {
35            Cow::Borrowed(docs) => Documentation::new_borrowed(docs.docs()),
36            Cow::Owned(docs) => Documentation::new_owned(docs.into_docs()),
37        };
38        Some(docs)
39    }
40    fn docs_with_rangemap(self, db: &dyn HirDatabase) -> Option<Cow<'_, hir::Docs>> {
41        self.hir_docs(db).map(Cow::Borrowed)
42    }
43    fn resolve_doc_path(
44        self,
45        db: &dyn HirDatabase,
46        link: &str,
47        ns: Option<hir::Namespace>,
48        is_inner_doc: hir::IsInnerDoc,
49    ) -> Option<hir::DocLinkDef> {
50        resolve_doc_path_on(db, self, link, ns, is_inner_doc)
51    }
52}
53
54macro_rules! impl_has_docs {
55    ($($def:ident,)*) => {$(
56        impl HasDocs for hir::$def {}
57    )*};
58}
59
60impl_has_docs![
61    Variant, Field, Static, Const, Trait, TypeAlias, Macro, Function, Adt, Module, Impl, Crate,
62    AssocItem, Struct, Union, Enum,
63];
64
65impl HasDocs for hir::ExternCrateDecl {
66    fn docs(self, db: &dyn HirDatabase) -> Option<Documentation<'_>> {
67        let crate_docs = self.resolved_crate(db)?.hir_docs(db);
68        let decl_docs = self.hir_docs(db);
69        match (decl_docs, crate_docs) {
70            (None, None) => None,
71            (Some(docs), None) | (None, Some(docs)) => {
72                Some(Documentation::new_borrowed(docs.docs()))
73            }
74            (Some(decl_docs), Some(crate_docs)) => {
75                let mut docs = String::with_capacity(
76                    decl_docs.docs().len() + "\n\n".len() + crate_docs.docs().len(),
77                );
78                docs.push_str(decl_docs.docs());
79                docs.push_str("\n\n");
80                docs.push_str(crate_docs.docs());
81                Some(Documentation::new_owned(docs))
82            }
83        }
84    }
85
86    fn docs_with_rangemap(self, db: &dyn HirDatabase) -> Option<Cow<'_, hir::Docs>> {
87        let crate_docs = self.resolved_crate(db)?.hir_docs(db);
88        let decl_docs = self.hir_docs(db);
89        match (decl_docs, crate_docs) {
90            (None, None) => None,
91            (Some(docs), None) | (None, Some(docs)) => Some(Cow::Borrowed(docs)),
92            (Some(decl_docs), Some(crate_docs)) => {
93                let mut docs = decl_docs.clone();
94                docs.append_str("\n\n");
95                docs.append(crate_docs);
96                Some(Cow::Owned(docs))
97            }
98        }
99    }
100}