aboutsummaryrefslogtreecommitdiff
path: root/crates/ra_syntax
diff options
context:
space:
mode:
Diffstat (limited to 'crates/ra_syntax')
-rw-r--r--crates/ra_syntax/src/ast.rs93
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)]
306pub enum ElseBranchFlavor<'a> {
307 Block(&'a Block),
308 IfExpr(&'a IfExpr),
309}
310
288impl IfExpr { 311impl 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]
724fn 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]
693fn test_doc_comment_of_items() { 736fn 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]
749fn 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}