Skip to content

Commit 4c655c0

Browse files
committed
Enable hover and autocomplete docs on macro generated items
1 parent 9b3d4be commit 4c655c0

File tree

5 files changed

+110
-10
lines changed

5 files changed

+110
-10
lines changed

crates/ra_hir_def/src/attr.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,12 +87,18 @@ impl Attrs {
8787
}
8888

8989
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+
);
9096
let mut attrs = owner.attrs().peekable();
9197
let entries = if attrs.peek().is_none() {
9298
// Avoid heap allocation
9399
None
94100
} 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())
96102
};
97103
Attrs { entries }
98104
}

crates/ra_hir_def/src/docs.rs

Lines changed: 41 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,45 @@ impl Documentation {
7070
}
7171
}
7272

73-
pub(crate) fn docs_from_ast(node: &impl ast::DocCommentsOwner) -> Option<Documentation> {
74-
node.doc_comment_text().map(|it| Documentation::new(&it))
73+
pub(crate) fn docs_from_ast<N>(node: &N) -> Option<Documentation>
74+
where
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+
83+
fn 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+
99+
fn 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+
}
75114
}

crates/ra_hir_expand/src/name.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,7 @@ pub mod known {
153153
str,
154154
// Special names
155155
macro_rules,
156+
doc,
156157
// Components of known path (value or mod name)
157158
std,
158159
core,

crates/ra_ide/src/hover.rs

Lines changed: 50 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -169,21 +169,30 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<Strin
169169
return match def {
170170
Definition::Macro(it) => {
171171
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)
173176
}
174177
Definition::Field(it) => {
175178
let src = it.source(db);
176179
match src.value {
177180
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)
179185
}
180186
_ => None,
181187
}
182188
}
183189
Definition::ModuleDef(it) => match it {
184190
ModuleDef::Module(it) => match it.definition_source(db).value {
185191
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)
187196
}
188197
_ => None,
189198
},
@@ -208,10 +217,46 @@ fn hover_text_from_name_kind(db: &RootDatabase, def: Definition) -> Option<Strin
208217
fn from_def_source<A, D>(db: &RootDatabase, def: D, mod_path: Option<String>) -> Option<String>
209218
where
210219
D: HasSource<Ast = A>,
211-
A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel,
220+
A: ast::DocCommentsOwner + ast::NameOwner + ShortLabel + ast::AttrsOwner,
212221
{
213222
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+
230+
fn 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+
246+
fn 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)
215260
}
216261
}
217262

crates/ra_syntax/src/ast/traits.rs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -83,13 +83,22 @@ pub trait DocCommentsOwner: AstNode {
8383
CommentIter { iter: self.syntax().children_with_tokens() }
8484
}
8585

86+
fn doc_comment_text(&self) -> Option<String> {
87+
self.doc_comments().doc_comment_text()
88+
}
89+
}
90+
91+
impl CommentIter {
92+
pub fn from_syntax_node(syntax_node: &ast::SyntaxNode) -> CommentIter {
93+
CommentIter { iter: syntax_node.children_with_tokens() }
94+
}
95+
8696
/// Returns the textual content of a doc comment block as a single string.
8797
/// That is, strips leading `///` (+ optional 1 character of whitespace),
8898
/// 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> {
90100
let mut has_comments = false;
91101
let docs = self
92-
.doc_comments()
93102
.filter(|comment| comment.kind().doc.is_some())
94103
.map(|comment| {
95104
has_comments = true;

0 commit comments

Comments
 (0)