From 7d9d04c569ec2466adaac83057fcd6a1f652aaa1 Mon Sep 17 00:00:00 2001 From: Boshen Date: Mon, 27 Nov 2023 23:11:53 +0800 Subject: [PATCH] refactor(prettier): align `line` with prettier (#1559) This PR alines all the line types with prettier ``` const hardlineWithoutBreakParent = { type: DOC_TYPE_LINE, hard: true }; const literallineWithoutBreakParent = { type: DOC_TYPE_LINE, hard: true, literal: true, }; const line = { type: DOC_TYPE_LINE }; const softline = { type: DOC_TYPE_LINE, soft: true }; const hardline = [hardlineWithoutBreakParent, breakParent]; const literalline = [literallineWithoutBreakParent, breakParent]; ``` https://github.com/prettier/prettier/blob/101598f94f3647d24a479d8a6e8d04931c9c7625/src/document/builders.js#L165-L175 --- crates/oxc_prettier/src/comments/print.rs | 26 +-- crates/oxc_prettier/src/doc.rs | 65 +++++-- crates/oxc_prettier/src/format/array.rs | 16 +- crates/oxc_prettier/src/format/assignment.rs | 10 +- crates/oxc_prettier/src/format/binaryish.rs | 6 +- crates/oxc_prettier/src/format/block.rs | 15 +- .../src/format/call_expression.rs | 8 +- crates/oxc_prettier/src/format/class.rs | 11 +- crates/oxc_prettier/src/format/misc.rs | 4 +- crates/oxc_prettier/src/format/mod.rs | 151 ++++++++-------- crates/oxc_prettier/src/format/object.rs | 6 +- crates/oxc_prettier/src/format/statement.rs | 10 +- crates/oxc_prettier/src/format/ternary.rs | 6 +- crates/oxc_prettier/src/macros.rs | 6 +- crates/oxc_prettier/src/printer/command.rs | 17 +- crates/oxc_prettier/src/printer/mod.rs | 162 +++++++++--------- tasks/prettier_conformance/prettier.snap.md | 7 +- 17 files changed, 297 insertions(+), 229 deletions(-) diff --git a/crates/oxc_prettier/src/comments/print.rs b/crates/oxc_prettier/src/comments/print.rs index 3b014b7f1..9d6e4120d 100644 --- a/crates/oxc_prettier/src/comments/print.rs +++ b/crates/oxc_prettier/src/comments/print.rs @@ -4,7 +4,7 @@ use oxc_span::Span; use crate::{ array, doc::{Doc, DocBuilder, Separator}, - hardline, indent, line, ss, Prettier, + hardline, line, ss, Prettier, }; use super::{Comment, CommentFlags, DanglingCommentsPrintOptions}; @@ -60,18 +60,17 @@ impl<'a> Prettier<'a> { parts.push(printed); if comment.is_block { - let line_break = if self.has_newline(comment.end, /* backwards */ false) { + if self.has_newline(comment.end, /* backwards */ false) { if self.has_newline(comment.start, /* backwards */ true) { - hardline!() + parts.extend(hardline!()); } else { - line!() + parts.push(line!()); } } else { - ss!(" ") + parts.push(ss!(" ")); }; - parts.push(line_break); } else { - parts.push(hardline!()); + parts.extend(hardline!()); } if self @@ -79,7 +78,7 @@ impl<'a> Prettier<'a> { .and_then(|idx| self.skip_newline(Some(idx), false)) .is_some_and(|i| self.has_newline(i, /* backwards */ false)) { - parts.push(hardline!()); + parts.extend(hardline!()); } } @@ -122,9 +121,9 @@ impl<'a> Prettier<'a> { parts.push(printed); let suffix = { let mut parts = self.vec(); - parts.push(hardline!()); + parts.extend(hardline!()); if self.is_previous_line_empty(comment.start) { - parts.push(hardline!()); + parts.extend(hardline!()); } parts }; @@ -185,7 +184,12 @@ impl<'a> Prettier<'a> { } (!parts.is_empty()).then(|| Doc::Array(self.join(Separator::Hardline, parts))).map(|doc| { if dangling_options.is_some_and(|options| options.ident) { - indent!(self, hardline!(), doc) + Doc::Indent({ + let mut parts = self.vec(); + parts.extend(hardline!()); + parts.push(doc); + parts + }) } else { doc } diff --git a/crates/oxc_prettier/src/doc.rs b/crates/oxc_prettier/src/doc.rs index 8fd1b5662..adebd9c24 100644 --- a/crates/oxc_prettier/src/doc.rs +++ b/crates/oxc_prettier/src/doc.rs @@ -25,13 +25,7 @@ pub enum Doc<'a> { /// Specify a line break. /// If an expression fits on one line, the line break will be replaced with a space. /// Line breaks always indent the next line with the current level of indentation. - Line, - /// Specify a line break. - /// The difference from line is that if the expression fits on one line, it will be replaced with nothing. - Softline, - /// Specify a line break that is **always** included in the output, - /// no matter if the expression fits on one line or not. - Hardline, + Line(Line), /// This is used to implement trailing comments. /// It's not practical to constantly check where the line ends to avoid accidentally printing some code at the end of a comment. /// `lineSuffix` buffers docs passed to it and flushes them before any new line. @@ -46,6 +40,39 @@ pub enum Doc<'a> { BreakParent, } +#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)] +pub struct Line { + pub hard: bool, + pub soft: bool, + pub literal: bool, +} + +impl Line { + /// Specify a line break. + /// The difference from line is that if the expression fits on one line, it will be replaced with nothing. + pub fn softline() -> Self { + Self { soft: true, ..Self::default() } + } + + /// Specify a line break that is **always** included in the output, + /// no matter if the expression fits on one line or not. + pub fn hardline() -> Self { + Self { hard: true, ..Self::default() } + } + + pub fn literal_line() -> Self { + Self { literal: true, ..Self::default() } + } + + pub fn hardline_without_break_parent() -> Self { + Self { hard: true, ..Self::default() } + } + + pub fn literal_line_without_break_parent() -> Self { + Self { hard: true, literal: true, ..Self::default() } + } +} + #[derive(Debug)] pub struct Group<'a> { pub contents: Vec<'a, Doc<'a>>, @@ -72,11 +99,13 @@ impl<'a> Fill<'a> { pub fn new(docs: Vec<'a, Doc<'a>>) -> Self { Self { parts: docs } } + pub fn drain_out_pair(&mut self) -> (Option>, Option>) { let content = if self.parts.len() > 0 { Some(self.parts.remove(0)) } else { None }; let whitespace = if self.parts.len() > 0 { Some(self.parts.remove(0)) } else { None }; (content, whitespace) } + pub fn dequeue(&mut self) -> Option> { if self.parts.len() > 0 { Some(self.parts.remove(0)) @@ -87,9 +116,11 @@ impl<'a> Fill<'a> { pub fn enqueue(&mut self, doc: Doc<'a>) { self.parts.insert(0, doc); } + pub fn parts(&self) -> &[Doc<'a>] { &self.parts } + pub fn take_parts(self) -> Vec<'a, Doc<'a>> { self.parts } @@ -134,8 +165,8 @@ pub trait DocBuilder<'a> { for (i, doc) in docs.into_iter().enumerate() { if i != 0 { parts.push(match separator { - Separator::Softline => Doc::Softline, - Separator::Hardline => Doc::Hardline, + Separator::Softline => Doc::Line(Line::softline()), + Separator::Hardline => Doc::Line(Line::hardline()), Separator::CommaLine => array![self, ss!(","), line!()], }); } @@ -207,14 +238,14 @@ fn print_doc_to_debug(doc: &Doc<'_>) -> std::string::String { } string.push_str(" })"); } - Doc::Line => { - string.push_str("line"); - } - Doc::Softline => { - string.push_str("softline"); - } - Doc::Hardline => { - string.push_str("hardline"); + Doc::Line(Line { soft, hard, .. }) => { + if *soft { + string.push_str("softline"); + } else if *hard { + string.push_str("hardline"); + } else { + string.push_str("line"); + } } Doc::IfBreak(if_break) => { string.push_str(&format!( diff --git a/crates/oxc_prettier/src/format/array.rs b/crates/oxc_prettier/src/format/array.rs index fcfac9c61..f73de7914 100644 --- a/crates/oxc_prettier/src/format/array.rs +++ b/crates/oxc_prettier/src/format/array.rs @@ -6,7 +6,7 @@ use crate::{ array, comments::DanglingCommentsPrintOptions, doc::{Doc, DocBuilder, Fill, Group}, - group, if_break, indent, softline, ss, Prettier, + group, if_break, indent, line, softline, ss, Prettier, }; use super::Format; @@ -129,7 +129,7 @@ fn print_elements<'a>(p: &mut Prettier<'a>, array: &Array<'a, '_>) -> Doc<'a> { for (i, element) in array.elements.iter().enumerate() { if i > 0 && i < array.elements.len() { parts.push(ss!(",")); - parts.push(Doc::Line); + parts.push(line!()); } parts.push(element.format(p)); @@ -139,7 +139,7 @@ fn print_elements<'a>(p: &mut Prettier<'a>, array: &Array<'a, '_>) -> Doc<'a> { for (i, element) in tuple.element_types.iter().enumerate() { if i > 0 && i < tuple.element_types.len() { parts.push(ss!(",")); - parts.push(Doc::Line); + parts.push(line!()); } parts.push(element.format(p)); @@ -149,7 +149,7 @@ fn print_elements<'a>(p: &mut Prettier<'a>, array: &Array<'a, '_>) -> Doc<'a> { for (i, element) in array_pat.elements.iter().enumerate() { if i > 0 && i < array_pat.elements.len() { parts.push(ss!(",")); - parts.push(Doc::Line); + parts.push(line!()); } if let Some(binding_pat) = element { @@ -159,7 +159,7 @@ fn print_elements<'a>(p: &mut Prettier<'a>, array: &Array<'a, '_>) -> Doc<'a> { if let Some(rest) = &array_pat.rest { parts.push(ss!(",")); - parts.push(Doc::Line); + parts.push(line!()); parts.push(rest.format(p)); } } @@ -167,7 +167,7 @@ fn print_elements<'a>(p: &mut Prettier<'a>, array: &Array<'a, '_>) -> Doc<'a> { for (i, element) in array_pat.elements.iter().enumerate() { if i > 0 && i < array_pat.elements.len() { parts.push(ss!(",")); - parts.push(Doc::Line); + parts.push(line!()); } if let Some(binding_pat) = element { @@ -177,7 +177,7 @@ fn print_elements<'a>(p: &mut Prettier<'a>, array: &Array<'a, '_>) -> Doc<'a> { if let Some(rest) = &array_pat.rest { parts.push(ss!(",")); - parts.push(Doc::Line); + parts.push(line!()); parts.push(rest.format(p)); } } @@ -207,7 +207,7 @@ where parts.push(part); if !is_last { - parts.push(Doc::Line); + parts.push(line!()); } } } diff --git a/crates/oxc_prettier/src/format/assignment.rs b/crates/oxc_prettier/src/format/assignment.rs index 9a217bef2..73dcbb0df 100644 --- a/crates/oxc_prettier/src/format/assignment.rs +++ b/crates/oxc_prettier/src/format/assignment.rs @@ -9,7 +9,7 @@ use oxc_ast::{ use crate::{ array, doc::{Doc, DocBuilder}, - group, indent, ss, Format, Prettier, + group, indent, line, ss, Format, Prettier, }; pub(super) fn print_assignment_expression<'a>( @@ -60,7 +60,7 @@ fn print_assignment<'a>( match layout { Layout::BreakAfterOperator => { - group!(p, group!(p, left_doc), op, group!(p, indent!(p, Doc::Line, right_doc))) + group!(p, group!(p, left_doc), op, group!(p, indent!(p, line!(), right_doc))) } Layout::NeverBreakAfterOperator => { group!(p, group!(p, left_doc), op, ss!(" "), group!(p, right_doc)) @@ -71,7 +71,7 @@ fn print_assignment<'a>( p, group!(p, left_doc), op, - group!(p, indent!(p, Doc::Line)), + group!(p, indent!(p, line!())), // TODO: wrap `right_doc` in indent_if_break!() when we have support for group IDs. right_doc ) @@ -82,10 +82,10 @@ fn print_assignment<'a>( // Parts of assignment chains aren't wrapped in groups. // Once one of them breaks, the chain breaks too. Layout::Chain => { - array!(p, group!(p, left_doc), op, Doc::Line, right_doc) + array!(p, group!(p, left_doc), op, line!(), right_doc) } Layout::ChainTail => { - array!(p, group!(p, left_doc), op, indent!(p, Doc::Line, right_doc)) + array!(p, group!(p, left_doc), op, indent!(p, line!(), right_doc)) } Layout::ChainTailArrowChain => { array!(p, group!(p, left_doc), op, right_doc) diff --git a/crates/oxc_prettier/src/format/binaryish.rs b/crates/oxc_prettier/src/format/binaryish.rs index 502c29434..e1005c37b 100644 --- a/crates/oxc_prettier/src/format/binaryish.rs +++ b/crates/oxc_prettier/src/format/binaryish.rs @@ -3,7 +3,7 @@ use oxc_syntax::operator::{BinaryOperator, LogicalOperator}; use crate::{ doc::{Doc, DocBuilder, Group}, - group, ss, Format, Prettier, + group, line, ss, Format, Prettier, }; pub enum BinaryishLeft<'a, 'b> { @@ -77,11 +77,11 @@ pub(super) fn print_binaryish_expression<'a>( parts.push(ss!(" ")); if operator.is_binary() { - parts.push(group!(p, ss!(operator.as_str()), Doc::Line, right.format(p))); + parts.push(group!(p, ss!(operator.as_str()), line!(), right.format(p))); Doc::Group(Group::new(parts, false)) } else { parts.push(ss!(operator.as_str())); - parts.push(Doc::Line); + parts.push(line!()); parts.push(right.format(p)); Doc::Array(parts) } diff --git a/crates/oxc_prettier/src/format/block.rs b/crates/oxc_prettier/src/format/block.rs index 1f19ec16a..fb971bcb6 100644 --- a/crates/oxc_prettier/src/format/block.rs +++ b/crates/oxc_prettier/src/format/block.rs @@ -3,7 +3,7 @@ use oxc_ast::{ast::*, AstKind}; use super::{statement, Format}; use crate::{ doc::{Doc, DocBuilder}, - hardline, indent, ss, Prettier, + hardline, ss, Prettier, }; pub(super) fn print_block<'a>( @@ -14,8 +14,13 @@ pub(super) fn print_block<'a>( let mut parts = p.vec(); parts.push(ss!("{")); if let Some(doc) = print_block_body(p, stmts, directives, true, false) { - parts.push(indent![p, hardline!(), doc]); - parts.push(hardline!()); + parts.push({ + let mut parts = p.vec(); + parts.extend(hardline!()); + parts.push(doc); + Doc::Indent(parts) + }); + parts.extend(hardline!()); } else { let parent = p.parent_kind(); let parent_parent = p.parent_parent_kind(); @@ -35,7 +40,7 @@ pub(super) fn print_block<'a>( && !matches!(p.parent_parent_kind(), Some(AstKind::TryStatement(stmt)) if stmt.finalizer.is_some())) || matches!(p.current_kind(), AstKind::StaticBlock(_))) { - parts.push(hardline!()); + parts.extend(hardline!()); } } parts.push(ss!("}")); @@ -69,7 +74,7 @@ pub(super) fn print_block_body<'a>( } if is_root { - parts.push(hardline!()); + parts.extend(hardline!()); } Some(Doc::Array(parts)) diff --git a/crates/oxc_prettier/src/format/call_expression.rs b/crates/oxc_prettier/src/format/call_expression.rs index a3bef6101..91b3c1f38 100644 --- a/crates/oxc_prettier/src/format/call_expression.rs +++ b/crates/oxc_prettier/src/format/call_expression.rs @@ -5,7 +5,7 @@ use oxc_span::{GetSpan, Span}; use crate::{ doc::{Doc, DocBuilder, Group}, - if_break, ss, Format, Prettier, + if_break, line, softline, ss, Format, Prettier, }; pub(super) enum CallExpressionLike<'a, 'b> { @@ -94,14 +94,14 @@ fn print_call_expression_arguments<'a>( if i < arguments.len() - 1 { parts_inner.push(ss!(",")); - parts_inner.push(Doc::Line); + parts_inner.push(line!()); } } if should_break { - parts_inner.insert(0, Doc::Softline); + parts_inner.insert(0, softline!()); parts.push(Doc::Indent(parts_inner)); parts.push(if_break!(p, ",", "", None)); - parts.push(Doc::Softline); + parts.push(softline!()); } else { parts.extend(parts_inner); } diff --git a/crates/oxc_prettier/src/format/class.rs b/crates/oxc_prettier/src/format/class.rs index 30dd8c1f6..6140e1d66 100644 --- a/crates/oxc_prettier/src/format/class.rs +++ b/crates/oxc_prettier/src/format/class.rs @@ -3,7 +3,7 @@ use oxc_ast::ast::*; use crate::{ array, doc::{Doc, DocBuilder}, - hardline, indent, ss, Format, Prettier, + hardline, ss, Format, Prettier, }; pub(super) fn print_class<'a>(p: &mut Prettier<'a>, class: &Class<'a>) -> Doc<'a> { @@ -34,7 +34,14 @@ pub(super) fn print_class_body<'a>(p: &mut Prettier<'a>, class_body: &ClassBody< let mut parts = p.vec(); parts.push(ss!("{")); if !inner_parts.is_empty() { - parts.push(array![p, indent!(p, hardline!(), Doc::Array(inner_parts)), hardline!()]); + let indent = { + let mut parts = p.vec(); + parts.extend(hardline!()); + parts.push(Doc::Array(inner_parts)); + Doc::Indent(parts) + }; + parts.push(array![p, indent]); + parts.extend(hardline!()); } parts.push(ss!("}")); diff --git a/crates/oxc_prettier/src/format/misc.rs b/crates/oxc_prettier/src/format/misc.rs index 7c45e46a8..ac08f77a3 100644 --- a/crates/oxc_prettier/src/format/misc.rs +++ b/crates/oxc_prettier/src/format/misc.rs @@ -1,7 +1,7 @@ use oxc_ast::{ast::*, AstKind}; use oxc_span::Span; -use crate::{array, doc::Doc, indent, ss, Prettier}; +use crate::{array, doc::Doc, indent, line, ss, Prettier}; pub(super) fn adjust_clause<'a>( p: &Prettier<'a>, @@ -17,7 +17,7 @@ pub(super) fn adjust_clause<'a>( return array![p, ss!(" "), clause]; } - indent![p, Doc::Line, clause] + indent![p, line!(), clause] } pub(super) fn has_new_line_in_range(text: &str, start: u32, end: u32) -> bool { diff --git a/crates/oxc_prettier/src/format/mod.rs b/crates/oxc_prettier/src/format/mod.rs index d0af9209e..5be7e96ad 100644 --- a/crates/oxc_prettier/src/format/mod.rs +++ b/crates/oxc_prettier/src/format/mod.rs @@ -62,7 +62,7 @@ impl<'a> Format<'a> for Program<'a> { if let Some(hashbang) = &self.hashbang { parts.push(hashbang.format(p)); if p.is_next_line_empty(hashbang.span.end - 1) { - parts.push(hardline!()); + parts.extend(hardline!()); } } if let Some(doc) = block::print_block_body( @@ -94,7 +94,7 @@ impl<'a> Format<'a> for Directive { if p.options.semi { parts.push(ss!(";")); } - parts.push(hardline!()); + parts.extend(hardline!()); Doc::Array(parts) } } @@ -165,7 +165,11 @@ impl<'a> Format<'a> for IfStatement<'a> { if let Some(alternate) = &self.alternate { let else_on_same_line = matches!(alternate, Statement::BlockStatement(_)); - parts.push(if else_on_same_line { ss!(" ") } else { hardline!() }); + if else_on_same_line { + parts.push(ss!(" ")); + } else { + parts.extend(hardline!()); + } parts.push(ss!("else")); let alternate_doc = format!(p, alternate); parts.push(group!( @@ -349,7 +353,7 @@ impl<'a> Format<'a> for DoWhileStatement<'a> { if matches!(self.body, Statement::BlockStatement(_)) { parts.push(ss!(" ")); } else { - parts.push(hardline!()); + parts.extend(hardline!()); } parts.push(ss!("while (")); @@ -417,14 +421,19 @@ impl<'a> Format<'a> for SwitchStatement<'a> { let mut cases_parts = p.vec(); let len = self.cases.len(); for (i, case) in self.cases.iter().enumerate() { - cases_parts.push(indent!(p, hardline!(), format!(p, case))); + cases_parts.push({ + let mut parts = p.vec(); + parts.extend(hardline!()); + parts.push(format!(p, case)); + Doc::Indent(parts) + }); if i != len - 1 && p.is_next_line_empty(case.span.end) { - cases_parts.push(hardline!()); + cases_parts.extend(hardline!()); } } parts.extend(cases_parts); - parts.push(hardline!()); + parts.extend(hardline!()); parts.push(ss!("}")); Doc::Array(parts) @@ -459,11 +468,15 @@ 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) { - consequent_parts.push(hardline!()); + consequent_parts.extend(hardline!()); } } - consequent_parts.push(if is_only_one_block_statement { ss!(" ") } else { hardline!() }); + if is_only_one_block_statement { + consequent_parts.push(ss!(" ")); + } else { + consequent_parts.extend(hardline!()); + } consequent_parts.push(format!(p, stmt)); } @@ -618,7 +631,11 @@ impl<'a> Format<'a> for VariableDeclaration<'a> { let mut d_parts = p.vec(); if i != 0 { d_parts.push(p.str(",")); - d_parts.push(if is_hardline { hardline!() } else { line!() }); + if is_hardline { + d_parts.extend(hardline!()); + } else { + d_parts.push(line!()); + } } d_parts.push(decl.format(p)); Doc::Indent(d_parts) @@ -638,7 +655,7 @@ impl<'a> Format<'a> for VariableDeclaration<'a> { impl<'a> Format<'a> for UsingDeclaration<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } @@ -780,31 +797,31 @@ impl<'a> Format<'a> for TSVoidKeyword { impl<'a> Format<'a> for TSArrayType<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for TSConditionalType<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for TSConstructorType<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for TSFunctionType<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for TSImportType<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } @@ -827,7 +844,7 @@ impl<'a> Format<'a> for TSInferType<'a> { impl<'a> Format<'a> for TSIntersectionType<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } @@ -848,13 +865,13 @@ impl<'a> Format<'a> for TSLiteralType<'a> { impl<'a> Format<'a> for TSMappedType<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for TSQualifiedName<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } @@ -875,97 +892,97 @@ impl<'a> Format<'a> for TSTupleType<'a> { impl<'a> Format<'a> for TSTypeLiteral<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for TSTypeOperatorType<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for TSTypePredicate<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for TSTypeQuery<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for TSTypeReference<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for TSUnionType<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for JSDocNullableType<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for JSDocUnknownType { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for TSInterfaceDeclaration<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for TSEnumDeclaration<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for TSModuleDeclaration<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for TSImportEqualsDeclaration<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for TSTypeParameter<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for TSTypeParameterDeclaration<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for TSTypeParameterInstantiation<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for TSTupleElement<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } @@ -1065,13 +1082,13 @@ impl<'a> Format<'a> for ImportNamespaceSpecifier { impl<'a> Format<'a> for Option> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for ImportAttribute { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } @@ -1093,13 +1110,13 @@ impl<'a> Format<'a> for ExportNamedDeclaration<'a> { impl<'a> Format<'a> for TSExportAssignment<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for TSNamespaceExportDeclaration { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } @@ -1754,7 +1771,7 @@ impl<'a> Format<'a> for ImportExpression<'a> { if !self.arguments.is_empty() { for arg in &self.arguments { indent_parts.push(ss!(",")); - indent_parts.push(Doc::Line); + indent_parts.push(line!()); indent_parts.push(format!(p, arg)); } } @@ -1877,133 +1894,133 @@ impl<'a> Format<'a> for ClassElement<'a> { impl<'a> Format<'a> for JSXIdentifier { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for JSXMemberExpressionObject<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for JSXMemberExpression<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for JSXElementName<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for JSXNamespacedName { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for JSXAttributeName<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for JSXAttribute<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for JSXEmptyExpression { fn format(&self, _: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for JSXExpression<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for JSXExpressionContainer<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for JSXAttributeValue<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for JSXSpreadAttribute<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for JSXAttributeItem<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for JSXOpeningElement<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for JSXClosingElement<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for JSXElement<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for JSXOpeningFragment { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for JSXClosingFragment { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for JSXText { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for JSXSpreadChild<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for JSXChild<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for JSXFragment<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } @@ -2023,13 +2040,13 @@ impl<'a> Format<'a> for MethodDefinition<'a> { impl<'a> Format<'a> for PropertyDefinition<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for AccessorProperty<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } @@ -2120,18 +2137,18 @@ impl<'a> Format<'a> for RegExpFlags { impl<'a> Format<'a> for TSAbstractMethodDefinition<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for TSAbstractPropertyDefinition<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } impl<'a> Format<'a> for TSIndexSignature<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Line + line!() } } diff --git a/crates/oxc_prettier/src/format/object.rs b/crates/oxc_prettier/src/format/object.rs index a76f539dd..7087a8944 100644 --- a/crates/oxc_prettier/src/format/object.rs +++ b/crates/oxc_prettier/src/format/object.rs @@ -52,7 +52,7 @@ pub(super) fn print_object_properties<'a, F: Format<'a> + GetSpan>( indent_parts.push(prop.format(p)); if i < properties.len() - 1 { indent_parts.push(Doc::Str(",")); - indent_parts.push(Doc::Line); + indent_parts.push(line!()); } } @@ -60,9 +60,9 @@ pub(super) fn print_object_properties<'a, F: Format<'a> + GetSpan>( parts.push(if_break!(p, ",", "", None)); if p.options.bracket_spacing { - parts.push(Doc::Line); + parts.push(line!()); } else { - parts.push(Doc::Softline); + parts.push(softline!()); } parts.push(ss!("}")); diff --git a/crates/oxc_prettier/src/format/statement.rs b/crates/oxc_prettier/src/format/statement.rs index e499dcd3f..d80a8ece9 100644 --- a/crates/oxc_prettier/src/format/statement.rs +++ b/crates/oxc_prettier/src/format/statement.rs @@ -2,7 +2,7 @@ use oxc_allocator::Vec; use oxc_ast::ast::Statement; use crate::{ - doc::{Doc, DocBuilder, Group}, + doc::{Doc, DocBuilder, Group, Line}, hardline, Prettier, }; use oxc_span::GetSpan; @@ -31,7 +31,9 @@ pub(super) fn print_statement_sequence<'a>( if remove_last_statement_hardline && i == len - 1 { match docs { Doc::Array(ref mut docs) | Doc::Group(Group { contents: ref mut docs, .. }) => { - if matches!(docs.last(), Some(Doc::Hardline)) { + if docs.last().is_some_and( + |doc| matches!(doc, Doc::Line(line) if *line == Line::hardline()), + ) { docs.pop(); } } @@ -42,10 +44,10 @@ pub(super) fn print_statement_sequence<'a>( parts.push(docs); if i < len - 1 { - parts.push(hardline!()); + parts.extend(hardline!()); if p.is_next_line_empty(stmt.span().end) { - parts.push(hardline!()); + parts.extend(hardline!()); } } } diff --git a/crates/oxc_prettier/src/format/ternary.rs b/crates/oxc_prettier/src/format/ternary.rs index 8981a2171..c7b5d9a54 100644 --- a/crates/oxc_prettier/src/format/ternary.rs +++ b/crates/oxc_prettier/src/format/ternary.rs @@ -1,6 +1,6 @@ use oxc_ast::ast::*; -use crate::{doc::Doc, group, indent, ss, Format, Prettier}; +use crate::{doc::Doc, group, indent, line, ss, Format, Prettier}; pub(super) fn print_ternary<'a>(p: &mut Prettier<'a>, expr: &ConditionalExpression<'a>) -> Doc<'a> { group![ @@ -8,10 +8,10 @@ pub(super) fn print_ternary<'a>(p: &mut Prettier<'a>, expr: &ConditionalExpressi expr.test.format(p), indent!( p, - Doc::Line, + line!(), ss!("? "), expr.consequent.format(p), - Doc::Line, + line!(), ss!(": "), expr.alternate.format(p) ) diff --git a/crates/oxc_prettier/src/macros.rs b/crates/oxc_prettier/src/macros.rs index abdc3986d..5510ba9bf 100644 --- a/crates/oxc_prettier/src/macros.rs +++ b/crates/oxc_prettier/src/macros.rs @@ -52,21 +52,21 @@ macro_rules! indent_if_break { #[macro_export] macro_rules! line { () => { - Doc::Line + Doc::Line($crate::doc::Line::default()) }; } #[macro_export] macro_rules! softline { () => { - Doc::Softline + Doc::Line($crate::doc::Line::softline()) }; } #[macro_export] macro_rules! hardline { () => { - Doc::Hardline + [Doc::Line($crate::doc::Line::hardline()), Doc::BreakParent] }; } diff --git a/crates/oxc_prettier/src/printer/command.rs b/crates/oxc_prettier/src/printer/command.rs index 4ddf1cc54..2bc4be967 100644 --- a/crates/oxc_prettier/src/printer/command.rs +++ b/crates/oxc_prettier/src/printer/command.rs @@ -16,7 +16,7 @@ impl<'a> Command<'a> { } } -#[derive(Clone, Debug, Copy, Eq, PartialEq)] +#[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum Mode { Break, Flat, @@ -24,17 +24,26 @@ pub enum Mode { impl Mode { pub fn is_break(self) -> bool { - matches!(self, Self::Break) + self == Self::Break + } + + pub fn is_flat(self) -> bool { + self == Self::Flat } } -#[derive(Clone, Copy)] +#[derive(Debug, Clone, Copy)] pub struct Indent { + pub root: bool, pub length: usize, } impl Indent { pub fn root() -> Self { - Self { length: 0 } + Self { root: true, length: 0 } + } + + pub fn new(length: usize) -> Self { + Self { root: false, length } } } diff --git a/crates/oxc_prettier/src/printer/mod.rs b/crates/oxc_prettier/src/printer/mod.rs index 71f38475e..dc3f9bb75 100644 --- a/crates/oxc_prettier/src/printer/mod.rs +++ b/crates/oxc_prettier/src/printer/mod.rs @@ -9,7 +9,7 @@ use oxc_allocator::Allocator; use std::collections::{HashMap, VecDeque}; use crate::{ - doc::{Doc, DocBuilder, Fill, IfBreak}, + doc::{Doc, DocBuilder, Fill, IfBreak, Line}, GroupId, PrettierOptions, }; @@ -71,27 +71,6 @@ impl<'a> Printer<'a> { unsafe { String::from_utf8_unchecked(self.out) } } - /// Reference: - /// * https://github.com/prettier/prettier/blob/main/src/document/utils.js#L156-L185 - pub fn propagate_breaks(doc: &mut Doc<'_>) -> bool { - match doc { - Doc::Hardline => true, - Doc::Group(group) => { - let should_break = - group.contents.iter_mut().rev().any(|doc| Self::propagate_breaks(doc)); - if should_break { - group.should_break = should_break; - } - group.should_break - } - Doc::IfBreak(d) => Self::propagate_breaks(&mut d.break_contents), - Doc::Array(arr) | Doc::Indent(arr) | Doc::IndentIfBreak(arr) => { - arr.iter_mut().any(|doc| Self::propagate_breaks(doc)) - } - _ => false, - } - } - /// Turn Doc into a string /// /// Reference: @@ -105,13 +84,11 @@ impl<'a> Printer<'a> { Doc::Indent(docs) => self.handle_indent(indent, mode, docs), Doc::Group(_) => self.handle_group(indent, mode, doc), Doc::IndentIfBreak(docs) => self.handle_indent_if_break(indent, mode, docs), - Doc::Line => self.handle_line(indent, mode), - Doc::Softline => self.handle_softline(indent, mode), - Doc::Hardline => self.handle_line(indent, Mode::Break), + Doc::Line(line) => self.handle_line(line, indent, mode, doc), Doc::LineSuffix(docs) => self.handle_line_suffix(indent, mode, docs), Doc::IfBreak(if_break) => self.handle_if_break(if_break, indent, mode), Doc::Fill(fill) => self.handle_fill(indent, mode, fill), - Doc::BreakParent => {} // No op + Doc::BreakParent => { /* No op */ } } if self.cmds.is_empty() && !self.line_suffix.is_empty() { @@ -120,6 +97,11 @@ impl<'a> Printer<'a> { } } + #[allow(clippy::cast_possible_wrap)] + fn remaining_width(&self) -> isize { + (self.options.print_width as isize) - (self.pos as isize) + } + fn handle_str(&mut self, s: &str) { self.out.extend(s.as_bytes()); self.pos += s.len(); @@ -133,7 +115,7 @@ impl<'a> Printer<'a> { self.cmds.extend( docs.into_iter() .rev() - .map(|doc| Command::new(Indent { length: indent.length + 1 }, mode, doc)), + .map(|doc| Command::new(Indent::new(indent.length + 1), mode, doc)), ); } @@ -192,39 +174,46 @@ impl<'a> Printer<'a> { ); } Mode::Break => { - self.cmds.extend(docs.into_iter().rev().map(|doc| { - Command::new(Indent { length: indent.length + 1 }, Mode::Break, doc) - })); + self.cmds.extend( + docs.into_iter() + .rev() + .map(|doc| Command::new(Indent::new(indent.length + 1), Mode::Break, doc)), + ); } } } - fn handle_line(&mut self, indent: Indent, mode: Mode) { - if mode.is_break() { - if !self.line_suffix.is_empty() { - self.cmds.extend(self.line_suffix.drain(..).rev()); + fn handle_line(&mut self, line: Line, indent: Indent, mode: Mode, doc: Doc<'a>) { + if mode.is_flat() { + if line.hard { + // shouldRemeasure = true; + } else { + if !line.soft { + self.out.push(b' '); + self.pos += 1; + } return; } + } - self.handle_hardline(indent); + if !self.line_suffix.is_empty() { + self.cmds.push(Command::new(indent, mode, doc)); + self.cmds.extend(self.line_suffix.drain(..).rev()); + return; + } + + if line.literal { + self.out.extend(self.new_line.as_bytes()); + if !indent.root { + self.pos = 0; + } } else { - self.out.push(b' '); - self.pos += 1; + self.trim(); + self.out.extend(self.new_line.as_bytes()); + self.pos = self.indent(indent.length); } } - fn handle_softline(&mut self, indent: Indent, mode: Mode) { - if mode.is_break() { - self.handle_line(indent, Mode::Break); - } - } - - fn handle_hardline(&mut self, indent: Indent) { - self.trim(); - self.out.extend(self.new_line.as_bytes()); - self.pos = self.indent(indent.length); - } - fn handle_line_suffix( &mut self, indent: Indent, @@ -331,6 +320,27 @@ impl<'a> Printer<'a> { }; } + fn indent(&mut self, size: usize) -> usize { + if self.options.use_tabs { + self.out.extend("\t".repeat(size).as_bytes()); + size + } else { + let count = self.options.tab_width * size; + self.out.extend(" ".repeat(count).as_bytes()); + count + } + } + + fn trim(&mut self) { + while let Some(&last) = self.out.last() { + if last == b' ' || last == b'\t' { + self.out.pop(); + } else { + break; + } + } + } + #[allow(clippy::cast_possible_wrap)] fn fits(&self, next: &Command<'a>, width: isize) -> bool { let mut remaining_width = width; @@ -339,7 +349,6 @@ impl<'a> Printer<'a> { let mut cmds = self.cmds.iter().rev(); while let Some((mode, doc)) = queue.pop_front() { - let is_break = matches!(mode, Mode::Break); match doc { Doc::Str(string) => { remaining_width -= string.len() as isize; @@ -369,20 +378,14 @@ impl<'a> Printer<'a> { queue.push_front((mode, contents)); } - Doc::Line => { - if is_break { + Doc::Line(line) => { + if mode.is_break() || line.hard { return true; } - remaining_width -= 1_isize; - } - Doc::Softline => { - if is_break { - return true; + if !line.soft { + remaining_width -= 1_isize; } } - Doc::Hardline => { - return true; - } Doc::Fill(fill) => { for part in fill.parts().iter().rev() { queue.push_front((mode, part)); @@ -408,29 +411,24 @@ impl<'a> Printer<'a> { true } - fn indent(&mut self, size: usize) -> usize { - if self.options.use_tabs { - self.out.extend("\t".repeat(size).as_bytes()); - size - } else { - let count = self.options.tab_width * size; - self.out.extend(" ".repeat(count).as_bytes()); - count - } - } - - fn trim(&mut self) { - while let Some(&last) = self.out.last() { - if last == b' ' || last == b'\t' { - self.out.pop(); - } else { - break; + /// Reference: + /// * https://github.com/prettier/prettier/blob/main/src/document/utils.js#L156-L185 + pub fn propagate_breaks(doc: &mut Doc<'_>) -> bool { + match doc { + Doc::BreakParent => true, + Doc::Group(group) => { + let should_break = + group.contents.iter_mut().rev().any(|doc| Self::propagate_breaks(doc)); + if should_break { + group.should_break = should_break; + } + group.should_break } + Doc::IfBreak(d) => Self::propagate_breaks(&mut d.break_contents), + Doc::Array(arr) | Doc::Indent(arr) | Doc::IndentIfBreak(arr) => { + arr.iter_mut().any(|doc| Self::propagate_breaks(doc)) + } + _ => false, } } - - #[allow(clippy::cast_possible_wrap)] - fn remaining_width(&self) -> isize { - (self.options.print_width as isize) - (self.pos as isize) - } } diff --git a/tasks/prettier_conformance/prettier.snap.md b/tasks/prettier_conformance/prettier.snap.md index 4d6ac0821..61b7d45c5 100644 --- a/tasks/prettier_conformance/prettier.snap.md +++ b/tasks/prettier_conformance/prettier.snap.md @@ -1,8 +1,4 @@ -<<<<<<< HEAD -Compatibility: 173/591 (29.27%) -======= -Compatibility: 171/591 (28.93%) ->>>>>>> 169f7a38 (feat(prettier): add id on `IfBreak` and `Group`) +Compatibility: 177/591 (29.95%) # Failed @@ -179,7 +175,6 @@ Compatibility: 171/591 (28.93%) * comments/empty-statements.js * comments/export-and-import.js * comments/export.js -* comments/first-line.js * comments/function-declaration.js * comments/if.js * comments/issue-3532.js