aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAaron Loucks <[email protected]>2020-05-30 19:21:06 +0100
committerAaron Loucks <[email protected]>2020-06-03 11:46:07 +0100
commit4c655c01f31ceffae4f8219f9706992e0e7f188a (patch)
tree6d295976e70602a08268fa97fefd1c1f82dba59f
parent9b3d4be42120796c9845376a9ad315736fc85eca (diff)
Enable hover and autocomplete docs on macro generated items
-rw-r--r--crates/ra_hir_def/src/attr.rs8
-rw-r--r--crates/ra_hir_def/src/docs.rs43
-rw-r--r--crates/ra_hir_expand/src/name.rs1
-rw-r--r--crates/ra_ide/src/hover.rs55
-rw-r--r--crates/ra_syntax/src/ast/traits.rs13
5 files changed, 110 insertions, 10 deletions
diff --git a/crates/ra_hir_def/src/attr.rs b/crates/ra_hir_def/src/attr.rs
index 8b6c0bede..2eeba0572 100644
--- a/crates/ra_hir_def/src/attr.rs
+++ b/crates/ra_hir_def/src/attr.rs
@@ -87,12 +87,18 @@ impl Attrs {
87 } 87 }
88 88
89 pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Attrs { 89 pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Attrs {
90 let docs = ast::CommentIter::from_syntax_node(owner.syntax()).doc_comment_text().map(
91 |docs_text| Attr {
92 input: Some(AttrInput::Literal(SmolStr::new(docs_text))),
93 path: ModPath::from(hir_expand::name!(doc)),
94 },
95 );
90 let mut attrs = owner.attrs().peekable(); 96 let mut attrs = owner.attrs().peekable();
91 let entries = if attrs.peek().is_none() { 97 let entries = if attrs.peek().is_none() {
92 // Avoid heap allocation 98 // Avoid heap allocation
93 None 99 None
94 } else { 100 } else {
95 Some(attrs.flat_map(|ast| Attr::from_src(ast, hygiene)).collect()) 101 Some(attrs.flat_map(|ast| Attr::from_src(ast, hygiene)).chain(docs).collect())
96 }; 102 };
97 Attrs { entries } 103 Attrs { entries }
98 } 104 }
diff --git a/crates/ra_hir_def/src/docs.rs b/crates/ra_hir_def/src/docs.rs
index b221ae1ce..74b9f8199 100644
--- a/crates/ra_hir_def/src/docs.rs
+++ b/crates/ra_hir_def/src/docs.rs
@@ -70,6 +70,45 @@ impl Documentation {
70 } 70 }
71} 71}
72 72
73pub(crate) fn docs_from_ast(node: &impl ast::DocCommentsOwner) -> Option<Documentation> { 73pub(crate) fn docs_from_ast<N>(node: &N) -> Option<Documentation>
74 node.doc_comment_text().map(|it| Documentation::new(&it)) 74where
75 N: ast::DocCommentsOwner + ast::AttrsOwner,
76{
77 let doc_comment_text = node.doc_comment_text();
78 let doc_attr_text = expand_doc_attrs(node);
79 let docs = merge_doc_comments_and_attrs(doc_comment_text, doc_attr_text);
80 docs.map(|it| Documentation::new(&it))
81}
82
83fn merge_doc_comments_and_attrs(
84 doc_comment_text: Option<String>,
85 doc_attr_text: Option<String>,
86) -> Option<String> {
87 match (doc_comment_text, doc_attr_text) {
88 (Some(mut comment_text), Some(attr_text)) => {
89 comment_text.push_str("\n\n");
90 comment_text.push_str(&attr_text);
91 Some(comment_text)
92 }
93 (Some(comment_text), None) => Some(comment_text),
94 (None, Some(attr_text)) => Some(attr_text),
95 (None, None) => None,
96 }
97}
98
99fn expand_doc_attrs(owner: &dyn ast::AttrsOwner) -> Option<String> {
100 let mut docs = String::new();
101 for attr in owner.attrs() {
102 if let Some(("doc", value)) =
103 attr.as_simple_key_value().as_ref().map(|(k, v)| (k.as_str(), v.as_str()))
104 {
105 docs.push_str(value);
106 docs.push_str("\n\n");
107 }
108 }
109 if docs.is_empty() {
110 None
111 } else {
112 Some(docs)
113 }
75} 114}
diff --git a/crates/ra_hir_expand/src/name.rs b/crates/ra_hir_expand/src/name.rs
index ea495cb11..660bdfe33 100644
--- a/crates/ra_hir_expand/src/name.rs
+++ b/crates/ra_hir_expand/src/name.rs
@@ -153,6 +153,7 @@ pub mod known {
153 str, 153 str,
154 // Special names 154 // Special names
155 macro_rules, 155 macro_rules,
156 doc,
156 // Components of known path (value or mod name) 157 // Components of known path (value or mod name)
157 std, 158 std,
158 core, 159 core,
diff --git a/crates/ra_ide/src/hover.rs b/crates/ra_ide/src/hover.rs
index d96cb5596..e25a7dacf 100644
--- a/crates/ra_ide/src/hover.rs
+++ b/crates/ra_ide/src/hover.rs
@@ -169,13 +169,19 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<Strin
169 return match def { 169 return match def {
170 Definition::Macro(it) => { 170 Definition::Macro(it) => {
171 let src = it.source(db); 171 let src = it.source(db);
172 hover_text(src.value.doc_comment_text(), Some(macro_label(&src.value)), mod_path) 172 let doc_comment_text = src.value.doc_comment_text();
173 let doc_attr_text = expand_doc_attrs(&src.value);
174 let docs = merge_doc_comments_and_attrs(doc_comment_text, doc_attr_text);
175 hover_text(docs, Some(macro_label(&src.value)), mod_path)
173 } 176 }
174 Definition::Field(it) => { 177 Definition::Field(it) => {
175 let src = it.source(db); 178 let src = it.source(db);
176 match src.value { 179 match src.value {
177 FieldSource::Named(it) => { 180 FieldSource::Named(it) => {
178 hover_text(it.doc_comment_text(), it.short_label(), mod_path) 181 let doc_comment_text = it.doc_comment_text();
182 let doc_attr_text = expand_doc_attrs(&it);
183 let docs = merge_doc_comments_and_attrs(doc_comment_text, doc_attr_text);
184 hover_text(docs, it.short_label(), mod_path)
179 } 185 }
180 _ => None, 186 _ => None,
181 } 187 }
@@ -183,7 +189,10 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<Strin
183 Definition::ModuleDef(it) => match it { 189 Definition::ModuleDef(it) => match it {
184 ModuleDef::Module(it) => match it.definition_source(db).value { 190 ModuleDef::Module(it) => match it.definition_source(db).value {
185 ModuleSource::Module(it) => { 191 ModuleSource::Module(it) => {
186 hover_text(it.doc_comment_text(), it.short_label(), mod_path) 192 let doc_comment_text = it.doc_comment_text();
193 let doc_attr_text = expand_doc_attrs(&it);
194 let docs = merge_doc_comments_and_attrs(doc_comment_text, doc_attr_text);
195 hover_text(docs, it.short_label(), mod_path)
187 } 196 }
188 _ => None, 197 _ => None,
189 }, 198 },
@@ -208,10 +217,46 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<Strin
208 fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<String> 217 fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<String>
209 where 218 where
210 D: HasSource<Ast = A>, 219 D: HasSource<Ast = A>,
211 A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel, 220 A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel + ast::AttrsOwner,
212 { 221 {
213 let src = def.source(db); 222 let src = def.source(db);
214 hover_text(src.value.doc_comment_text(), src.value.short_label(), mod_path) 223 let doc_comment_text = src.value.doc_comment_text();
224 let doc_attr_text = expand_doc_attrs(&src.value);
225 let docs = merge_doc_comments_and_attrs(doc_comment_text, doc_attr_text);
226 hover_text(docs, src.value.short_label(), mod_path)
227 }
228}
229
230fn merge_doc_comments_and_attrs(
231 doc_comment_text: Option<String>,
232 doc_attr_text: Option<String>,
233) -> Option<String> {
234 match (doc_comment_text, doc_attr_text) {
235 (Some(mut comment_text), Some(attr_text)) => {
236 comment_text.push_str("\n\n");
237 comment_text.push_str(&attr_text);
238 Some(comment_text)
239 }
240 (Some(comment_text), None) => Some(comment_text),
241 (None, Some(attr_text)) => Some(attr_text),
242 (None, None) => None,
243 }
244}
245
246fn expand_doc_attrs(owner: &dyn ast::AttrsOwner) -> Option<String> {
247 let mut docs = String::new();
248 for attr in owner.attrs() {
249 if let Some(("doc", value)) =
250 attr.as_simple_key_value().as_ref().map(|(k, v)| (k.as_str(), v.as_str()))
251 {
252 docs.push_str(value);
253 docs.push_str("\n\n");
254 }
255 }
256 if docs.is_empty() {
257 None
258 } else {
259 Some(docs)
215 } 260 }
216} 261}
217 262
diff --git a/crates/ra_syntax/src/ast/traits.rs b/crates/ra_syntax/src/ast/traits.rs
index bfc05e08b..a8f2454fd 100644
--- a/crates/ra_syntax/src/ast/traits.rs
+++ b/crates/ra_syntax/src/ast/traits.rs
@@ -83,13 +83,22 @@ pub trait DocCommentsOwner: AstNode {
83 CommentIter { iter: self.syntax().children_with_tokens() } 83 CommentIter { iter: self.syntax().children_with_tokens() }
84 } 84 }
85 85
86 fn doc_comment_text(&self) -> Option<String> {
87 self.doc_comments().doc_comment_text()
88 }
89}
90
91impl CommentIter {
92 pub fn from_syntax_node(syntax_node: &ast::SyntaxNode) -> CommentIter {
93 CommentIter { iter: syntax_node.children_with_tokens() }
94 }
95
86 /// Returns the textual content of a doc comment block as a single string. 96 /// Returns the textual content of a doc comment block as a single string.
87 /// That is, strips leading `///` (+ optional 1 character of whitespace), 97 /// That is, strips leading `///` (+ optional 1 character of whitespace),
88 /// trailing `*/`, trailing whitespace and then joins the lines. 98 /// trailing `*/`, trailing whitespace and then joins the lines.
89 fn doc_comment_text(&self) -> Option<String> { 99 pub fn doc_comment_text(self) -> Option<String> {
90 let mut has_comments = false; 100 let mut has_comments = false;
91 let docs = self 101 let docs = self
92 .doc_comments()
93 .filter(|comment| comment.kind().doc.is_some()) 102 .filter(|comment| comment.kind().doc.is_some())
94 .map(|comment| { 103 .map(|comment| {
95 has_comments = true; 104 has_comments = true;