diff --git a/crates/oxc_prettier/src/comments/print.rs b/crates/oxc_prettier/src/comments/print.rs index 9d6e4120d..dea8917e8 100644 --- a/crates/oxc_prettier/src/comments/print.rs +++ b/crates/oxc_prettier/src/comments/print.rs @@ -74,7 +74,7 @@ impl<'a> Prettier<'a> { } if self - .skip_spaces(comment.end, false) + .skip_spaces(Some(comment.end), false) .and_then(|idx| self.skip_newline(Some(idx), false)) .is_some_and(|i| self.has_newline(i, /* backwards */ false)) { diff --git a/crates/oxc_prettier/src/format/mod.rs b/crates/oxc_prettier/src/format/mod.rs index d475c9231..24eb30850 100644 --- a/crates/oxc_prettier/src/format/mod.rs +++ b/crates/oxc_prettier/src/format/mod.rs @@ -61,7 +61,7 @@ impl<'a> Format<'a> for Program<'a> { let mut parts = p.vec(); if let Some(hashbang) = &self.hashbang { parts.push(hashbang.format(p)); - if p.is_next_line_empty(hashbang.span.end - 1) { + if p.is_next_line_empty_after_index(hashbang.span.end - 1) { parts.extend(hardline!()); } } @@ -427,7 +427,7 @@ impl<'a> Format<'a> for SwitchStatement<'a> { parts.push(format!(p, case)); Doc::Indent(parts) }); - if i != len - 1 && p.is_next_line_empty(case.span.end) { + if i != len - 1 && p.is_next_line_empty(case.span) { cases_parts.extend(hardline!()); } } @@ -467,7 +467,7 @@ impl<'a> Format<'a> for SwitchCase<'a> { if i != 0 && matches!(stmt, Statement::BreakStatement(_)) { let last_stmt = &consequent[i - 1]; - if p.is_next_line_empty(last_stmt.span().end) { + if p.is_next_line_empty(last_stmt.span()) { consequent_parts.extend(hardline!()); } } diff --git a/crates/oxc_prettier/src/format/statement.rs b/crates/oxc_prettier/src/format/statement.rs index d80a8ece9..7b890980b 100644 --- a/crates/oxc_prettier/src/format/statement.rs +++ b/crates/oxc_prettier/src/format/statement.rs @@ -46,7 +46,7 @@ pub(super) fn print_statement_sequence<'a>( if i < len - 1 { parts.extend(hardline!()); - if p.is_next_line_empty(stmt.span().end) { + if p.is_next_line_empty(stmt.span()) { parts.extend(hardline!()); } } diff --git a/crates/oxc_prettier/src/lib.rs b/crates/oxc_prettier/src/lib.rs index 9dcbf9777..7a0367bbd 100644 --- a/crates/oxc_prettier/src/lib.rs +++ b/crates/oxc_prettier/src/lib.rs @@ -14,12 +14,12 @@ mod printer; use std::{iter::Peekable, vec}; -use doc::DocBuilder; use oxc_allocator::Allocator; use oxc_ast::{ast::Program, AstKind, CommentKind, Trivias}; +use oxc_span::Span; use oxc_syntax::identifier::is_line_terminator; -use crate::{doc::Doc, format::Format, printer::Printer}; +use crate::{doc::Doc, doc::DocBuilder, format::Format, printer::Printer}; pub use crate::options::{ArrowParens, EndOfLine, PrettierOptions, QuoteProps, TrailingComma}; @@ -135,8 +135,55 @@ impl<'a> Prettier<'a> { trailing_comma.is_all() || (trailing_comma.is_es5() && !level_all) } - fn is_next_line_empty(&self, end: u32) -> bool { - self.source_text[end as usize..].chars().nth(1).is_some_and(|c| c == '\n') + fn is_next_line_empty(&self, span: Span) -> bool { + self.is_next_line_empty_after_index(span.end) + } + + fn is_next_line_empty_after_index(&self, start_index: u32) -> bool { + let mut old_idx = None; + let mut idx = Some(start_index); + while idx != old_idx { + old_idx = idx; + idx = self.skip_to_line_end(idx); + // idx = self.skip_inline_comment(idx); + idx = self.skip_spaces(idx, /* backwards */ false); + } + // idx = self.skip_trailing_comment(idx); + idx = self.skip_newline(idx, /* backwards */ false); + idx.is_some_and(|idx| self.has_newline(idx, /* backwards */ false)) + // self.source_text[end as usize..].chars().nth(1).is_some_and(|c| c == '\n') + } + + fn skip_to_line_end(&self, start_index: Option) -> Option { + self.skip(start_index, false, |c| matches!(c, ' ' | '\t' | ',' | ';')) + } + + fn skip_spaces(&self, start_index: Option, backwards: bool) -> Option { + self.skip(start_index, backwards, |c| matches!(c, ' ' | '\t')) + } + + fn skip(&self, start_index: Option, backwards: bool, f: F) -> Option + where + F: Fn(char) -> bool, + { + let start_index = start_index?; + let mut index = start_index; + if backwards { + for c in self.source_text[..=start_index as usize].chars().rev() { + if !f(c) { + return Some(index); + } + index -= 1_u32; + } + } else { + for c in self.source_text[start_index as usize..].chars() { + if !f(c) { + return Some(index); + } + index += 1_u32; + } + } + None } #[allow(clippy::cast_possible_truncation)] @@ -154,26 +201,6 @@ impl<'a> Prettier<'a> { Some(start_index) } - fn skip_spaces(&self, start_index: u32, backwards: bool) -> Option { - let mut index = start_index; - if backwards { - for c in self.source_text[..=start_index as usize].chars().rev() { - if !matches!(c, ' ' | '\t') { - return Some(index); - } - index -= 1_u32; - } - } else { - for c in self.source_text[start_index as usize..].chars() { - if !matches!(c, ' ' | '\t') { - return Some(index); - } - index += 1_u32; - } - } - None - } - fn has_newline(&self, start_index: u32, backwards: bool) -> bool { if (backwards && start_index == 0) || (!backwards && start_index as usize == self.source_text.len()) @@ -181,15 +208,15 @@ impl<'a> Prettier<'a> { return false; } let start_index = if backwards { start_index - 1 } else { start_index }; - let idx = self.skip_spaces(start_index, backwards); + let idx = self.skip_spaces(Some(start_index), backwards); let idx2 = self.skip_newline(idx, backwards); idx != idx2 } fn is_previous_line_empty(&self, start_index: u32) -> bool { let idx = start_index - 1; - let idx = self.skip_spaces(idx, true); - let Some(idx) = self.skip_newline(idx, true) else { return false }; + let idx = self.skip_spaces(Some(idx), true); + let idx = self.skip_newline(idx, true); let idx = self.skip_spaces(idx, true); let idx2 = self.skip_newline(idx, true); idx != idx2 diff --git a/tasks/prettier_conformance/prettier.snap.md b/tasks/prettier_conformance/prettier.snap.md index 20dfa6cac..9da3dea2e 100644 --- a/tasks/prettier_conformance/prettier.snap.md +++ b/tasks/prettier_conformance/prettier.snap.md @@ -1,4 +1,4 @@ -Compatibility: 194/580 (33.45%) +Compatibility: 196/580 (33.79%) # Failed @@ -124,9 +124,6 @@ Compatibility: 194/580 (33.45%) * class-extends/extends.js * class-extends/tuple-and-record.js -### class-static-block -* class-static-block/class-static-block.js - ### classes * classes/assignment.js * classes/empty.js @@ -300,9 +297,6 @@ Compatibility: 194/580 (33.45%) * functional-composition/reselect_createselector.js * functional-composition/rxjs_pipe.js -### generator -* generator/anonymous.js - ### identifier/for-of * identifier/for-of/await.js * identifier/for-of/let.js