diff --git a/compiler/noirc_frontend/src/hir/printer/mod.rs b/compiler/noirc_frontend/src/hir/printer/mod.rs index fb199643698..f550d34bbd9 100644 --- a/compiler/noirc_frontend/src/hir/printer/mod.rs +++ b/compiler/noirc_frontend/src/hir/printer/mod.rs @@ -170,6 +170,7 @@ impl<'context, 'string> ItemPrinter<'context, 'string> { self.push('\n'); self.write_indent(); } + self.push_str(" * "); self.push_str(line); } @@ -180,7 +181,7 @@ impl<'context, 'string> ItemPrinter<'context, 'string> { self.push_str("*/"); } else { - self.push_str("///"); + self.push_str("/// "); self.push_str(comment); } self.push('\n'); diff --git a/compiler/noirc_frontend/src/parser/parser/doc_comments.rs b/compiler/noirc_frontend/src/parser/parser/doc_comments.rs index 0d4a65495e7..7e17ada5b57 100644 --- a/compiler/noirc_frontend/src/parser/parser/doc_comments.rs +++ b/compiler/noirc_frontend/src/parser/parser/doc_comments.rs @@ -13,8 +13,8 @@ impl Parser<'_> { fn parse_inner_doc_comment(&mut self) -> Option { self.eat_kind(TokenKind::InnerDocComment).map(|token| match token.into_token() { - Token::LineComment(comment, Some(DocStyle::Inner)) - | Token::BlockComment(comment, Some(DocStyle::Inner)) => comment, + Token::LineComment(comment, Some(DocStyle::Inner)) => fix_line_comment(comment), + Token::BlockComment(comment, Some(DocStyle::Inner)) => fix_block_comment(comment), _ => unreachable!(), }) } @@ -27,8 +27,8 @@ impl Parser<'_> { /// OuterDocComment = outer_doc_comment pub(super) fn parse_outer_doc_comment(&mut self) -> Option { self.eat_kind(TokenKind::OuterDocComment).map(|token| match token.into_token() { - Token::LineComment(comment, Some(DocStyle::Outer)) - | Token::BlockComment(comment, Some(DocStyle::Outer)) => comment, + Token::LineComment(comment, Some(DocStyle::Outer)) => fix_line_comment(comment), + Token::BlockComment(comment, Some(DocStyle::Outer)) => fix_block_comment(comment), _ => unreachable!(), }) } @@ -53,6 +53,45 @@ impl Parser<'_> { } } +/// Strips leading ' ' from a line comment. +fn fix_line_comment(comment: String) -> String { + if let Some(comment) = comment.strip_prefix(' ') { comment.to_string() } else { comment } +} + +/// Strips leading '*' from a block comment if all non-empty lines have it. +fn fix_block_comment(comment: String) -> String { + let all_stars = comment.lines().enumerate().all(|(index, line)| { + if index == 0 || line.trim().is_empty() { + // The first line never has a star. Then we ignore empty lines. + true + } else { + line.trim_start().starts_with('*') + } + }); + + let mut fixed_comment = String::new(); + for (index, line) in comment.lines().enumerate() { + if index > 0 { + fixed_comment.push('\n'); + } + + if all_stars { + if let Some(line) = line.trim_start().strip_prefix("*") { + fixed_comment.push_str(line.strip_prefix(' ').unwrap_or(line)); + continue; + } + } + + if let Some(line) = line.strip_prefix(' ') { + fixed_comment.push_str(line); + continue; + } + + fixed_comment.push_str(line); + } + fixed_comment.trim().to_string() +} + #[cfg(test)] mod tests { use crate::parser::{Parser, parser::tests::expect_no_errors}; @@ -64,8 +103,28 @@ mod tests { let comments = parser.parse_inner_doc_comments(); expect_no_errors(&parser.errors); assert_eq!(comments.len(), 2); - assert_eq!(comments[0], " Hello"); - assert_eq!(comments[1], " World"); + assert_eq!(comments[0], "Hello"); + assert_eq!(comments[1], "World"); + } + + #[test] + fn parses_inner_block_doc_comments() { + let src = "/*! Hello\n * World\n *\n * !\n*/"; + let mut parser = Parser::for_str_with_dummy_file(src); + let comments = parser.parse_inner_doc_comments(); + expect_no_errors(&parser.errors); + assert_eq!(comments.len(), 1); + assert_eq!(comments[0], "Hello\nWorld\n\n!"); + } + + #[test] + fn parses_inner_block_doc_comments_with_indentation() { + let src = " /*! Hello\n * World\n *\n * !\n */"; + let mut parser = Parser::for_str_with_dummy_file(src); + let comments = parser.parse_inner_doc_comments(); + expect_no_errors(&parser.errors); + assert_eq!(comments.len(), 1); + assert_eq!(comments[0], "Hello\nWorld\n\n!"); } #[test] @@ -75,7 +134,27 @@ mod tests { let comments = parser.parse_outer_doc_comments(); expect_no_errors(&parser.errors); assert_eq!(comments.len(), 2); - assert_eq!(comments[0], " Hello"); - assert_eq!(comments[1], " World"); + assert_eq!(comments[0], "Hello"); + assert_eq!(comments[1], "World"); + } + + #[test] + fn parses_outer_block_doc_comments() { + let src = "/** Hello\n * World\n *\n * !\n*/"; + let mut parser = Parser::for_str_with_dummy_file(src); + let comments = parser.parse_outer_doc_comments(); + expect_no_errors(&parser.errors); + assert_eq!(comments.len(), 1); + assert_eq!(comments[0], "Hello\nWorld\n\n!"); + } + + #[test] + fn parses_outer_block_doc_comments_not_every_line_has_stars() { + let src = "/** Hello\n * World\n Oops\n * !\n*/"; + let mut parser = Parser::for_str_with_dummy_file(src); + let comments = parser.parse_outer_doc_comments(); + expect_no_errors(&parser.errors); + assert_eq!(comments.len(), 1); + assert_eq!(comments[0], "Hello\n* World\nOops\n* !"); } } diff --git a/compiler/noirc_frontend/src/parser/parser/item.rs b/compiler/noirc_frontend/src/parser/parser/item.rs index 7172f95f622..a936cad774a 100644 --- a/compiler/noirc_frontend/src/parser/parser/item.rs +++ b/compiler/noirc_frontend/src/parser/parser/item.rs @@ -340,7 +340,7 @@ mod tests { let item = &module.items[0]; assert_eq!( item.doc_comments, - vec![" One".to_string(), " Two".to_string(), " Three".to_string(),] + vec!["One".to_string(), "Two".to_string(), "Three".to_string(),] ); let ItemKind::Function(func) = &item.kind else { panic!("Expected function"); diff --git a/tooling/lsp/src/requests/hover.rs b/tooling/lsp/src/requests/hover.rs index 93f985d1c09..054f328631f 100644 --- a/tooling/lsp/src/requests/hover.rs +++ b/tooling/lsp/src/requests/hover.rs @@ -446,7 +446,7 @@ mod hover_tests { --- - Red, blue, etc." +Red, blue, etc." )); } @@ -463,7 +463,7 @@ mod hover_tests { --- - Red, blue, etc." +Red, blue, etc." )); } @@ -478,7 +478,7 @@ mod hover_tests { --- - Like a tomato" +Like a tomato" )); } @@ -493,7 +493,7 @@ mod hover_tests { --- - Like a tomato" +Like a tomato" )); }