diff options
Diffstat (limited to 'crates/ra_syntax')
-rw-r--r-- | crates/ra_syntax/src/ast.rs | 93 |
1 files changed, 78 insertions, 15 deletions
diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs index 00c60ebf3..3d22a88f3 100644 --- a/crates/ra_syntax/src/ast.rs +++ b/crates/ra_syntax/src/ast.rs | |||
@@ -115,21 +115,38 @@ pub trait DocCommentsOwner: AstNode { | |||
115 | } | 115 | } |
116 | 116 | ||
117 | /// Returns the textual content of a doc comment block as a single string. | 117 | /// Returns the textual content of a doc comment block as a single string. |
118 | /// That is, strips leading `///` and joins lines | 118 | /// That is, strips leading `///` (+ optional 1 character of whitespace) |
119 | fn doc_comment_text(&self) -> std::string::String { | 119 | /// and joins lines. |
120 | self.doc_comments() | 120 | fn doc_comment_text(&self) -> Option<std::string::String> { |
121 | let docs = self | ||
122 | .doc_comments() | ||
121 | .filter(|comment| comment.is_doc_comment()) | 123 | .filter(|comment| comment.is_doc_comment()) |
122 | .map(|comment| { | 124 | .map(|comment| { |
123 | let prefix = comment.prefix(); | 125 | let prefix_len = comment.prefix().len(); |
124 | let trimmed = comment | 126 | |
125 | .text() | 127 | let line = comment.text().as_str(); |
126 | .as_str() | 128 | |
127 | .trim() | 129 | // Determine if the prefix or prefix + 1 char is stripped |
128 | .trim_start_matches(prefix) | 130 | let pos = if line |
129 | .trim_start(); | 131 | .chars() |
130 | trimmed.to_owned() | 132 | .nth(prefix_len) |
133 | .map(|c| c.is_whitespace()) | ||
134 | .unwrap_or(false) | ||
135 | { | ||
136 | prefix_len + 1 | ||
137 | } else { | ||
138 | prefix_len | ||
139 | }; | ||
140 | |||
141 | line[pos..].to_owned() | ||
131 | }) | 142 | }) |
132 | .join("\n") | 143 | .join("\n"); |
144 | |||
145 | if docs.is_empty() { | ||
146 | None | ||
147 | } else { | ||
148 | Some(docs) | ||
149 | } | ||
133 | } | 150 | } |
134 | } | 151 | } |
135 | 152 | ||
@@ -285,13 +302,27 @@ impl LetStmt { | |||
285 | } | 302 | } |
286 | } | 303 | } |
287 | 304 | ||
305 | #[derive(Debug, Clone, PartialEq, Eq)] | ||
306 | pub enum ElseBranchFlavor<'a> { | ||
307 | Block(&'a Block), | ||
308 | IfExpr(&'a IfExpr), | ||
309 | } | ||
310 | |||
288 | impl IfExpr { | 311 | impl IfExpr { |
289 | pub fn then_branch(&self) -> Option<&Block> { | 312 | pub fn then_branch(&self) -> Option<&Block> { |
290 | self.blocks().nth(0) | 313 | self.blocks().nth(0) |
291 | } | 314 | } |
292 | pub fn else_branch(&self) -> Option<&Block> { | 315 | pub fn else_branch(&self) -> Option<ElseBranchFlavor> { |
293 | self.blocks().nth(1) | 316 | let res = match self.blocks().nth(1) { |
317 | Some(block) => ElseBranchFlavor::Block(block), | ||
318 | None => { | ||
319 | let elif: &IfExpr = child_opt(self)?; | ||
320 | ElseBranchFlavor::IfExpr(elif) | ||
321 | } | ||
322 | }; | ||
323 | Some(res) | ||
294 | } | 324 | } |
325 | |||
295 | fn blocks(&self) -> AstChildren<Block> { | 326 | fn blocks(&self) -> AstChildren<Block> { |
296 | children(self) | 327 | children(self) |
297 | } | 328 | } |
@@ -690,6 +721,18 @@ impl BindPat { | |||
690 | } | 721 | } |
691 | 722 | ||
692 | #[test] | 723 | #[test] |
724 | fn test_doc_comment_none() { | ||
725 | let file = SourceFile::parse( | ||
726 | r#" | ||
727 | // non-doc | ||
728 | mod foo {} | ||
729 | "#, | ||
730 | ); | ||
731 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); | ||
732 | assert!(module.doc_comment_text().is_none()); | ||
733 | } | ||
734 | |||
735 | #[test] | ||
693 | fn test_doc_comment_of_items() { | 736 | fn test_doc_comment_of_items() { |
694 | let file = SourceFile::parse( | 737 | let file = SourceFile::parse( |
695 | r#" | 738 | r#" |
@@ -699,5 +742,25 @@ fn test_doc_comment_of_items() { | |||
699 | "#, | 742 | "#, |
700 | ); | 743 | ); |
701 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); | 744 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); |
702 | assert_eq!("doc", module.doc_comment_text()); | 745 | assert_eq!("doc", module.doc_comment_text().unwrap()); |
746 | } | ||
747 | |||
748 | #[test] | ||
749 | fn test_doc_comment_preserves_indents() { | ||
750 | let file = SourceFile::parse( | ||
751 | r#" | ||
752 | /// doc1 | ||
753 | /// ``` | ||
754 | /// fn foo() { | ||
755 | /// // ... | ||
756 | /// } | ||
757 | /// ``` | ||
758 | mod foo {} | ||
759 | "#, | ||
760 | ); | ||
761 | let module = file.syntax().descendants().find_map(Module::cast).unwrap(); | ||
762 | assert_eq!( | ||
763 | "doc1\n```\nfn foo() {\n // ...\n}\n```", | ||
764 | module.doc_comment_text().unwrap() | ||
765 | ); | ||
703 | } | 766 | } |