diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs index 00c60ebf341..74a45f52f31 100644 --- a/crates/ra_syntax/src/ast.rs +++ b/crates/ra_syntax/src/ast.rs @@ -115,19 +115,30 @@ fn doc_comments(&self) -> AstChildren { } /// Returns the textual content of a doc comment block as a single string. - /// That is, strips leading `///` and joins lines + /// That is, strips leading `///` (+ optional 1 character of whitespace) + /// and joins lines. fn doc_comment_text(&self) -> std::string::String { self.doc_comments() .filter(|comment| comment.is_doc_comment()) .map(|comment| { - let prefix = comment.prefix(); - let trimmed = comment - .text() - .as_str() - .trim() - .trim_start_matches(prefix) - .trim_start(); - trimmed.to_owned() + let prefix_len = comment.prefix().len(); + + // Strip leading and trailing whitespace + let line = comment.text().as_str().trim(); + + // Determine if the prefix or prefix + 1 char is stripped + let pos = if line + .chars() + .nth(prefix_len) + .map(|c| c.is_whitespace()) + .unwrap_or(false) + { + prefix_len + 1 + } else { + prefix_len + }; + + line[pos..].to_owned() }) .join("\n") } @@ -701,3 +712,23 @@ mod foo {} let module = file.syntax().descendants().find_map(Module::cast).unwrap(); assert_eq!("doc", module.doc_comment_text()); } + +#[test] +fn test_doc_comment_preserves_indents() { + let file = SourceFile::parse( + r#" + /// doc1 + /// ``` + /// fn foo() { + /// // ... + /// } + /// ``` + mod foo {} + "#, + ); + let module = file.syntax().descendants().find_map(Module::cast).unwrap(); + assert_eq!( + "doc1\n```\nfn foo() {\n // ...\n}\n```", + module.doc_comment_text() + ); +}