diff --git a/crates/oxc_ast/src/ast/comment.rs b/crates/oxc_ast/src/ast/comment.rs index 7bf3d20fe72a8..8c0ff61971d50 100644 --- a/crates/oxc_ast/src/ast/comment.rs +++ b/crates/oxc_ast/src/ast/comment.rs @@ -81,6 +81,22 @@ pub enum CommentAnnotation { CoverageIgnore = 7, } +/// State of newlines around a comment. +#[ast] +#[generate_derive(CloneIn, ContentEq)] +#[derive(Debug, Default, Clone, Copy, Eq, PartialEq)] +pub enum CommentNewlines { + /// No newlines before or after + #[default] + None = 0, + /// Preceded by a newline + Leading = 1, + /// Followed by a newline + Trailing = 2, + /// Preceded and followed by a newline + LeadingAndTrailing = 3, +} + /// A comment in source code. #[ast] #[generate_derive(CloneIn, ContentEq, ESTree)] @@ -105,14 +121,10 @@ pub struct Comment { #[estree(skip)] pub position: CommentPosition, - /// Whether this comment has a preceding newline. + /// Whether this comment has newlines around it. /// Used to avoid becoming a trailing comment in codegen. #[estree(skip)] - pub preceded_by_newline: bool, - - /// Whether this comment has a tailing newline. - #[estree(skip)] - pub followed_by_newline: bool, + pub newlines: CommentNewlines, /// Comment Annotation #[estree(skip)] @@ -129,8 +141,7 @@ impl Comment { attached_to: 0, kind, position: CommentPosition::Trailing, - preceded_by_newline: false, - followed_by_newline: false, + newlines: CommentNewlines::None, annotation: CommentAnnotation::None, } } @@ -219,4 +230,50 @@ impl Comment { pub fn is_coverage_ignore(self) -> bool { self.annotation == CommentAnnotation::CoverageIgnore && self.is_leading() } + + /// Sets the state of `newlines` to include/exclude a newline after the comment. + pub fn set_followed_by_newline(&mut self, followed_by_newline: bool) { + if followed_by_newline { + self.newlines = match self.newlines { + CommentNewlines::None => CommentNewlines::Trailing, + CommentNewlines::Leading => CommentNewlines::LeadingAndTrailing, + _ => self.newlines, + } + } else { + self.newlines = match self.newlines { + CommentNewlines::Trailing => CommentNewlines::None, + CommentNewlines::LeadingAndTrailing => CommentNewlines::Leading, + _ => self.newlines, + } + } + } + + /// Sets the state of `newlines` to include/exclude a newline before the comment. + pub fn set_preceded_by_newline(&mut self, preceded_by_newline: bool) { + if preceded_by_newline { + self.newlines = match self.newlines { + CommentNewlines::None => CommentNewlines::Leading, + CommentNewlines::Trailing => CommentNewlines::LeadingAndTrailing, + _ => self.newlines, + } + } else { + self.newlines = match self.newlines { + CommentNewlines::Leading => CommentNewlines::None, + CommentNewlines::LeadingAndTrailing => CommentNewlines::Trailing, + _ => self.newlines, + } + } + } + + /// Returns `true` if this comment is preceded by a newline. + #[inline] + pub fn preceded_by_newline(self) -> bool { + matches!(self.newlines, CommentNewlines::Leading | CommentNewlines::LeadingAndTrailing) + } + + /// Returns `true` if this comment is followed by a newline. + #[inline] + pub fn followed_by_newline(self) -> bool { + matches!(self.newlines, CommentNewlines::Trailing | CommentNewlines::LeadingAndTrailing) + } } diff --git a/crates/oxc_ast/src/generated/assert_layouts.rs b/crates/oxc_ast/src/generated/assert_layouts.rs index 9155a2f00d759..93ee3134b71f9 100644 --- a/crates/oxc_ast/src/generated/assert_layouts.rs +++ b/crates/oxc_ast/src/generated/assert_layouts.rs @@ -1581,16 +1581,18 @@ const _: () = { assert!(size_of::() == 1); assert!(align_of::() == 1); - // Padding: 7 bytes - assert!(size_of::() == 24); + assert!(size_of::() == 1); + assert!(align_of::() == 1); + + // Padding: 0 bytes + assert!(size_of::() == 16); assert!(align_of::() == 8); assert!(offset_of!(Comment, span) == 0); assert!(offset_of!(Comment, attached_to) == 8); assert!(offset_of!(Comment, kind) == 12); assert!(offset_of!(Comment, position) == 13); - assert!(offset_of!(Comment, preceded_by_newline) == 14); - assert!(offset_of!(Comment, followed_by_newline) == 15); - assert!(offset_of!(Comment, annotation) == 16); + assert!(offset_of!(Comment, newlines) == 14); + assert!(offset_of!(Comment, annotation) == 15); }; #[cfg(target_pointer_width = "32")] @@ -3167,16 +3169,18 @@ const _: () = { assert!(size_of::() == 1); assert!(align_of::() == 1); - // Padding: 3 bytes - assert!(size_of::() == 20); + assert!(size_of::() == 1); + assert!(align_of::() == 1); + + // Padding: 0 bytes + assert!(size_of::() == 16); assert!(align_of::() == 4); assert!(offset_of!(Comment, span) == 0); assert!(offset_of!(Comment, attached_to) == 8); assert!(offset_of!(Comment, kind) == 12); assert!(offset_of!(Comment, position) == 13); - assert!(offset_of!(Comment, preceded_by_newline) == 14); - assert!(offset_of!(Comment, followed_by_newline) == 15); - assert!(offset_of!(Comment, annotation) == 16); + assert!(offset_of!(Comment, newlines) == 14); + assert!(offset_of!(Comment, annotation) == 15); }; #[cfg(not(any(target_pointer_width = "64", target_pointer_width = "32")))] diff --git a/crates/oxc_ast/src/generated/derive_clone_in.rs b/crates/oxc_ast/src/generated/derive_clone_in.rs index 958dc30676737..457a709f1d978 100644 --- a/crates/oxc_ast/src/generated/derive_clone_in.rs +++ b/crates/oxc_ast/src/generated/derive_clone_in.rs @@ -7866,6 +7866,20 @@ impl<'new_alloc> CloneIn<'new_alloc> for CommentAnnotation { } } +impl<'new_alloc> CloneIn<'new_alloc> for CommentNewlines { + type Cloned = CommentNewlines; + + #[inline(always)] + fn clone_in(&self, allocator: &'new_alloc Allocator) -> Self::Cloned { + *self + } + + #[inline(always)] + fn clone_in_with_semantic_ids(&self, allocator: &'new_alloc Allocator) -> Self::Cloned { + *self + } +} + impl<'new_alloc> CloneIn<'new_alloc> for Comment { type Cloned = Comment; @@ -7875,8 +7889,7 @@ impl<'new_alloc> CloneIn<'new_alloc> for Comment { attached_to: CloneIn::clone_in(&self.attached_to, allocator), kind: CloneIn::clone_in(&self.kind, allocator), position: CloneIn::clone_in(&self.position, allocator), - preceded_by_newline: CloneIn::clone_in(&self.preceded_by_newline, allocator), - followed_by_newline: CloneIn::clone_in(&self.followed_by_newline, allocator), + newlines: CloneIn::clone_in(&self.newlines, allocator), annotation: CloneIn::clone_in(&self.annotation, allocator), } } @@ -7887,14 +7900,7 @@ impl<'new_alloc> CloneIn<'new_alloc> for Comment { attached_to: CloneIn::clone_in_with_semantic_ids(&self.attached_to, allocator), kind: CloneIn::clone_in_with_semantic_ids(&self.kind, allocator), position: CloneIn::clone_in_with_semantic_ids(&self.position, allocator), - preceded_by_newline: CloneIn::clone_in_with_semantic_ids( - &self.preceded_by_newline, - allocator, - ), - followed_by_newline: CloneIn::clone_in_with_semantic_ids( - &self.followed_by_newline, - allocator, - ), + newlines: CloneIn::clone_in_with_semantic_ids(&self.newlines, allocator), annotation: CloneIn::clone_in_with_semantic_ids(&self.annotation, allocator), } } diff --git a/crates/oxc_ast/src/generated/derive_content_eq.rs b/crates/oxc_ast/src/generated/derive_content_eq.rs index e433ecea7d801..38731cab2b576 100644 --- a/crates/oxc_ast/src/generated/derive_content_eq.rs +++ b/crates/oxc_ast/src/generated/derive_content_eq.rs @@ -2482,13 +2482,18 @@ impl ContentEq for CommentAnnotation { } } +impl ContentEq for CommentNewlines { + fn content_eq(&self, other: &Self) -> bool { + self == other + } +} + impl ContentEq for Comment { fn content_eq(&self, other: &Self) -> bool { ContentEq::content_eq(&self.attached_to, &other.attached_to) && ContentEq::content_eq(&self.kind, &other.kind) && ContentEq::content_eq(&self.position, &other.position) - && ContentEq::content_eq(&self.preceded_by_newline, &other.preceded_by_newline) - && ContentEq::content_eq(&self.followed_by_newline, &other.followed_by_newline) + && ContentEq::content_eq(&self.newlines, &other.newlines) && ContentEq::content_eq(&self.annotation, &other.annotation) } } diff --git a/crates/oxc_codegen/src/comment.rs b/crates/oxc_codegen/src/comment.rs index 4fe416a499b18..d958ddc7c1cd4 100644 --- a/crates/oxc_codegen/src/comment.rs +++ b/crates/oxc_codegen/src/comment.rs @@ -96,7 +96,7 @@ impl Codegen<'_> { pub(crate) fn print_comments(&mut self, comments: &[Comment]) { for (i, comment) in comments.iter().enumerate() { if i == 0 { - if comment.preceded_by_newline { + if comment.preceded_by_newline() { // Skip printing newline if this comment is already on a newline. if let Some(b) = self.last_byte() { match b { @@ -113,7 +113,7 @@ impl Codegen<'_> { } } if i >= 1 { - if comment.preceded_by_newline { + if comment.preceded_by_newline() { self.print_hard_newline(); self.print_indent(); } else if comment.is_legal() { @@ -122,7 +122,7 @@ impl Codegen<'_> { } self.print_comment(comment); if i == comments.len() - 1 { - if comment.is_line() || comment.followed_by_newline { + if comment.is_line() || comment.followed_by_newline() { self.print_hard_newline(); } else { self.print_next_indent_as_space = true; diff --git a/crates/oxc_parser/src/lexer/trivia_builder.rs b/crates/oxc_parser/src/lexer/trivia_builder.rs index a528bd1a44e24..aa4327253f604 100644 --- a/crates/oxc_parser/src/lexer/trivia_builder.rs +++ b/crates/oxc_parser/src/lexer/trivia_builder.rs @@ -69,7 +69,7 @@ impl TriviaBuilder { // The last unprocessed comment is on a newline. let len = self.comments.len(); if self.processed < len { - self.comments[len - 1].followed_by_newline = true; + self.comments[len - 1].set_followed_by_newline(true); if !self.saw_newline { self.processed = self.comments.len(); } @@ -131,10 +131,10 @@ impl TriviaBuilder { } // This newly added comment may be preceded by a newline. - comment.preceded_by_newline = self.saw_newline; + comment.set_preceded_by_newline(self.saw_newline); if comment.is_line() { // A line comment is always followed by a newline. This is never set in `handle_newline`. - comment.followed_by_newline = true; + comment.set_followed_by_newline(true); if self.should_be_treated_as_trailing_comment() { self.processed = self.comments.len() + 1; // +1 to include this comment. } @@ -241,7 +241,7 @@ fn contains_license_or_preserve_comment(s: &str) -> bool { #[cfg(test)] mod test { use oxc_allocator::Allocator; - use oxc_ast::{Comment, CommentAnnotation, CommentKind, CommentPosition}; + use oxc_ast::{Comment, CommentAnnotation, CommentKind, CommentPosition, ast::CommentNewlines}; use oxc_span::{SourceType, Span}; use crate::Parser; @@ -268,8 +268,7 @@ mod test { kind: CommentKind::Block, position: CommentPosition::Leading, attached_to: 70, - preceded_by_newline: true, - followed_by_newline: true, + newlines: CommentNewlines::LeadingAndTrailing, annotation: CommentAnnotation::None, }, Comment { @@ -277,8 +276,7 @@ mod test { kind: CommentKind::Line, position: CommentPosition::Leading, attached_to: 70, - preceded_by_newline: true, - followed_by_newline: true, + newlines: CommentNewlines::LeadingAndTrailing, annotation: CommentAnnotation::None, }, Comment { @@ -286,8 +284,7 @@ mod test { kind: CommentKind::Block, position: CommentPosition::Leading, attached_to: 70, - preceded_by_newline: true, - followed_by_newline: false, + newlines: CommentNewlines::Leading, annotation: CommentAnnotation::None, }, Comment { @@ -295,8 +292,7 @@ mod test { kind: CommentKind::Block, position: CommentPosition::Trailing, attached_to: 0, - preceded_by_newline: false, - followed_by_newline: false, + newlines: CommentNewlines::None, annotation: CommentAnnotation::None, }, Comment { @@ -304,8 +300,7 @@ mod test { kind: CommentKind::Line, position: CommentPosition::Trailing, attached_to: 0, - preceded_by_newline: false, - followed_by_newline: true, + newlines: CommentNewlines::Trailing, annotation: CommentAnnotation::None, }, Comment { @@ -313,8 +308,7 @@ mod test { kind: CommentKind::Line, position: CommentPosition::Leading, attached_to: 147, - preceded_by_newline: true, - followed_by_newline: true, + newlines: CommentNewlines::LeadingAndTrailing, annotation: CommentAnnotation::None, }, ]; @@ -338,8 +332,7 @@ token /* Trailing 1 */ kind: CommentKind::Block, position: CommentPosition::Leading, attached_to: 36, - preceded_by_newline: true, - followed_by_newline: true, + newlines: CommentNewlines::LeadingAndTrailing, annotation: CommentAnnotation::None, }, Comment { @@ -347,8 +340,7 @@ token /* Trailing 1 */ kind: CommentKind::Block, position: CommentPosition::Trailing, attached_to: 0, - preceded_by_newline: false, - followed_by_newline: true, + newlines: CommentNewlines::Trailing, annotation: CommentAnnotation::None, }, ]; @@ -373,8 +365,7 @@ token /* Trailing 1 */ kind: CommentKind::Block, position: CommentPosition::Leading, attached_to: 28, - preceded_by_newline: true, - followed_by_newline: true, + newlines: CommentNewlines::LeadingAndTrailing, annotation: CommentAnnotation::None, }, Comment { @@ -382,8 +373,7 @@ token /* Trailing 1 */ kind: CommentKind::Block, position: CommentPosition::Leading, attached_to: 28, - preceded_by_newline: true, - followed_by_newline: true, + newlines: CommentNewlines::LeadingAndTrailing, annotation: CommentAnnotation::None, }, ]; @@ -406,8 +396,7 @@ token /* Trailing 1 */ kind: CommentKind::Line, position: CommentPosition::Leading, attached_to: 57, - preceded_by_newline: false, - followed_by_newline: true, + newlines: CommentNewlines::Trailing, annotation: CommentAnnotation::None, }, Comment { @@ -415,8 +404,7 @@ token /* Trailing 1 */ kind: CommentKind::Line, position: CommentPosition::Leading, attached_to: 129, - preceded_by_newline: false, - followed_by_newline: true, + newlines: CommentNewlines::Trailing, annotation: CommentAnnotation::None, }, ]; @@ -438,8 +426,7 @@ token /* Trailing 1 */ kind: CommentKind::Line, position: CommentPosition::Leading, attached_to: 55, - preceded_by_newline: false, - followed_by_newline: true, + newlines: CommentNewlines::Trailing, annotation: CommentAnnotation::None, }, Comment { @@ -447,8 +434,7 @@ token /* Trailing 1 */ kind: CommentKind::Line, position: CommentPosition::Leading, attached_to: 116, - preceded_by_newline: false, - followed_by_newline: true, + newlines: CommentNewlines::Trailing, annotation: CommentAnnotation::None, }, ]; diff --git a/napi/parser/generated/deserialize/js.js b/napi/parser/generated/deserialize/js.js index 4e2f67fb03ed2..de13a7f1c2da4 100644 --- a/napi/parser/generated/deserialize/js.js +++ b/napi/parser/generated/deserialize/js.js @@ -4055,7 +4055,7 @@ function deserializeVecComment(pos) { pos = uint32[pos32]; for (let i = 0; i < len; i++) { arr.push(deserializeComment(pos)); - pos += 24; + pos += 16; } return arr; } diff --git a/napi/parser/generated/deserialize/ts.js b/napi/parser/generated/deserialize/ts.js index 598678a96d4aa..c727f72c37008 100644 --- a/napi/parser/generated/deserialize/ts.js +++ b/napi/parser/generated/deserialize/ts.js @@ -4207,7 +4207,7 @@ function deserializeVecComment(pos) { pos = uint32[pos32]; for (let i = 0; i < len; i++) { arr.push(deserializeComment(pos)); - pos += 24; + pos += 16; } return arr; }