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    EnumVariant,
62    Field,
63    Static,
64    Const,
65    Trait,
66    TypeAlias,
67    Macro,
68    Function,
69    Adt,
70    Module,
71    Impl,
72    Crate,
73    AssocItem,
74    Struct,
75    Union,
76    Enum,
77];
78
79impl HasDocs for hir::ExternCrateDecl {
80    fn docs(self, db: &dyn HirDatabase) -> Option<Documentation<'_>> {
81        let crate_docs = self.resolved_crate(db)?.hir_docs(db);
82        let decl_docs = self.hir_docs(db);
83        match (decl_docs, crate_docs) {
84            (None, None) => None,
85            (Some(docs), None) | (None, Some(docs)) => {
86                Some(Documentation::new_borrowed(docs.docs()))
87            }
88            (Some(decl_docs), Some(crate_docs)) => {
89                let mut docs = String::with_capacity(
90                    decl_docs.docs().len() + "\n\n".len() + crate_docs.docs().len(),
91                );
92                docs.push_str(decl_docs.docs());
93                docs.push_str("\n\n");
94                docs.push_str(crate_docs.docs());
95                Some(Documentation::new_owned(docs))
96            }
97        }
98    }
99
100    fn docs_with_rangemap(self, db: &dyn HirDatabase) -> Option<Cow<'_, hir::Docs>> {
101        let crate_docs = self.resolved_crate(db)?.hir_docs(db);
102        let decl_docs = self.hir_docs(db);
103        match (decl_docs, crate_docs) {
104            (None, None) => None,
105            (Some(docs), None) | (None, Some(docs)) => Some(Cow::Borrowed(docs)),
106            (Some(decl_docs), Some(crate_docs)) => {
107                let mut docs = decl_docs.clone();
108                docs.append_str("\n\n");
109                docs.append(crate_docs);
110                Some(Cow::Owned(docs))
111            }
112        }
113    }
114}