From 8c08b6825ec961af8bc485f729eb68f69a56e7aa Mon Sep 17 00:00:00 2001 From: Jeremy Kolb Date: Fri, 25 Jan 2019 21:31:31 -0500 Subject: [PATCH 1/3] Preserve indentation in doc comments --- crates/ra_syntax/src/ast.rs | 49 ++++++++++++++++++++++++++++++------- 1 file changed, 40 insertions(+), 9 deletions(-) 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 @@ pub trait DocCommentsOwner: AstNode { } /// 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 @@ fn test_doc_comment_of_items() { 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() + ); +} From e9e0ea03986294f58c371d0329926709ae2f164c Mon Sep 17 00:00:00 2001 From: Jeremy Kolb Date: Sat, 26 Jan 2019 09:55:30 -0500 Subject: [PATCH 2/3] Do not unconditionally trim comments --- crates/ra_syntax/src/ast.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs index 74a45f52f31..cdfb9c52364 100644 --- a/crates/ra_syntax/src/ast.rs +++ b/crates/ra_syntax/src/ast.rs @@ -123,8 +123,7 @@ pub trait DocCommentsOwner: AstNode { .map(|comment| { let prefix_len = comment.prefix().len(); - // Strip leading and trailing whitespace - let line = comment.text().as_str().trim(); + let line = comment.text().as_str(); // Determine if the prefix or prefix + 1 char is stripped let pos = if line From a89206795140209b816eb8e91fa3f1e1d6509269 Mon Sep 17 00:00:00 2001 From: Jeremy Kolb Date: Sat, 26 Jan 2019 10:35:23 -0500 Subject: [PATCH 3/3] Make doc comments optional --- crates/ra_hir/src/docs.rs | 7 +------ crates/ra_ide_api/src/call_info.rs | 3 +-- crates/ra_ide_api/src/hover.rs | 7 +------ crates/ra_syntax/src/ast.rs | 29 ++++++++++++++++++++++++----- 4 files changed, 27 insertions(+), 19 deletions(-) diff --git a/crates/ra_hir/src/docs.rs b/crates/ra_hir/src/docs.rs index b1b47af9e63..5db72c08a3e 100644 --- a/crates/ra_hir/src/docs.rs +++ b/crates/ra_hir/src/docs.rs @@ -27,10 +27,5 @@ pub trait Docs { } pub(crate) fn docs_from_ast(node: &impl ast::DocCommentsOwner) -> Option { - let comments = node.doc_comment_text(); - if comments.is_empty() { - None - } else { - Some(Documentation::new(&comments)) - } + node.doc_comment_text().map(|it| Documentation::new(&it)) } diff --git a/crates/ra_ide_api/src/call_info.rs b/crates/ra_ide_api/src/call_info.rs index 3267fff9617..7554c4aeeda 100644 --- a/crates/ra_ide_api/src/call_info.rs +++ b/crates/ra_ide_api/src/call_info.rs @@ -120,8 +120,7 @@ impl CallInfo { }; let mut doc = None; - let docs = node.doc_comment_text(); - if !docs.is_empty() { + if let Some(docs) = node.doc_comment_text() { // Massage markdown let mut processed_lines = Vec::new(); let mut in_code_block = false; diff --git a/crates/ra_ide_api/src/hover.rs b/crates/ra_ide_api/src/hover.rs index ff9ae2d9c87..f993a461c35 100644 --- a/crates/ra_ide_api/src/hover.rs +++ b/crates/ra_ide_api/src/hover.rs @@ -100,12 +100,7 @@ impl NavigationTarget { fn docs(&self, db: &RootDatabase) -> Option { let node = self.node(db)?; fn doc_comments(node: &N) -> Option { - let comments = node.doc_comment_text(); - if comments.is_empty() { - None - } else { - Some(comments) - } + node.doc_comment_text() } visitor() diff --git a/crates/ra_syntax/src/ast.rs b/crates/ra_syntax/src/ast.rs index cdfb9c52364..d59890d9569 100644 --- a/crates/ra_syntax/src/ast.rs +++ b/crates/ra_syntax/src/ast.rs @@ -117,8 +117,9 @@ pub trait DocCommentsOwner: AstNode { /// Returns the textual content of a doc comment block as a single string. /// That is, strips leading `///` (+ optional 1 character of whitespace) /// and joins lines. - fn doc_comment_text(&self) -> std::string::String { - self.doc_comments() + fn doc_comment_text(&self) -> Option { + let docs = self + .doc_comments() .filter(|comment| comment.is_doc_comment()) .map(|comment| { let prefix_len = comment.prefix().len(); @@ -139,7 +140,13 @@ pub trait DocCommentsOwner: AstNode { line[pos..].to_owned() }) - .join("\n") + .join("\n"); + + if docs.is_empty() { + None + } else { + Some(docs) + } } } @@ -699,6 +706,18 @@ impl BindPat { } } +#[test] +fn test_doc_comment_none() { + let file = SourceFile::parse( + r#" + // non-doc + mod foo {} + "#, + ); + let module = file.syntax().descendants().find_map(Module::cast).unwrap(); + assert!(module.doc_comment_text().is_none()); +} + #[test] fn test_doc_comment_of_items() { let file = SourceFile::parse( @@ -709,7 +728,7 @@ fn test_doc_comment_of_items() { "#, ); let module = file.syntax().descendants().find_map(Module::cast).unwrap(); - assert_eq!("doc", module.doc_comment_text()); + assert_eq!("doc", module.doc_comment_text().unwrap()); } #[test] @@ -728,6 +747,6 @@ fn test_doc_comment_preserves_indents() { let module = file.syntax().descendants().find_map(Module::cast).unwrap(); assert_eq!( "doc1\n```\nfn foo() {\n // ...\n}\n```", - module.doc_comment_text() + module.doc_comment_text().unwrap() ); }