diff options
-rw-r--r-- | crates/hir_def/src/attr.rs | 83 | ||||
-rw-r--r-- | crates/ide/src/goto_definition.rs | 8 | ||||
-rw-r--r-- | crates/ide/src/hover.rs | 34 | ||||
-rw-r--r-- | crates/ide/src/runnables.rs | 48 | ||||
-rw-r--r-- | crates/ide_completion/src/lib.rs | 2 | ||||
-rw-r--r-- | crates/syntax/src/ast.rs | 29 | ||||
-rw-r--r-- | crates/syntax/src/ast/token_ext.rs | 19 | ||||
-rw-r--r-- | crates/syntax/src/ast/traits.rs | 16 |
8 files changed, 158 insertions, 81 deletions
diff --git a/crates/hir_def/src/attr.rs b/crates/hir_def/src/attr.rs index 7ba53ee5c..aeeb2c5cf 100644 --- a/crates/hir_def/src/attr.rs +++ b/crates/hir_def/src/attr.rs | |||
@@ -77,33 +77,19 @@ impl RawAttrs { | |||
77 | pub(crate) const EMPTY: Self = Self { entries: None }; | 77 | pub(crate) const EMPTY: Self = Self { entries: None }; |
78 | 78 | ||
79 | pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Self { | 79 | pub(crate) fn new(owner: &dyn AttrsOwner, hygiene: &Hygiene) -> Self { |
80 | let attrs: Vec<_> = collect_attrs(owner).collect(); | 80 | let entries = collect_attrs(owner) |
81 | let entries = if attrs.is_empty() { | 81 | .enumerate() |
82 | // Avoid heap allocation | 82 | .flat_map(|(i, attr)| match attr { |
83 | None | 83 | Either::Left(attr) => Attr::from_src(attr, hygiene, i as u32), |
84 | } else { | 84 | Either::Right(comment) => comment.doc_comment().map(|doc| Attr { |
85 | Some( | 85 | index: i as u32, |
86 | attrs | 86 | input: Some(AttrInput::Literal(SmolStr::new(doc))), |
87 | .into_iter() | 87 | path: ModPath::from(hir_expand::name!(doc)), |
88 | .enumerate() | 88 | }), |
89 | .flat_map(|(i, attr)| match attr { | 89 | }) |
90 | Either::Left(attr) => Attr::from_src(attr, hygiene).map(|attr| (i, attr)), | 90 | .collect::<Arc<_>>(); |
91 | Either::Right(comment) => comment.doc_comment().map(|doc| { | 91 | |
92 | ( | 92 | Self { entries: if entries.is_empty() { None } else { Some(entries) } } |
93 | i, | ||
94 | Attr { | ||
95 | index: 0, | ||
96 | input: Some(AttrInput::Literal(SmolStr::new(doc))), | ||
97 | path: ModPath::from(hir_expand::name!(doc)), | ||
98 | }, | ||
99 | ) | ||
100 | }), | ||
101 | }) | ||
102 | .map(|(i, attr)| Attr { index: i as u32, ..attr }) | ||
103 | .collect(), | ||
104 | ) | ||
105 | }; | ||
106 | Self { entries } | ||
107 | } | 93 | } |
108 | 94 | ||
109 | fn from_attrs_owner(db: &dyn DefDatabase, owner: InFile<&dyn AttrsOwner>) -> Self { | 95 | fn from_attrs_owner(db: &dyn DefDatabase, owner: InFile<&dyn AttrsOwner>) -> Self { |
@@ -162,7 +148,7 @@ impl RawAttrs { | |||
162 | let attr = ast::Attr::parse(&format!("#[{}]", tree)).ok()?; | 148 | let attr = ast::Attr::parse(&format!("#[{}]", tree)).ok()?; |
163 | // FIXME hygiene | 149 | // FIXME hygiene |
164 | let hygiene = Hygiene::new_unhygienic(); | 150 | let hygiene = Hygiene::new_unhygienic(); |
165 | Attr::from_src(attr, &hygiene).map(|attr| Attr { index, ..attr }) | 151 | Attr::from_src(attr, &hygiene, index) |
166 | }); | 152 | }); |
167 | 153 | ||
168 | let cfg_options = &crate_graph[krate].cfg_options; | 154 | let cfg_options = &crate_graph[krate].cfg_options; |
@@ -325,15 +311,36 @@ impl Attrs { | |||
325 | AttrInput::Literal(s) => Some(s), | 311 | AttrInput::Literal(s) => Some(s), |
326 | AttrInput::TokenTree(_) => None, | 312 | AttrInput::TokenTree(_) => None, |
327 | }); | 313 | }); |
328 | // FIXME: Replace `Itertools::intersperse` with `Iterator::intersperse[_with]` until the | 314 | let indent = docs |
329 | // libstd api gets stabilized (https://github.com/rust-lang/rust/issues/79524). | 315 | .clone() |
330 | let docs = Itertools::intersperse(docs, &SmolStr::new_inline("\n")) | 316 | .flat_map(|s| s.lines()) |
331 | .map(|it| it.as_str()) | 317 | .filter(|line| !line.chars().all(|c| c.is_whitespace())) |
332 | .collect::<String>(); | 318 | .map(|line| line.chars().take_while(|c| c.is_whitespace()).count()) |
333 | if docs.is_empty() { | 319 | .min() |
320 | .unwrap_or(0); | ||
321 | let mut buf = String::new(); | ||
322 | for doc in docs { | ||
323 | // str::lines doesn't yield anything for the empty string | ||
324 | if doc.is_empty() { | ||
325 | buf.push('\n'); | ||
326 | } else { | ||
327 | buf.extend(Itertools::intersperse( | ||
328 | doc.lines().map(|line| { | ||
329 | line.char_indices() | ||
330 | .nth(indent) | ||
331 | .map_or(line, |(offset, _)| &line[offset..]) | ||
332 | .trim_end() | ||
333 | }), | ||
334 | "\n", | ||
335 | )); | ||
336 | } | ||
337 | buf.push('\n'); | ||
338 | } | ||
339 | buf.pop(); | ||
340 | if buf.is_empty() { | ||
334 | None | 341 | None |
335 | } else { | 342 | } else { |
336 | Some(Documentation(docs)) | 343 | Some(Documentation(buf)) |
337 | } | 344 | } |
338 | } | 345 | } |
339 | } | 346 | } |
@@ -407,7 +414,7 @@ pub enum AttrInput { | |||
407 | } | 414 | } |
408 | 415 | ||
409 | impl Attr { | 416 | impl Attr { |
410 | fn from_src(ast: ast::Attr, hygiene: &Hygiene) -> Option<Attr> { | 417 | fn from_src(ast: ast::Attr, hygiene: &Hygiene, index: u32) -> Option<Attr> { |
411 | let path = ModPath::from_src(ast.path()?, hygiene)?; | 418 | let path = ModPath::from_src(ast.path()?, hygiene)?; |
412 | let input = if let Some(lit) = ast.literal() { | 419 | let input = if let Some(lit) = ast.literal() { |
413 | let value = match lit.kind() { | 420 | let value = match lit.kind() { |
@@ -420,7 +427,7 @@ impl Attr { | |||
420 | } else { | 427 | } else { |
421 | None | 428 | None |
422 | }; | 429 | }; |
423 | Some(Attr { index: 0, path, input }) | 430 | Some(Attr { index, path, input }) |
424 | } | 431 | } |
425 | 432 | ||
426 | /// Maps this lowered `Attr` back to its original syntax node. | 433 | /// Maps this lowered `Attr` back to its original syntax node. |
@@ -508,7 +515,7 @@ impl<'a> AttrQuery<'a> { | |||
508 | self.attrs().next().is_some() | 515 | self.attrs().next().is_some() |
509 | } | 516 | } |
510 | 517 | ||
511 | pub fn attrs(self) -> impl Iterator<Item = &'a Attr> { | 518 | pub fn attrs(self) -> impl Iterator<Item = &'a Attr> + Clone { |
512 | let key = self.key; | 519 | let key = self.key; |
513 | self.attrs | 520 | self.attrs |
514 | .iter() | 521 | .iter() |
diff --git a/crates/ide/src/goto_definition.rs b/crates/ide/src/goto_definition.rs index b71f4917c..5072ecea0 100644 --- a/crates/ide/src/goto_definition.rs +++ b/crates/ide/src/goto_definition.rs | |||
@@ -95,12 +95,10 @@ fn extract_positioned_link_from_comment( | |||
95 | let comment_range = comment.syntax().text_range(); | 95 | let comment_range = comment.syntax().text_range(); |
96 | let doc_comment = comment.doc_comment()?; | 96 | let doc_comment = comment.doc_comment()?; |
97 | let def_links = extract_definitions_from_markdown(doc_comment); | 97 | let def_links = extract_definitions_from_markdown(doc_comment); |
98 | let start = comment_range.start() + TextSize::from(comment.prefix().len() as u32); | ||
98 | let (def_link, ns, _) = def_links.iter().min_by_key(|(_, _, def_link_range)| { | 99 | let (def_link, ns, _) = def_links.iter().min_by_key(|(_, _, def_link_range)| { |
99 | let matched_position = comment_range.start() + TextSize::from(def_link_range.start as u32); | 100 | let matched_position = start + TextSize::from(def_link_range.start as u32); |
100 | match position.offset.checked_sub(matched_position) { | 101 | position.offset.checked_sub(matched_position).unwrap_or_else(|| comment_range.end()) |
101 | Some(distance) => distance, | ||
102 | None => comment_range.end(), | ||
103 | } | ||
104 | })?; | 102 | })?; |
105 | Some((def_link.to_string(), *ns)) | 103 | Some((def_link.to_string(), *ns)) |
106 | } | 104 | } |
diff --git a/crates/ide/src/hover.rs b/crates/ide/src/hover.rs index 325014622..cc2b79124 100644 --- a/crates/ide/src/hover.rs +++ b/crates/ide/src/hover.rs | |||
@@ -3424,6 +3424,40 @@ mod Foo$0 { | |||
3424 | } | 3424 | } |
3425 | 3425 | ||
3426 | #[test] | 3426 | #[test] |
3427 | fn hover_doc_block_style_indentend() { | ||
3428 | check( | ||
3429 | r#" | ||
3430 | /** | ||
3431 | foo | ||
3432 | ```rust | ||
3433 | let x = 3; | ||
3434 | ``` | ||
3435 | */ | ||
3436 | fn foo$0() {} | ||
3437 | "#, | ||
3438 | expect![[r#" | ||
3439 | *foo* | ||
3440 | |||
3441 | ```rust | ||
3442 | test | ||
3443 | ``` | ||
3444 | |||
3445 | ```rust | ||
3446 | fn foo() | ||
3447 | ``` | ||
3448 | |||
3449 | --- | ||
3450 | |||
3451 | foo | ||
3452 | |||
3453 | ```rust | ||
3454 | let x = 3; | ||
3455 | ``` | ||
3456 | "#]], | ||
3457 | ); | ||
3458 | } | ||
3459 | |||
3460 | #[test] | ||
3427 | fn hover_comments_dont_highlight_parent() { | 3461 | fn hover_comments_dont_highlight_parent() { |
3428 | check_hover_no_result( | 3462 | check_hover_no_result( |
3429 | r#" | 3463 | r#" |
diff --git a/crates/ide/src/runnables.rs b/crates/ide/src/runnables.rs index 397e2126b..bea020b06 100644 --- a/crates/ide/src/runnables.rs +++ b/crates/ide/src/runnables.rs | |||
@@ -576,6 +576,20 @@ fn should_have_runnable_1() {} | |||
576 | /// ``` | 576 | /// ``` |
577 | fn should_have_runnable_2() {} | 577 | fn should_have_runnable_2() {} |
578 | 578 | ||
579 | /** | ||
580 | ```rust | ||
581 | let z = 55; | ||
582 | ``` | ||
583 | */ | ||
584 | fn should_have_no_runnable_3() {} | ||
585 | |||
586 | /** | ||
587 | ```rust | ||
588 | let z = 55; | ||
589 | ``` | ||
590 | */ | ||
591 | fn should_have_no_runnable_4() {} | ||
592 | |||
579 | /// ```no_run | 593 | /// ```no_run |
580 | /// let z = 55; | 594 | /// let z = 55; |
581 | /// ``` | 595 | /// ``` |
@@ -616,7 +630,7 @@ fn should_have_no_runnable_6() {} | |||
616 | struct StructWithRunnable(String); | 630 | struct StructWithRunnable(String); |
617 | 631 | ||
618 | "#, | 632 | "#, |
619 | &[&BIN, &DOCTEST, &DOCTEST, &DOCTEST, &DOCTEST], | 633 | &[&BIN, &DOCTEST, &DOCTEST, &DOCTEST, &DOCTEST, &DOCTEST, &DOCTEST], |
620 | expect![[r#" | 634 | expect![[r#" |
621 | [ | 635 | [ |
622 | Runnable { | 636 | Runnable { |
@@ -682,7 +696,37 @@ struct StructWithRunnable(String); | |||
682 | file_id: FileId( | 696 | file_id: FileId( |
683 | 0, | 697 | 0, |
684 | ), | 698 | ), |
685 | full_range: 756..821, | 699 | full_range: 256..320, |
700 | name: "should_have_no_runnable_3", | ||
701 | }, | ||
702 | kind: DocTest { | ||
703 | test_id: Path( | ||
704 | "should_have_no_runnable_3", | ||
705 | ), | ||
706 | }, | ||
707 | cfg: None, | ||
708 | }, | ||
709 | Runnable { | ||
710 | nav: NavigationTarget { | ||
711 | file_id: FileId( | ||
712 | 0, | ||
713 | ), | ||
714 | full_range: 322..398, | ||
715 | name: "should_have_no_runnable_4", | ||
716 | }, | ||
717 | kind: DocTest { | ||
718 | test_id: Path( | ||
719 | "should_have_no_runnable_4", | ||
720 | ), | ||
721 | }, | ||
722 | cfg: None, | ||
723 | }, | ||
724 | Runnable { | ||
725 | nav: NavigationTarget { | ||
726 | file_id: FileId( | ||
727 | 0, | ||
728 | ), | ||
729 | full_range: 900..965, | ||
686 | name: "StructWithRunnable", | 730 | name: "StructWithRunnable", |
687 | }, | 731 | }, |
688 | kind: DocTest { | 732 | kind: DocTest { |
diff --git a/crates/ide_completion/src/lib.rs b/crates/ide_completion/src/lib.rs index 5b7ad38d5..d9ea7b7ea 100644 --- a/crates/ide_completion/src/lib.rs +++ b/crates/ide_completion/src/lib.rs | |||
@@ -255,7 +255,7 @@ fn foo() { | |||
255 | bar.fo$0; | 255 | bar.fo$0; |
256 | } | 256 | } |
257 | "#, | 257 | "#, |
258 | DetailAndDocumentation { detail: "fn(&self)", documentation: " Do the foo" }, | 258 | DetailAndDocumentation { detail: "fn(&self)", documentation: "Do the foo" }, |
259 | ); | 259 | ); |
260 | } | 260 | } |
261 | 261 | ||
diff --git a/crates/syntax/src/ast.rs b/crates/syntax/src/ast.rs index 19261686c..38e0b04ef 100644 --- a/crates/syntax/src/ast.rs +++ b/crates/syntax/src/ast.rs | |||
@@ -118,7 +118,7 @@ fn test_doc_comment_none() { | |||
118 | .ok() | 118 | .ok() |
119 | .unwrap(); | 119 | .unwrap(); |
120 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); | 120 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); |
121 | assert!(module.doc_comment_text().is_none()); | 121 | assert!(module.doc_comments().doc_comment_text().is_none()); |
122 | } | 122 | } |
123 | 123 | ||
124 | #[test] | 124 | #[test] |
@@ -133,7 +133,7 @@ fn test_outer_doc_comment_of_items() { | |||
133 | .ok() | 133 | .ok() |
134 | .unwrap(); | 134 | .unwrap(); |
135 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); | 135 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); |
136 | assert_eq!("doc", module.doc_comment_text().unwrap()); | 136 | assert_eq!(" doc", module.doc_comments().doc_comment_text().unwrap()); |
137 | } | 137 | } |
138 | 138 | ||
139 | #[test] | 139 | #[test] |
@@ -148,7 +148,7 @@ fn test_inner_doc_comment_of_items() { | |||
148 | .ok() | 148 | .ok() |
149 | .unwrap(); | 149 | .unwrap(); |
150 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); | 150 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); |
151 | assert!(module.doc_comment_text().is_none()); | 151 | assert!(module.doc_comments().doc_comment_text().is_none()); |
152 | } | 152 | } |
153 | 153 | ||
154 | #[test] | 154 | #[test] |
@@ -162,7 +162,7 @@ fn test_doc_comment_of_statics() { | |||
162 | .ok() | 162 | .ok() |
163 | .unwrap(); | 163 | .unwrap(); |
164 | let st = file.syntax().descendants().find_map(Static::cast).unwrap(); | 164 | let st = file.syntax().descendants().find_map(Static::cast).unwrap(); |
165 | assert_eq!("Number of levels", st.doc_comment_text().unwrap()); | 165 | assert_eq!(" Number of levels", st.doc_comments().doc_comment_text().unwrap()); |
166 | } | 166 | } |
167 | 167 | ||
168 | #[test] | 168 | #[test] |
@@ -181,7 +181,10 @@ fn test_doc_comment_preserves_indents() { | |||
181 | .ok() | 181 | .ok() |
182 | .unwrap(); | 182 | .unwrap(); |
183 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); | 183 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); |
184 | assert_eq!("doc1\n```\nfn foo() {\n // ...\n}\n```", module.doc_comment_text().unwrap()); | 184 | assert_eq!( |
185 | " doc1\n ```\n fn foo() {\n // ...\n }\n ```", | ||
186 | module.doc_comments().doc_comment_text().unwrap() | ||
187 | ); | ||
185 | } | 188 | } |
186 | 189 | ||
187 | #[test] | 190 | #[test] |
@@ -198,7 +201,7 @@ fn test_doc_comment_preserves_newlines() { | |||
198 | .ok() | 201 | .ok() |
199 | .unwrap(); | 202 | .unwrap(); |
200 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); | 203 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); |
201 | assert_eq!("this\nis\nmod\nfoo", module.doc_comment_text().unwrap()); | 204 | assert_eq!(" this\n is\n mod\n foo", module.doc_comments().doc_comment_text().unwrap()); |
202 | } | 205 | } |
203 | 206 | ||
204 | #[test] | 207 | #[test] |
@@ -212,7 +215,7 @@ fn test_doc_comment_single_line_block_strips_suffix() { | |||
212 | .ok() | 215 | .ok() |
213 | .unwrap(); | 216 | .unwrap(); |
214 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); | 217 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); |
215 | assert_eq!("this is mod foo", module.doc_comment_text().unwrap()); | 218 | assert_eq!(" this is mod foo", module.doc_comments().doc_comment_text().unwrap()); |
216 | } | 219 | } |
217 | 220 | ||
218 | #[test] | 221 | #[test] |
@@ -226,7 +229,7 @@ fn test_doc_comment_single_line_block_strips_suffix_whitespace() { | |||
226 | .ok() | 229 | .ok() |
227 | .unwrap(); | 230 | .unwrap(); |
228 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); | 231 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); |
229 | assert_eq!("this is mod foo ", module.doc_comment_text().unwrap()); | 232 | assert_eq!(" this is mod foo ", module.doc_comments().doc_comment_text().unwrap()); |
230 | } | 233 | } |
231 | 234 | ||
232 | #[test] | 235 | #[test] |
@@ -245,8 +248,8 @@ fn test_doc_comment_multi_line_block_strips_suffix() { | |||
245 | .unwrap(); | 248 | .unwrap(); |
246 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); | 249 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); |
247 | assert_eq!( | 250 | assert_eq!( |
248 | " this\n is\n mod foo\n ", | 251 | "\n this\n is\n mod foo\n ", |
249 | module.doc_comment_text().unwrap() | 252 | module.doc_comments().doc_comment_text().unwrap() |
250 | ); | 253 | ); |
251 | } | 254 | } |
252 | 255 | ||
@@ -259,8 +262,8 @@ fn test_comments_preserve_trailing_whitespace() { | |||
259 | .unwrap(); | 262 | .unwrap(); |
260 | let def = file.syntax().descendants().find_map(Struct::cast).unwrap(); | 263 | let def = file.syntax().descendants().find_map(Struct::cast).unwrap(); |
261 | assert_eq!( | 264 | assert_eq!( |
262 | "Representation of a Realm. \nIn the specification these are called Realm Records.", | 265 | " Representation of a Realm. \n In the specification these are called Realm Records.", |
263 | def.doc_comment_text().unwrap() | 266 | def.doc_comments().doc_comment_text().unwrap() |
264 | ); | 267 | ); |
265 | } | 268 | } |
266 | 269 | ||
@@ -276,7 +279,7 @@ fn test_four_slash_line_comment() { | |||
276 | .ok() | 279 | .ok() |
277 | .unwrap(); | 280 | .unwrap(); |
278 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); | 281 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); |
279 | assert_eq!("doc comment", module.doc_comment_text().unwrap()); | 282 | assert_eq!(" doc comment", module.doc_comments().doc_comment_text().unwrap()); |
280 | } | 283 | } |
281 | 284 | ||
282 | #[test] | 285 | #[test] |
diff --git a/crates/syntax/src/ast/token_ext.rs b/crates/syntax/src/ast/token_ext.rs index 977eb8181..6c242d126 100644 --- a/crates/syntax/src/ast/token_ext.rs +++ b/crates/syntax/src/ast/token_ext.rs | |||
@@ -33,23 +33,20 @@ impl ast::Comment { | |||
33 | prefix | 33 | prefix |
34 | } | 34 | } |
35 | 35 | ||
36 | /// Returns the textual content of a doc comment block as a single string. | 36 | /// Returns the textual content of a doc comment node as a single string with prefix and suffix |
37 | /// That is, strips leading `///` (+ optional 1 character of whitespace), | 37 | /// removed. |
38 | /// trailing `*/`, trailing whitespace and then joins the lines. | ||
39 | pub fn doc_comment(&self) -> Option<&str> { | 38 | pub fn doc_comment(&self) -> Option<&str> { |
40 | let kind = self.kind(); | 39 | let kind = self.kind(); |
41 | match kind { | 40 | match kind { |
42 | CommentKind { shape, doc: Some(_) } => { | 41 | CommentKind { shape, doc: Some(_) } => { |
43 | let prefix = kind.prefix(); | 42 | let prefix = kind.prefix(); |
44 | let text = &self.text()[prefix.len()..]; | 43 | let text = &self.text()[prefix.len()..]; |
45 | let ws = text.chars().next().filter(|c| c.is_whitespace()); | 44 | let text = if shape == CommentShape::Block { |
46 | let text = ws.map_or(text, |ws| &text[ws.len_utf8()..]); | 45 | text.strip_suffix("*/").unwrap_or(text) |
47 | match shape { | 46 | } else { |
48 | CommentShape::Block if text.ends_with("*/") => { | 47 | text |
49 | Some(&text[..text.len() - "*/".len()]) | 48 | }; |
50 | } | 49 | Some(text) |
51 | _ => Some(text), | ||
52 | } | ||
53 | } | 50 | } |
54 | _ => None, | 51 | _ => None, |
55 | } | 52 | } |
diff --git a/crates/syntax/src/ast/traits.rs b/crates/syntax/src/ast/traits.rs index 96d4cc997..ddd213637 100644 --- a/crates/syntax/src/ast/traits.rs +++ b/crates/syntax/src/ast/traits.rs | |||
@@ -1,8 +1,6 @@ | |||
1 | //! Various traits that are implemented by ast nodes. | 1 | //! Various traits that are implemented by ast nodes. |
2 | //! | 2 | //! |
3 | //! The implementations are usually trivial, and live in generated.rs | 3 | //! The implementations are usually trivial, and live in generated.rs |
4 | use itertools::Itertools; | ||
5 | |||
6 | use crate::{ | 4 | use crate::{ |
7 | ast::{self, support, AstChildren, AstNode, AstToken}, | 5 | ast::{self, support, AstChildren, AstNode, AstToken}, |
8 | syntax_node::SyntaxElementChildren, | 6 | syntax_node::SyntaxElementChildren, |
@@ -76,10 +74,6 @@ pub trait DocCommentsOwner: AttrsOwner { | |||
76 | fn doc_comments(&self) -> CommentIter { | 74 | fn doc_comments(&self) -> CommentIter { |
77 | CommentIter { iter: self.syntax().children_with_tokens() } | 75 | CommentIter { iter: self.syntax().children_with_tokens() } |
78 | } | 76 | } |
79 | |||
80 | fn doc_comment_text(&self) -> Option<String> { | ||
81 | self.doc_comments().doc_comment_text() | ||
82 | } | ||
83 | } | 77 | } |
84 | 78 | ||
85 | impl CommentIter { | 79 | impl CommentIter { |
@@ -87,12 +81,12 @@ impl CommentIter { | |||
87 | CommentIter { iter: syntax_node.children_with_tokens() } | 81 | CommentIter { iter: syntax_node.children_with_tokens() } |
88 | } | 82 | } |
89 | 83 | ||
90 | /// Returns the textual content of a doc comment block as a single string. | 84 | #[cfg(test)] |
91 | /// That is, strips leading `///` (+ optional 1 character of whitespace), | ||
92 | /// trailing `*/`, trailing whitespace and then joins the lines. | ||
93 | pub fn doc_comment_text(self) -> Option<String> { | 85 | pub fn doc_comment_text(self) -> Option<String> { |
94 | let docs = | 86 | let docs = itertools::Itertools::join( |
95 | self.filter_map(|comment| comment.doc_comment().map(ToOwned::to_owned)).join("\n"); | 87 | &mut self.filter_map(|comment| comment.doc_comment().map(ToOwned::to_owned)), |
88 | "\n", | ||
89 | ); | ||
96 | if docs.is_empty() { | 90 | if docs.is_empty() { |
97 | None | 91 | None |
98 | } else { | 92 | } else { |