Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
92 changes: 68 additions & 24 deletions crates/oxc_codegen/src/comment.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,21 +26,54 @@ impl<'a> Codegen<'a> {
}
}

pub fn has_annotation_comments(&self, start: u32) -> bool {
pub fn has_comment(&self, start: u32) -> bool {
self.comments.contains_key(&start)
}

pub fn has_annotation_comment(&self, start: u32) -> bool {
let Some(source_text) = self.source_text else { return false };
self.comments.get(&start).is_some_and(|comments| {
comments.iter().any(|comment| Self::is_annotation_comments(comment, source_text))
comments.iter().any(|comment| Self::is_annotation_comment(comment, source_text))
})
}

pub fn has_non_annotation_comment(&self, start: u32) -> bool {
let Some(source_text) = self.source_text else { return false };
self.comments.get(&start).is_some_and(|comments| {
comments.iter().any(|comment| !Self::is_annotation_comment(comment, source_text))
})
}

/// Weather to keep leading comments.
fn is_leading_comments(comment: &Comment, source_text: &str) -> bool {
(comment.is_jsdoc(source_text) || (comment.is_line() && Self::is_annotation_comments(comment, source_text)))
(comment.is_jsdoc(source_text) || (comment.is_line() && Self::is_annotation_comment(comment, source_text)))
&& comment.preceded_by_newline
// webpack comment `/*****/`
&& !comment.span.source_text(source_text).chars().all(|c| c == '*')
}

fn print_comment(&mut self, comment: &Comment, source_text: &str) {
let comment_source = comment.real_span().source_text(source_text);
match comment.kind {
CommentKind::Line => {
self.print_str(comment_source);
}
CommentKind::Block => {
// Print block comments with our own indentation.
let lines = comment_source.split(is_line_terminator);
for line in lines {
if !line.starts_with("/*") {
self.print_indent();
}
self.print_str(line.trim_start());
if !line.ends_with("*/") {
self.print_hard_newline();
}
}
}
}
}

pub(crate) fn print_leading_comments(&mut self, start: u32) {
if self.options.minify {
return;
Expand Down Expand Up @@ -68,25 +101,7 @@ impl<'a> Codegen<'a> {
self.print_indent();
}

let comment_source = comment.real_span().source_text(source_text);
match comment.kind {
CommentKind::Line => {
self.print_str(comment_source);
}
CommentKind::Block => {
// Print block comments with our own indentation.
let lines = comment_source.split(is_line_terminator);
for line in lines {
if !line.starts_with("/*") {
self.print_indent();
}
self.print_str(line.trim_start());
if !line.ends_with("*/") {
self.print_hard_newline();
}
}
}
}
self.print_comment(comment, source_text);
}

if comments.last().is_some_and(|c| c.is_line() || c.followed_by_newline) {
Expand All @@ -99,7 +114,7 @@ impl<'a> Codegen<'a> {
}
}

fn is_annotation_comments(comment: &Comment, source_text: &str) -> bool {
fn is_annotation_comment(comment: &Comment, source_text: &str) -> bool {
let comment_content = comment.span.source_text(source_text);
ANNOTATION_MATCHER.find_iter(comment_content).count() != 0
}
Expand All @@ -116,11 +131,40 @@ impl<'a> Codegen<'a> {
let Some(comments) = self.comments.remove(&start) else { return };

for comment in comments {
if !Self::is_annotation_comments(&comment, source_text) {
if !Self::is_annotation_comment(&comment, source_text) {
continue;
}
self.print_str(comment.real_span().source_text(source_text));
self.print_hard_space();
}
}

pub(crate) fn print_expr_comments(&mut self, start: u32) -> bool {
if self.options.minify {
return false;
}
let Some(source_text) = self.source_text else { return false };
let Some(comments) = self.comments.remove(&start) else { return false };

let (annotation_comments, comments): (Vec<_>, Vec<_>) = comments
.into_iter()
.partition(|comment| Self::is_annotation_comment(comment, source_text));

if !annotation_comments.is_empty() {
self.comments.insert(start, annotation_comments);
}

for comment in &comments {
self.print_hard_newline();
self.print_indent();
self.print_comment(comment, source_text);
}

if comments.is_empty() {
false
} else {
self.print_hard_newline();
true
}
}
}
62 changes: 56 additions & 6 deletions crates/oxc_codegen/src/gen.rs
Original file line number Diff line number Diff line change
Expand Up @@ -562,7 +562,7 @@ impl<'a> Gen for VariableDeclaration<'a> {
&& p.start_of_annotation_comment.is_none()
&& matches!(self.kind, VariableDeclarationKind::Const)
&& matches!(self.declarations.first(), Some(VariableDeclarator { init: Some(init), .. }) if init.is_function())
&& p.has_annotation_comments(self.span.start)
&& p.has_annotation_comment(self.span.start)
{
p.start_of_annotation_comment = Some(self.span.start);
}
Expand Down Expand Up @@ -834,7 +834,7 @@ impl<'a> Gen for ExportNamedDeclaration<'a> {
if matches!(var_decl.kind, VariableDeclarationKind::Const) =>
{
if matches!(var_decl.declarations.first(), Some(VariableDeclarator { init: Some(init), .. }) if init.is_function())
&& p.has_annotation_comments(self.span.start)
&& p.has_annotation_comment(self.span.start)
{
p.start_of_annotation_comment = Some(self.span.start);
}
Expand Down Expand Up @@ -1368,7 +1368,7 @@ impl<'a> GenExpr for CallExpression<'a> {
fn gen_expr(&self, p: &mut Codegen, precedence: Precedence, ctx: Context) {
let is_export_default = p.start_of_default_export == p.code_len();
let mut wrap = precedence >= Precedence::New || ctx.intersects(Context::FORBID_CALL);
if p.has_annotation_comments(self.span.start) && precedence >= Precedence::Postfix {
if p.has_annotation_comment(self.span.start) && precedence >= Precedence::Postfix {
wrap = true;
}

Expand All @@ -1386,7 +1386,19 @@ impl<'a> GenExpr for CallExpression<'a> {
type_parameters.print(p, ctx);
}
p.print_char(b'(');
p.print_list(&self.arguments, ctx);
let has_comment = (self.span.end > 0 && p.has_comment(self.span.end - 1))
|| self.arguments.iter().any(|item| p.has_comment(item.span().start));
if has_comment {
p.indent();
p.print_list_with_comments(&self.arguments, ctx);
// Handle `/* comment */);`
if !p.print_expr_comments(self.span.end - 1) {
p.print_soft_newline();
}
p.dedent();
} else {
p.print_list(&self.arguments, ctx);
}
p.print_char(b')');
p.add_source_mapping(self.span.end);
});
Expand Down Expand Up @@ -1949,14 +1961,40 @@ impl<'a> GenExpr for SequenceExpression<'a> {
impl<'a> GenExpr for ImportExpression<'a> {
fn gen_expr(&self, p: &mut Codegen, precedence: Precedence, ctx: Context) {
let wrap = precedence >= Precedence::New || ctx.intersects(Context::FORBID_CALL);
let has_comment = (self.span.end > 0 && p.has_comment(self.span.end - 1))
|| p.has_comment(self.source.span().start)
|| self.arguments.first().is_some_and(|argument| p.has_comment(argument.span().start));

p.wrap(wrap, |p| {
p.add_source_mapping(self.span.start);
p.print_str("import(");
if has_comment {
p.indent();
}
if p.print_expr_comments(self.source.span().start) {
p.print_indent();
} else if has_comment {
p.print_soft_newline();
p.print_indent();
}
self.source.print_expr(p, Precedence::Comma, Context::empty());
if !self.arguments.is_empty() {
p.print_comma();
if has_comment {
p.print_soft_newline();
p.print_indent();
} else {
p.print_soft_space();
}
p.print_expressions(&self.arguments, Precedence::Comma, Context::empty());
}
if has_comment {
// Handle `/* comment */);`
if !p.print_expr_comments(self.span.end - 1) {
p.print_soft_newline();
}
p.dedent();
}
p.print_char(b')');
});
}
Expand Down Expand Up @@ -2024,7 +2062,7 @@ impl<'a> GenExpr for ChainExpression<'a> {
impl<'a> GenExpr for NewExpression<'a> {
fn gen_expr(&self, p: &mut Codegen, precedence: Precedence, ctx: Context) {
let mut wrap = precedence >= self.precedence();
if p.has_annotation_comments(self.span.start) && precedence >= Precedence::Postfix {
if p.has_annotation_comment(self.span.start) && precedence >= Precedence::Postfix {
wrap = true;
}
p.wrap(wrap, |p| {
Expand All @@ -2034,7 +2072,19 @@ impl<'a> GenExpr for NewExpression<'a> {
p.print_str("new ");
self.callee.print_expr(p, Precedence::New, Context::FORBID_CALL);
p.print_char(b'(');
p.print_list(&self.arguments, ctx);
let has_comment = p.has_comment(self.span.end - 1)
|| self.arguments.iter().any(|item| p.has_comment(item.span().start));
if has_comment {
p.indent();
p.print_list_with_comments(&self.arguments, ctx);
// Handle `/* comment */);`
if !p.print_expr_comments(self.span.end - 1) {
p.print_soft_newline();
}
p.dedent();
} else {
p.print_list(&self.arguments, ctx);
}
p.print_char(b')');
});
}
Expand Down
18 changes: 17 additions & 1 deletion crates/oxc_codegen/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ use oxc_ast::{
Trivias,
};
use oxc_mangler::Mangler;
use oxc_span::Span;
use oxc_span::{GetSpan, Span};
use oxc_syntax::{
identifier::is_identifier_part,
operator::{BinaryOperator, UnaryOperator, UpdateOperator},
Expand Down Expand Up @@ -468,6 +468,22 @@ impl<'a> Codegen<'a> {
}
}

fn print_list_with_comments<T: Gen + GetSpan>(&mut self, items: &[T], ctx: Context) {
for (index, item) in items.iter().enumerate() {
if index != 0 {
self.print_comma();
}
if self.has_non_annotation_comment(item.span().start) {
self.print_expr_comments(item.span().start);
self.print_indent();
} else {
self.print_soft_newline();
self.print_indent();
}
item.print(self, ctx);
}
}

fn print_expressions<T: GenExpr>(&mut self, items: &[T], precedence: Precedence, ctx: Context) {
for (index, item) in items.iter().enumerate() {
if index != 0 {
Expand Down
Loading