aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--crates/hir_def/src/attr.rs52
-rw-r--r--crates/ide/src/hover.rs62
-rw-r--r--crates/syntax/src/ast/token_ext.rs8
3 files changed, 117 insertions, 5 deletions
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs
index 12f4b02e2..228d706db 100644
--- a/crates/hir_def/src/attr.rs
+++ b/crates/hir_def/src/attr.rs
@@ -9,7 +9,7 @@ use itertools::Itertools;
9use mbe::ast_to_token_tree; 9use mbe::ast_to_token_tree;
10use syntax::{ 10use syntax::{
11 ast::{self, AstNode, AttrsOwner}, 11 ast::{self, AstNode, AttrsOwner},
12 AstToken, SmolStr, 12 match_ast, AstToken, SmolStr, SyntaxNode,
13}; 13};
14use tt::Subtree; 14use tt::Subtree;
15 15
@@ -110,7 +110,17 @@ impl Attrs {
110 } 110 }
111 111
112 pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Attrs { 112 pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Attrs {
113 let docs = ast::CommentIter::from_syntax_node(owner.syntax()).map(|docs_text| { 113 let (inner_attrs, inner_docs) = inner_attributes(owner.syntax())
114 .map_or((None, None), |(attrs, docs)| ((Some(attrs), Some(docs))));
115
116 let outer_attrs = owner.attrs().filter(|attr| attr.excl_token().is_none());
117 let attrs = outer_attrs
118 .chain(inner_attrs.into_iter().flatten())
119 .map(|attr| (attr.syntax().text_range().start(), Attr::from_src(attr, hygiene)));
120
121 let outer_docs =
122 ast::CommentIter::from_syntax_node(owner.syntax()).filter(ast::Comment::is_outer);
123 let docs = outer_docs.chain(inner_docs.into_iter().flatten()).map(|docs_text| {
114 ( 124 (
115 docs_text.syntax().text_range().start(), 125 docs_text.syntax().text_range().start(),
116 docs_text.doc_comment().map(|doc| Attr { 126 docs_text.doc_comment().map(|doc| Attr {
@@ -119,9 +129,6 @@ impl Attrs {
119 }), 129 }),
120 ) 130 )
121 }); 131 });
122 let attrs = owner
123 .attrs()
124 .map(|attr| (attr.syntax().text_range().start(), Attr::from_src(attr, hygiene)));
125 // sort here by syntax node offset because the source can have doc attributes and doc strings be interleaved 132 // sort here by syntax node offset because the source can have doc attributes and doc strings be interleaved
126 let attrs: Vec<_> = docs.chain(attrs).sorted_by_key(|&(offset, _)| offset).collect(); 133 let attrs: Vec<_> = docs.chain(attrs).sorted_by_key(|&(offset, _)| offset).collect();
127 let entries = if attrs.is_empty() { 134 let entries = if attrs.is_empty() {
@@ -184,6 +191,41 @@ impl Attrs {
184 } 191 }
185} 192}
186 193
194fn inner_attributes(
195 syntax: &SyntaxNode,
196) -> Option<(impl Iterator<Item = ast::Attr>, impl Iterator<Item = ast::Comment>)> {
197 let (attrs, docs) = match_ast! {
198 match syntax {
199 ast::SourceFile(it) => (it.attrs(), ast::CommentIter::from_syntax_node(it.syntax())),
200 ast::ExternBlock(it) => {
201 let extern_item_list = it.extern_item_list()?;
202 (extern_item_list.attrs(), ast::CommentIter::from_syntax_node(extern_item_list.syntax()))
203 },
204 ast::Fn(it) => {
205 let body = it.body()?;
206 (body.attrs(), ast::CommentIter::from_syntax_node(body.syntax()))
207 },
208 ast::Impl(it) => {
209 let assoc_item_list = it.assoc_item_list()?;
210 (assoc_item_list.attrs(), ast::CommentIter::from_syntax_node(assoc_item_list.syntax()))
211 },
212 ast::Module(it) => {
213 let item_list = it.item_list()?;
214 (item_list.attrs(), ast::CommentIter::from_syntax_node(item_list.syntax()))
215 },
216 // FIXME: BlockExpr's only accept inner attributes in specific cases
217 // Excerpt from the reference:
218 // Block expressions accept outer and inner attributes, but only when they are the outer
219 // expression of an expression statement or the final expression of another block expression.
220 ast::BlockExpr(it) => return None,
221 _ => return None,
222 }
223 };
224 let attrs = attrs.filter(|attr| attr.excl_token().is_some());
225 let docs = docs.filter(|doc| doc.is_inner());
226 Some((attrs, docs))
227}
228
187#[derive(Debug, Clone, PartialEq, Eq)] 229#[derive(Debug, Clone, PartialEq, Eq)]
188pub struct Attr { 230pub struct Attr {
189 pub(crate) path: ModPath, 231 pub(crate) path: ModPath,
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs
index 1b6ff6d21..cf04c3de0 100644
--- a/crates/ide/src/hover.rs
+++ b/crates/ide/src/hover.rs
@@ -3357,4 +3357,66 @@ impl Foo {
3357 "#]], 3357 "#]],
3358 ); 3358 );
3359 } 3359 }
3360
3361 #[test]
3362 fn hover_doc_outer_inner() {
3363 check(
3364 r#"
3365/// Be quick;
3366mod Foo<|> {
3367 //! time is mana
3368
3369 /// This comment belongs to the function
3370 fn foo() {}
3371}
3372"#,
3373 expect![[r#"
3374 *Foo*
3375
3376 ```rust
3377 test
3378 ```
3379
3380 ```rust
3381 mod Foo
3382 ```
3383
3384 ---
3385
3386 Be quick;
3387 time is mana
3388 "#]],
3389 );
3390 }
3391
3392 #[test]
3393 fn hover_doc_outer_inner_attribue() {
3394 check(
3395 r#"
3396#[doc = "Be quick;"]
3397mod Foo<|> {
3398 #![doc = "time is mana"]
3399
3400 #[doc = "This comment belongs to the function"]
3401 fn foo() {}
3402}
3403"#,
3404 expect![[r#"
3405 *Foo*
3406
3407 ```rust
3408 test
3409 ```
3410
3411 ```rust
3412 mod Foo
3413 ```
3414
3415 ---
3416
3417 Be quick;
3418 time is mana
3419 "#]],
3420 );
3421 }
3360} 3422}
diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs
index a10b14778..52b7285dd 100644
--- a/crates/syntax/src/ast/token_ext.rs
+++ b/crates/syntax/src/ast/token_ext.rs
@@ -17,6 +17,14 @@ impl ast::Comment {
17 CommentKind::from_text(self.text()) 17 CommentKind::from_text(self.text())
18 } 18 }
19 19
20 pub fn is_inner(&self) -> bool {
21 self.kind().doc == Some(CommentPlacement::Inner)
22 }
23
24 pub fn is_outer(&self) -> bool {
25 self.kind().doc == Some(CommentPlacement::Outer)
26 }
27
20 pub fn prefix(&self) -> &'static str { 28 pub fn prefix(&self) -> &'static str {
21 let &(prefix, _kind) = CommentKind::BY_PREFIX 29 let &(prefix, _kind) = CommentKind::BY_PREFIX
22 .iter() 30 .iter()