From 9ca202a01e8f00c21daf8e22102b6cabcb981d65 Mon Sep 17 00:00:00 2001 From: Boshen <1430279+Boshen@users.noreply.github.com> Date: Tue, 24 Sep 2024 05:14:02 +0000 Subject: [PATCH] fix(codegen): preserve newlines between comments (#6014) fixes #6010 --- crates/oxc_codegen/src/comment.rs | 23 ++++++++------- crates/oxc_codegen/tests/integration/jsdoc.rs | 14 ++++++++++ .../tests/integration/snapshots/jsodc.snap | 28 ++++++++++++++++++- .../examples/isolated_declarations.rs | 2 +- 4 files changed, 55 insertions(+), 12 deletions(-) diff --git a/crates/oxc_codegen/src/comment.rs b/crates/oxc_codegen/src/comment.rs index 2a8de5ad7fafc..ec6aaedb37de4 100644 --- a/crates/oxc_codegen/src/comment.rs +++ b/crates/oxc_codegen/src/comment.rs @@ -1,7 +1,7 @@ -use oxc_syntax::identifier::is_line_terminator; use rustc_hash::FxHashMap; use oxc_ast::{Comment, CommentKind, Trivias}; +use oxc_syntax::identifier::is_line_terminator; use crate::Codegen; @@ -34,24 +34,28 @@ impl<'a> Codegen<'a> { let Some(source_text) = self.source_text else { return }; let Some(comments) = self.leading_comments.remove(&start) else { return }; - let first = comments.first().unwrap(); - if first.preceded_by_newline { + if comments.first().is_some_and(|c| c.preceded_by_newline) { // Skip printing newline if this comment is already on a newline. if self.peek_nth(0).is_some_and(|c| c != '\n' && c != '\t') { - self.print_char(b'\n'); + self.print_hard_newline(); self.print_indent(); } } - for comment in &comments { - let s = comment.real_span().source_text(source_text); + for (i, comment) in comments.iter().enumerate() { + if i >= 1 && comment.preceded_by_newline { + self.print_hard_newline(); + self.print_indent(); + } + + let comment_source = comment.real_span().source_text(source_text); match comment.kind { CommentKind::Line => { - self.print_str(s); + self.print_str(comment_source); } CommentKind::Block => { // Print block comments with our own indentation. - let lines = s.split(is_line_terminator); + let lines = comment_source.split(is_line_terminator); for line in lines { if !line.starts_with("/*") { self.print_indent(); @@ -65,8 +69,7 @@ impl<'a> Codegen<'a> { } } - let last = comments.last().unwrap(); - if last.is_line() || last.followed_by_newline { + if comments.last().is_some_and(|c| c.is_line() || c.followed_by_newline) { self.print_hard_newline(); self.print_indent(); } diff --git a/crates/oxc_codegen/tests/integration/jsdoc.rs b/crates/oxc_codegen/tests/integration/jsdoc.rs index 2f5d625382b55..7710a14363dfd 100644 --- a/crates/oxc_codegen/tests/integration/jsdoc.rs +++ b/crates/oxc_codegen/tests/integration/jsdoc.rs @@ -4,10 +4,20 @@ use crate::snapshot; fn comment() { let cases = vec![ r" +/** + * Top level + * + * @module + */ + /** This is a description of the foo function. */ function foo() { } +/** + * Preserve newline + */ + /** * Represents a book. * @constructor @@ -19,6 +29,10 @@ function Book(title, author) { /** Class representing a point. */ class Point { + /** + * Preserve newline + */ + /** * Create a point. * @param {number} x - The x value. diff --git a/crates/oxc_codegen/tests/integration/snapshots/jsodc.snap b/crates/oxc_codegen/tests/integration/snapshots/jsodc.snap index 93f07fff3e2da..0c266c7fad012 100644 --- a/crates/oxc_codegen/tests/integration/snapshots/jsodc.snap +++ b/crates/oxc_codegen/tests/integration/snapshots/jsodc.snap @@ -3,10 +3,20 @@ source: crates/oxc_codegen/tests/integration/main.rs --- ########## 0 +/** + * Top level + * + * @module + */ + /** This is a description of the foo function. */ function foo() { } +/** + * Preserve newline + */ + /** * Represents a book. * @constructor @@ -18,6 +28,10 @@ function Book(title, author) { /** Class representing a point. */ class Point { + /** + * Preserve newline + */ + /** * Create a point. * @param {number} x - The x value. @@ -93,9 +107,17 @@ export enum DefinitionKind { } ---------- +/** +* Top level +* +* @module +*/ /** This is a description of the foo function. */ function foo() {} /** +* Preserve newline +*/ +/** * Represents a book. * @constructor * @param {string} title - The title of the book. @@ -104,6 +126,9 @@ function foo() {} function Book(title, author) {} /** Class representing a point. */ class Point { + /** + * Preserve newline + */ /** * Create a point. * @param {number} x - The x value. @@ -132,7 +157,8 @@ const Point = class {}; /** * Shirt module. * @module my/shirt -*//** Button the shirt. */ +*/ +/** Button the shirt. */ exports.button = function() {}; /** Unbutton the shirt. */ exports.unbutton = function() {}; diff --git a/crates/oxc_isolated_declarations/examples/isolated_declarations.rs b/crates/oxc_isolated_declarations/examples/isolated_declarations.rs index e55d17d80debc..c20970ea0f4c4 100644 --- a/crates/oxc_isolated_declarations/examples/isolated_declarations.rs +++ b/crates/oxc_isolated_declarations/examples/isolated_declarations.rs @@ -10,7 +10,7 @@ use oxc_span::SourceType; // Instruction: // create a `test.js`, // run `cargo run -p oxc_isolated_declarations --example isolated_declarations` -// or `just watch "run -p oxc_isolated_declarations --example isolated_declarations"` +// or `just example isolated_declarations` fn main() { let name = env::args().nth(1).unwrap_or_else(|| "test.tsx".to_string());