aboutsummaryrefslogtreecommitdiff
path: root/crates/hir_def/src/docs.rs
diff options
context:
space:
mode:
Diffstat (limited to 'crates/hir_def/src/docs.rs')
-rw-r--r--crates/hir_def/src/docs.rs121
1 files changed, 121 insertions, 0 deletions
diff --git a/crates/hir_def/src/docs.rs b/crates/hir_def/src/docs.rs
new file mode 100644
index 000000000..e9a02b11b
--- /dev/null
+++ b/crates/hir_def/src/docs.rs
@@ -0,0 +1,121 @@
1//! Defines hir documentation.
2//!
3//! This really shouldn't exist, instead, we should deshugar doc comments into attributes, see
4//! https://github.com/rust-analyzer/rust-analyzer/issues/2148#issuecomment-550519102
5
6use std::sync::Arc;
7
8use either::Either;
9use syntax::ast;
10
11use crate::{
12 db::DefDatabase,
13 src::{HasChildSource, HasSource},
14 AdtId, AttrDefId, Lookup,
15};
16
17/// Holds documentation
18#[derive(Debug, Clone, PartialEq, Eq)]
19pub struct Documentation(Arc<str>);
20
21impl Into<String> for Documentation {
22 fn into(self) -> String {
23 self.as_str().to_owned()
24 }
25}
26
27impl Documentation {
28 fn new(s: &str) -> Documentation {
29 Documentation(s.into())
30 }
31
32 pub fn from_ast<N>(node: &N) -> Option<Documentation>
33 where
34 N: ast::DocCommentsOwner + ast::AttrsOwner,
35 {
36 docs_from_ast(node)
37 }
38
39 pub fn as_str(&self) -> &str {
40 &*self.0
41 }
42
43 pub(crate) fn documentation_query(
44 db: &dyn DefDatabase,
45 def: AttrDefId,
46 ) -> Option<Documentation> {
47 match def {
48 AttrDefId::ModuleId(module) => {
49 let def_map = db.crate_def_map(module.krate);
50 let src = def_map[module.local_id].declaration_source(db)?;
51 docs_from_ast(&src.value)
52 }
53 AttrDefId::FieldId(it) => {
54 let src = it.parent.child_source(db);
55 match &src.value[it.local_id] {
56 Either::Left(_tuple) => None,
57 Either::Right(record) => docs_from_ast(record),
58 }
59 }
60 AttrDefId::AdtId(it) => match it {
61 AdtId::StructId(it) => docs_from_ast(&it.lookup(db).source(db).value),
62 AdtId::EnumId(it) => docs_from_ast(&it.lookup(db).source(db).value),
63 AdtId::UnionId(it) => docs_from_ast(&it.lookup(db).source(db).value),
64 },
65 AttrDefId::EnumVariantId(it) => {
66 let src = it.parent.child_source(db);
67 docs_from_ast(&src.value[it.local_id])
68 }
69 AttrDefId::TraitId(it) => docs_from_ast(&it.lookup(db).source(db).value),
70 AttrDefId::MacroDefId(it) => docs_from_ast(&it.ast_id?.to_node(db.upcast())),
71 AttrDefId::ConstId(it) => docs_from_ast(&it.lookup(db).source(db).value),
72 AttrDefId::StaticId(it) => docs_from_ast(&it.lookup(db).source(db).value),
73 AttrDefId::FunctionId(it) => docs_from_ast(&it.lookup(db).source(db).value),
74 AttrDefId::TypeAliasId(it) => docs_from_ast(&it.lookup(db).source(db).value),
75 AttrDefId::ImplId(_) => None,
76 }
77 }
78}
79
80pub(crate) fn docs_from_ast<N>(node: &N) -> Option<Documentation>
81where
82 N: ast::DocCommentsOwner + ast::AttrsOwner,
83{
84 let doc_comment_text = node.doc_comment_text();
85 let doc_attr_text = expand_doc_attrs(node);
86 let docs = merge_doc_comments_and_attrs(doc_comment_text, doc_attr_text);
87 docs.map(|it| Documentation::new(&it))
88}
89
90fn merge_doc_comments_and_attrs(
91 doc_comment_text: Option<String>,
92 doc_attr_text: Option<String>,
93) -> Option<String> {
94 match (doc_comment_text, doc_attr_text) {
95 (Some(mut comment_text), Some(attr_text)) => {
96 comment_text.push_str("\n\n");
97 comment_text.push_str(&attr_text);
98 Some(comment_text)
99 }
100 (Some(comment_text), None) => Some(comment_text),
101 (None, Some(attr_text)) => Some(attr_text),
102 (None, None) => None,
103 }
104}
105
106fn expand_doc_attrs(owner: &dyn ast::AttrsOwner) -> Option<String> {
107 let mut docs = String::new();
108 for attr in owner.attrs() {
109 if let Some(("doc", value)) =
110 attr.as_simple_key_value().as_ref().map(|(k, v)| (k.as_str(), v.as_str()))
111 {
112 docs.push_str(value);
113 docs.push_str("\n\n");
114 }
115 }
116 if docs.is_empty() {
117 None
118 } else {
119 Some(docs.trim_end_matches("\n\n").to_owned())
120 }
121}