From 3a1ef6a2306bb1217fd2ad0ebd236efae73bc63a Mon Sep 17 00:00:00 2001 From: leaysgur <6259812+leaysgur@users.noreply.github.com> Date: Tue, 26 Nov 2024 09:31:30 +0000 Subject: [PATCH] refactor(prettier): Refactor IR related macros (#7491) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Remove `format!` macro - Rename string related macros, `&'static`: `text!` and `'a`: `dynamic_text!` - Apply `wrap!` macro instead of manually using `enter|leave_node` - Introduce `/ir` directory and move `Doc`, `impl Display`, `DocBuilder`, etc. I'm not yet determined how to, how deep to use macro, but this is first stepping stone... 🥌 --- crates/oxc_prettier/src/comments/print.rs | 2 +- crates/oxc_prettier/src/doc.rs | 344 --------- crates/oxc_prettier/src/format/array.rs | 29 +- .../oxc_prettier/src/format/arrow_function.rs | 13 +- crates/oxc_prettier/src/format/assignment.rs | 10 +- crates/oxc_prettier/src/format/binaryish.rs | 9 +- crates/oxc_prettier/src/format/block.rs | 9 +- .../oxc_prettier/src/format/call_arguments.rs | 41 +- .../src/format/call_expression.rs | 8 +- crates/oxc_prettier/src/format/class.rs | 51 +- crates/oxc_prettier/src/format/function.rs | 46 +- .../src/format/function_parameters.rs | 23 +- crates/oxc_prettier/src/format/misc.rs | 4 +- crates/oxc_prettier/src/format/mod.rs | 701 +++++++++--------- crates/oxc_prettier/src/format/module.rs | 33 +- crates/oxc_prettier/src/format/object.rs | 17 +- crates/oxc_prettier/src/format/statement.rs | 5 +- .../src/format/template_literal.rs | 12 +- crates/oxc_prettier/src/format/ternary.rs | 6 +- crates/oxc_prettier/src/ir/builder.rs | 54 ++ crates/oxc_prettier/src/ir/display.rs | 132 ++++ crates/oxc_prettier/src/ir/doc.rs | 164 ++++ crates/oxc_prettier/src/ir/mod.rs | 6 + crates/oxc_prettier/src/lib.rs | 4 +- crates/oxc_prettier/src/macros.rs | 169 ++--- crates/oxc_prettier/src/needs_parens.rs | 12 +- crates/oxc_prettier/src/printer/command.rs | 2 +- crates/oxc_prettier/src/printer/mod.rs | 4 +- crates/oxc_prettier/src/utils/document.rs | 2 +- 29 files changed, 970 insertions(+), 942 deletions(-) delete mode 100644 crates/oxc_prettier/src/doc.rs create mode 100644 crates/oxc_prettier/src/ir/builder.rs create mode 100644 crates/oxc_prettier/src/ir/display.rs create mode 100644 crates/oxc_prettier/src/ir/doc.rs create mode 100644 crates/oxc_prettier/src/ir/mod.rs diff --git a/crates/oxc_prettier/src/comments/print.rs b/crates/oxc_prettier/src/comments/print.rs index 1988bef0dcd56..a8f5cb5025b2d 100644 --- a/crates/oxc_prettier/src/comments/print.rs +++ b/crates/oxc_prettier/src/comments/print.rs @@ -2,7 +2,7 @@ use oxc_allocator::Vec; use oxc_span::Span; use crate::{ - doc::{Doc, DocBuilder}, + ir::{Doc, DocBuilder}, Prettier, }; diff --git a/crates/oxc_prettier/src/doc.rs b/crates/oxc_prettier/src/doc.rs deleted file mode 100644 index 651e0922a5d37..0000000000000 --- a/crates/oxc_prettier/src/doc.rs +++ /dev/null @@ -1,344 +0,0 @@ -//! Prettier IR -//! -//! References: -//! * - -use std::fmt; - -use oxc_allocator::{Allocator, Box, String, Vec}; - -use crate::{array, line, ss, GroupId}; - -#[derive(Debug)] -pub enum Doc<'a> { - Str(&'a str), - // perf: can we use &[Doc] here? - Array(Vec<'a, Doc<'a>>), - /// Increase the level of indentation. - Indent(Vec<'a, Doc<'a>>), - IndentIfBreak(IndentIfBreak<'a>), - /// Mark a group of items which the printer should try to fit on one line. - /// This is the basic command to tell the printer when to break. - /// Groups are usually nested, and the printer will try to fit everything on one line, - /// but if it doesn't fit it will break the outermost group first and try again. - /// It will continue breaking groups until everything fits (or there are no more groups to break). - Group(Group<'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(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. - LineSuffix(Vec<'a, Doc<'a>>), - /// Print something if the current `group` or the current element of `fill` breaks and something else if it doesn't. - IfBreak(IfBreak<'a>), - /// This is an alternative type of group which behaves like text layout: - /// it's going to add a break whenever the next element doesn't fit in the line anymore. - /// The difference with `group` is that it's not going to break all the separators, just the ones that are at the end of lines. - Fill(Fill<'a>), - /// Include this anywhere to force all parent groups to break. - 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>>, - pub should_break: bool, - pub expanded_states: Option>>, - pub id: Option, -} - -impl<'a> Group<'a> { - pub fn new(contents: Vec<'a, Doc<'a>>) -> Self { - Self { contents, should_break: false, id: None, expanded_states: None } - } - - pub fn new_conditional_group( - contents: Vec<'a, Doc<'a>>, - expanded_states: Vec<'a, Doc<'a>>, - ) -> Self { - Self { contents, should_break: false, id: None, expanded_states: Some(expanded_states) } - } - - pub fn with_break(mut self, yes: bool) -> Self { - self.should_break = yes; - self - } - - pub fn with_id(mut self, id: GroupId) -> Self { - self.id = Some(id); - self - } -} -#[derive(Debug)] -pub struct IndentIfBreak<'a> { - pub contents: Vec<'a, Doc<'a>>, - pub group_id: Option, -} - -impl<'a> IndentIfBreak<'a> { - pub fn new(contents: Vec<'a, Doc<'a>>) -> Self { - Self { contents, group_id: None } - } - - pub fn with_id(mut self, id: GroupId) -> Self { - self.group_id = Some(id); - self - } -} - -#[derive(Debug)] -pub struct Fill<'a> { - pub parts: Vec<'a, Doc<'a>>, -} - -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)) - } else { - None - } - } - - 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 - } -} - -#[derive(Debug)] -pub struct IfBreak<'a> { - pub break_contents: Box<'a, Doc<'a>>, - pub flat_content: Box<'a, Doc<'a>>, - pub group_id: Option, -} - -#[derive(Clone, Copy)] -pub enum Separator { - #[allow(unused)] - Softline, - Hardline, - CommaLine, // [",", line] -} - -/// Doc Builder -pub trait DocBuilder<'a> { - fn allocator(&self) -> &'a Allocator; - #[inline] - fn vec(&self) -> Vec<'a, T> { - Vec::new_in(self.allocator()) - } - fn vec_single(&self, value: T) -> Vec<'a, T> { - let mut vec = Vec::with_capacity_in(1, self.allocator()); - vec.push(value); - vec - } - - #[inline] - fn str(&self, s: &str) -> Doc<'a> { - Doc::Str(String::from_str_in(s, self.allocator()).into_bump_str()) - } - - #[inline] - fn boxed(&self, doc: Doc<'a>) -> Box<'a, Doc<'a>> { - Box::new_in(doc, self.allocator()) - } - - fn join(&self, separator: Separator, docs: std::vec::Vec>) -> Vec<'a, Doc<'a>> { - let mut parts = self.vec(); - for (i, doc) in docs.into_iter().enumerate() { - if i != 0 { - parts.push(match separator { - Separator::Softline => Doc::Line(Line::softline()), - Separator::Hardline => Doc::Line(Line::hardline()), - Separator::CommaLine => array![self, ss!(","), line!()], - }); - } - parts.push(doc); - } - parts - } -} - -impl<'a> fmt::Display for Doc<'a> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - write!(f, "{}", print_doc_to_debug(self)) - } -} - -// https://github.com/prettier/prettier/blob/3.3.3/src/document/debug.js -fn print_doc_to_debug(doc: &Doc<'_>) -> std::string::String { - use std::string::String; - let mut string = String::new(); - match doc { - Doc::Str(s) => { - string.push('"'); - string.push_str(s); - string.push('"'); - } - Doc::Array(docs) => { - string.push_str("[\n"); - for (idx, doc) in docs.iter().enumerate() { - string.push_str(&print_doc_to_debug(doc)); - if idx != docs.len() - 1 { - string.push_str(", "); - } - } - string.push_str("]\n"); - } - Doc::Indent(contents) => { - string.push_str("indent(["); - for (idx, doc) in contents.iter().enumerate() { - string.push_str(&print_doc_to_debug(doc)); - if idx != contents.len() - 1 { - string.push_str(", "); - } - } - string.push_str("])"); - } - Doc::IndentIfBreak(indent_if_break) => { - string.push_str("indentIfBreak("); - string.push_str("[\n"); - for (idx, doc) in indent_if_break.contents.iter().enumerate() { - string.push_str(&print_doc_to_debug(doc)); - if idx != indent_if_break.contents.len() - 1 { - string.push_str(", "); - } - } - - if let Some(id) = indent_if_break.group_id { - string.push_str(&format!(", {{id: {id}}}")); - } - - string.push_str("])"); - } - Doc::Group(group) => { - if group.expanded_states.is_some() { - string.push_str("conditionalGroup([\n"); - } - - string.push_str("group([\n"); - for (idx, doc) in group.contents.iter().enumerate() { - string.push_str(&print_doc_to_debug(doc)); - if idx != group.contents.len() - 1 { - string.push_str(", "); - } - } - string.push_str("], { shouldBreak: "); - string.push_str(&group.should_break.to_string()); - if let Some(id) = group.id { - string.push_str(&format!(", id: {id}")); - } - string.push_str(" })"); - - if let Some(expanded_states) = &group.expanded_states { - string.push_str(",\n"); - for (idx, doc) in expanded_states.iter().enumerate() { - string.push_str(&print_doc_to_debug(doc)); - if idx != expanded_states.len() - 1 { - string.push_str(", "); - } - } - string.push_str("])"); - } - } - 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!( - "ifBreak({}, {}", - print_doc_to_debug(&if_break.break_contents), - print_doc_to_debug(&if_break.flat_content) - )); - if let Some(group_id) = if_break.group_id { - string.push_str(&format!(", {{ groupId: {group_id} }}")); - } - string.push(')'); - } - Doc::Fill(fill) => { - string.push_str("fill([\n"); - let parts = fill.parts(); - for (idx, doc) in parts.iter().enumerate() { - string.push_str(&print_doc_to_debug(doc)); - if idx != parts.len() - 1 { - string.push_str(", "); - } - } - string.push_str("])"); - } - Doc::LineSuffix(docs) => { - string.push_str("lineSuffix("); - for (idx, doc) in docs.iter().enumerate() { - string.push_str(&print_doc_to_debug(doc)); - if idx != docs.len() - 1 { - string.push_str(", "); - } - } - string.push(')'); - } - Doc::BreakParent => { - string.push_str("BreakParent"); - } - } - - string -} diff --git a/crates/oxc_prettier/src/format/array.rs b/crates/oxc_prettier/src/format/array.rs index 13b0000ef142c..f67acd319257a 100644 --- a/crates/oxc_prettier/src/format/array.rs +++ b/crates/oxc_prettier/src/format/array.rs @@ -6,8 +6,9 @@ use super::Format; use crate::{ array, comments::{CommentFlags, DanglingCommentsPrintOptions}, - doc::{Doc, DocBuilder, Fill, Group}, - group, hardline, if_break, line, softline, ss, Prettier, + group, hardline, if_break, + ir::{Doc, DocBuilder, Fill, Group}, + line, softline, text, Prettier, }; #[allow(clippy::enum_variant_names)] @@ -82,9 +83,9 @@ pub fn print_array<'a>(p: &mut Prettier<'a>, array: &Array<'a, '_>) -> Doc<'a> { let trailing_comma_fn = |p: &Prettier<'a>| { if !can_have_trailing_comma { - ss!("") + text!("") } else if needs_forced_trailing_comma { - ss!(",") + text!(",") } else if should_use_concise_formatting { if_break!(p, ",", "", Some(id)) } else { @@ -97,7 +98,7 @@ pub fn print_array<'a>(p: &mut Prettier<'a>, array: &Array<'a, '_>) -> Doc<'a> { parts.push(Doc::Group( Group::new({ let mut group = p.vec(); - group.push(ss!("[")); + group.push(text!("[")); group.push({ Doc::Indent({ let mut indent_parts = p.vec(); @@ -115,7 +116,7 @@ pub fn print_array<'a>(p: &mut Prettier<'a>, array: &Array<'a, '_>) -> Doc<'a> { }) }); group.push(softline!()); - group.push(ss!("]")); + group.push(text!("]")); group }) .with_break(should_break(array)) @@ -128,8 +129,8 @@ pub fn print_array<'a>(p: &mut Prettier<'a>, array: &Array<'a, '_>) -> Doc<'a> { fn print_empty_array_elements<'a>(p: &mut Prettier<'a>, array: &Array<'a, '_>) -> Doc<'a> { let dangling_options = DanglingCommentsPrintOptions::default().with_ident(true); p.print_dangling_comments(array.span(), Some(&dangling_options)).map_or_else( - || ss!("[]"), - |dangling_comments| group![p, ss!("["), dangling_comments, softline!(), ss!("]")], + || text!("[]"), + |dangling_comments| group![p, text!("["), dangling_comments, softline!(), text!("]")], ) } @@ -141,7 +142,7 @@ fn print_array_elements<'a>(p: &mut Prettier<'a>, array: &Array<'a, '_>) -> Doc< parts.push(element.format(p)); let is_last = i == array.elements.len() - 1; if !is_last { - parts.push(ss!(",")); + parts.push(text!(",")); parts.push(line!()); if !element.is_elision() && is_line_after_element_empty(p, element.span().end) { parts.push(softline!()); @@ -152,7 +153,7 @@ fn print_array_elements<'a>(p: &mut Prettier<'a>, array: &Array<'a, '_>) -> Doc< Array::TSTupleType(tuple) => { for (i, element) in tuple.element_types.iter().enumerate() { if i > 0 && i < tuple.element_types.len() { - parts.push(ss!(",")); + parts.push(text!(",")); parts.push(line!()); } @@ -169,7 +170,7 @@ fn print_array_elements<'a>(p: &mut Prettier<'a>, array: &Array<'a, '_>) -> Doc< if i == len - 1 && !has_rest { break; } - parts.push(ss!(",")); + parts.push(text!(",")); parts.push(line!()); } if let Some(rest) = &array_pat.rest { @@ -179,7 +180,7 @@ fn print_array_elements<'a>(p: &mut Prettier<'a>, array: &Array<'a, '_>) -> Doc< Array::ArrayAssignmentTarget(array_pat) => { for (i, element) in array_pat.elements.iter().enumerate() { if i > 0 && i < array_pat.elements.len() { - parts.push(ss!(",")); + parts.push(text!(",")); parts.push(line!()); } @@ -189,7 +190,7 @@ fn print_array_elements<'a>(p: &mut Prettier<'a>, array: &Array<'a, '_>) -> Doc< } if let Some(rest) = &array_pat.rest { - parts.push(ss!(",")); + parts.push(text!(",")); parts.push(line!()); parts.push(rest.format(p)); } @@ -215,7 +216,7 @@ where let part = if is_last { array!(p, element.format(p), trailing_comma_fn(p)) } else { - array!(p, element.format(p), ss!(",")) + array!(p, element.format(p), text!(",")) }; parts.push(part); diff --git a/crates/oxc_prettier/src/format/arrow_function.rs b/crates/oxc_prettier/src/format/arrow_function.rs index 77eb13fa19b20..09a813e18adc3 100644 --- a/crates/oxc_prettier/src/format/arrow_function.rs +++ b/crates/oxc_prettier/src/format/arrow_function.rs @@ -1,8 +1,9 @@ use oxc_ast::ast::*; use crate::{ - doc::{Doc, DocBuilder}, - group, ss, Format, Prettier, + group, + ir::{Doc, DocBuilder}, + text, Format, Prettier, }; pub(super) fn print_arrow_function<'a>( @@ -12,11 +13,11 @@ pub(super) fn print_arrow_function<'a>( let mut parts = p.vec(); if !p.options.semi && p.options.arrow_parens.is_always() { - parts.push(ss!(";")); + parts.push(text!(";")); } if expr.r#async { - parts.push(ss!("async ")); + parts.push(text!("async ")); } if let Some(type_params) = &expr.type_parameters { @@ -27,11 +28,11 @@ pub(super) fn print_arrow_function<'a>( parts.push(group!(p, parameters)); if let Some(return_type) = &expr.return_type { - parts.push(ss!(": ")); + parts.push(text!(": ")); parts.push(return_type.type_annotation.format(p)); } - parts.push(ss!(" => ")); + parts.push(text!(" => ")); if expr.expression { let stmt = &expr.body.statements[0]; diff --git a/crates/oxc_prettier/src/format/assignment.rs b/crates/oxc_prettier/src/format/assignment.rs index 6d8898d39723c..8ff62513a9a33 100644 --- a/crates/oxc_prettier/src/format/assignment.rs +++ b/crates/oxc_prettier/src/format/assignment.rs @@ -10,9 +10,9 @@ use oxc_ast::{ use super::{binaryish::should_inline_logical_expression, class::ClassMemberish}; use crate::{ - array, - doc::{Doc, DocBuilder, Group, IndentIfBreak}, - group, indent, line, space, Format, Prettier, + array, group, indent, + ir::{Doc, DocBuilder, Group, IndentIfBreak}, + line, space, text, Format, Prettier, }; pub(super) fn print_assignment_expression<'a>( @@ -24,7 +24,7 @@ pub(super) fn print_assignment_expression<'a>( p, AssignmentLikeNode::AssignmentExpression(assignment_expr), left_doc, - array![p, space!(), Doc::Str(assignment_expr.operator.as_str())], + array![p, space!(), text!(assignment_expr.operator.as_str())], Some(&assignment_expr.right), ) } @@ -38,7 +38,7 @@ pub(super) fn print_variable_declarator<'a>( p, AssignmentLikeNode::VariableDeclarator(variable_declarator), left_doc, - Doc::Str(" ="), + text!(" ="), variable_declarator.init.as_ref(), ) } diff --git a/crates/oxc_prettier/src/format/binaryish.rs b/crates/oxc_prettier/src/format/binaryish.rs index 461dd1c9de7b6..d5f625260d80c 100644 --- a/crates/oxc_prettier/src/format/binaryish.rs +++ b/crates/oxc_prettier/src/format/binaryish.rs @@ -5,8 +5,9 @@ use oxc_span::GetSpan; use crate::{ binaryish::BinaryishOperator, comments::CommentFlags, - doc::{Doc, DocBuilder, Group}, - group, line, space, ss, Format, Prettier, + group, + ir::{Doc, DocBuilder, Group}, + line, space, text, Format, Prettier, }; pub(super) fn print_binaryish_expression<'a>( @@ -92,7 +93,7 @@ fn print_binaryish_expressions<'a>( let right = if should_inline { let mut parts = p.vec(); - parts.push(ss!(operator.as_str())); + parts.push(text!(operator.as_str())); parts.push(space!()); parts.push(right.format(p)); parts @@ -101,7 +102,7 @@ fn print_binaryish_expressions<'a>( if line_before_operator { parts.push(line!()); } - parts.push(ss!(operator.as_str())); + parts.push(text!(operator.as_str())); parts.push(if line_before_operator { space!() } else { line!() }); parts.push(right.format(p)); parts diff --git a/crates/oxc_prettier/src/format/block.rs b/crates/oxc_prettier/src/format/block.rs index 3f1bd1f5b6ce3..758bcb560245a 100644 --- a/crates/oxc_prettier/src/format/block.rs +++ b/crates/oxc_prettier/src/format/block.rs @@ -2,8 +2,9 @@ use oxc_ast::{ast::*, AstKind}; use super::{statement, Format}; use crate::{ - doc::{Doc, DocBuilder}, - hardline, ss, Prettier, + hardline, + ir::{Doc, DocBuilder}, + text, Prettier, }; pub(super) fn print_block<'a>( @@ -12,7 +13,7 @@ pub(super) fn print_block<'a>( directives: Option<&[Directive<'a>]>, ) -> Doc<'a> { let mut parts = p.vec(); - parts.push(ss!("{")); + parts.push(text!("{")); if let Some(doc) = print_block_body(p, stmts, directives, true, false) { parts.push({ let mut parts = p.vec(); @@ -45,7 +46,7 @@ pub(super) fn print_block<'a>( parts.extend(hardline!()); } } - parts.push(ss!("}")); + parts.push(text!("}")); Doc::Array(parts) } diff --git a/crates/oxc_prettier/src/format/call_arguments.rs b/crates/oxc_prettier/src/format/call_arguments.rs index 3845dda8baf8b..b7688f65e7926 100644 --- a/crates/oxc_prettier/src/format/call_arguments.rs +++ b/crates/oxc_prettier/src/format/call_arguments.rs @@ -8,9 +8,9 @@ use super::{ misc, }; use crate::{ - array, conditional_group, - doc::{Doc, DocBuilder, Group}, - group_break, hardline, if_break, indent, line, softline, ss, + array, conditional_group, group_break, hardline, if_break, indent, + ir::{Doc, DocBuilder, Group}, + line, softline, text, utils::will_break, Format, Prettier, }; @@ -20,7 +20,7 @@ pub fn print_call_arguments<'a>( expression: &CallExpressionLike<'a, '_>, ) -> Doc<'a> { let mut parts = p.vec(); - parts.push(ss!("(")); + parts.push(text!("(")); let callee = expression.callee(); let arguments = expression.arguments(); @@ -32,7 +32,7 @@ pub fn print_call_arguments<'a>( if arguments.is_empty() { parts.extend(p.print_inner_comment(Span::new(callee.span().end, expression.span().end))); - parts.push(ss!(")")); + parts.push(text!(")")); return Doc::Array(parts); } @@ -60,7 +60,7 @@ pub fn print_call_arguments<'a>( arg.push(doc); if i < len - 1 { - arg.push(ss!(",")); + arg.push(text!(",")); if p.is_next_line_empty(element.span()) { arg.extend(hardline!()); arg.extend(hardline!()); @@ -75,15 +75,15 @@ pub fn print_call_arguments<'a>( let all_args_broken_out = |p: &mut Prettier<'a>| { let mut parts = p.vec(); - parts.push(ss!("(")); + parts.push(text!("(")); parts.push(indent!( p, line!(), Doc::Array(get_printed_arguments(p, 0)), - if p.should_print_all_comma() { ss!(",") } else { ss!("") } + if p.should_print_all_comma() { text!(",") } else { text!("") } )); parts.push(line!()); - parts.push(ss!(")")); + parts.push(text!(")")); Doc::Group(Group::new(parts).with_break(true)) }; @@ -99,7 +99,14 @@ pub fn print_call_arguments<'a>( Doc::BreakParent, conditional_group!( p, - array!(p, ss!("("), group_break!(p, first_doc), ss!(", "), last_doc, ss!(")")), + array!( + p, + text!("("), + group_break!(p, first_doc), + text!(", "), + last_doc, + text!(")") + ), all_args_broken_out(p) ) ]; @@ -113,7 +120,7 @@ pub fn print_call_arguments<'a>( } if !printed_arguments.is_empty() { - printed_arguments.push(ss!(",")); + printed_arguments.push(text!(",")); printed_arguments.push(line!()); } @@ -134,10 +141,10 @@ pub fn print_call_arguments<'a>( p, array!( p, - ss!("("), + text!("("), Doc::Array(printed_arguments), group_break!(p, last_doc), - ss!(")") + text!(")") ), all_args_broken_out(p) ), @@ -146,13 +153,13 @@ pub fn print_call_arguments<'a>( return conditional_group!( p, - array!(p, ss!("("), Doc::Array(printed_arguments), last_doc, ss!(")")), + array!(p, text!("("), Doc::Array(printed_arguments), last_doc, text!(")")), array!( p, - ss!("("), + text!("("), Doc::Array(get_printed_arguments(p, -1)), group_break!(p, get_last_doc(p)), - ss!(")") + text!(")") ), all_args_broken_out(p) ); @@ -168,7 +175,7 @@ pub fn print_call_arguments<'a>( } else { parts.extend(printed_arguments); } - parts.push(ss!(")")); + parts.push(text!(")")); let should_break = should_break && arguments.iter().any(|arg| { diff --git a/crates/oxc_prettier/src/format/call_expression.rs b/crates/oxc_prettier/src/format/call_expression.rs index 7837edbcc403c..10a9339b5e211 100644 --- a/crates/oxc_prettier/src/format/call_expression.rs +++ b/crates/oxc_prettier/src/format/call_expression.rs @@ -4,8 +4,8 @@ use oxc_span::{GetSpan, Span}; use super::call_arguments::print_call_arguments; use crate::{ - doc::{Doc, DocBuilder, Group}, - ss, Format, Prettier, + ir::{Doc, DocBuilder, Group}, + text, Format, Prettier, }; pub(super) enum CallExpressionLike<'a, 'b> { @@ -65,7 +65,7 @@ pub(super) fn print_call_expression<'a>( let mut parts = p.vec(); if expression.is_new() { - parts.push(ss!("new ")); + parts.push(text!("new ")); }; parts.push(expression.callee().format(p)); @@ -75,7 +75,7 @@ pub(super) fn print_call_expression<'a>( } if expression.optional() { - parts.push(ss!("?.")); + parts.push(text!("?.")); } parts.push(print_call_arguments(p, expression)); diff --git a/crates/oxc_prettier/src/format/class.rs b/crates/oxc_prettier/src/format/class.rs index 35cd449b1d298..a81720ba5060f 100644 --- a/crates/oxc_prettier/src/format/class.rs +++ b/crates/oxc_prettier/src/format/class.rs @@ -6,9 +6,10 @@ use oxc_span::GetSpan; use super::assignment::AssignmentLikeNode; use crate::{ array, - doc::{Doc, DocBuilder, Group, IfBreak}, format::{assignment, Separator}, - group, hardline, indent, line, softline, space, ss, Format, Prettier, + group, hardline, indent, + ir::{Doc, DocBuilder, Group, IfBreak}, + line, softline, space, text, Format, Prettier, }; pub(super) fn print_class<'a>(p: &mut Prettier<'a>, class: &Class<'a>) -> Doc<'a> { @@ -25,7 +26,7 @@ pub(super) fn print_class<'a>(p: &mut Prettier<'a>, class: &Class<'a>) -> Doc<'a if let Some(super_class) = &class.super_class { let mut extend_parts = p.vec(); - extend_parts.push(ss!("extends ")); + extend_parts.push(text!("extends ")); extend_parts.push(super_class.format(p)); if let Some(super_type_parameters) = &class.super_type_parameters { @@ -44,20 +45,20 @@ pub(super) fn print_class<'a>(p: &mut Prettier<'a>, class: &Class<'a>) -> Doc<'a heritage_clauses_parts.push(print_heritage_clauses_implements(p, class)); for decorator in &class.decorators { - parts.push(ss!("@")); + parts.push(text!("@")); parts.push(decorator.expression.format(p)); parts.extend(hardline!()); } if class.declare { - parts.push(ss!("declare ")); + parts.push(text!("declare ")); } if class.r#abstract { - parts.push(ss!("abstract ")); + parts.push(text!("abstract ")); } - parts.push(ss!("class ")); + parts.push(text!("class ")); if let Some(id) = &class.id { group_parts.push(id.format(p)); @@ -101,7 +102,7 @@ pub(super) fn print_class_body<'a>(p: &mut Prettier<'a>, class_body: &ClassBody< && node.is_property() && should_print_semicolon_after_class_property(node, class_body.body.get(i + 1)) { - parts_inner.push(ss!(";")); + parts_inner.push(text!(";")); } if i < class_body.body.len() - 1 { @@ -116,7 +117,7 @@ pub(super) fn print_class_body<'a>(p: &mut Prettier<'a>, class_body: &ClassBody< // TODO: if there are any dangling comments, print them let mut parts = p.vec(); - parts.push(ss!("{")); + parts.push(text!("{")); if !parts_inner.is_empty() { let indent = { let mut parts = p.vec(); @@ -128,7 +129,7 @@ pub(super) fn print_class_body<'a>(p: &mut Prettier<'a>, class_body: &ClassBody< parts.extend(hardline!()); } - parts.push(ss!("}")); + parts.push(text!("}")); Doc::Array(parts) } @@ -225,8 +226,8 @@ impl<'a, 'b> ClassMemberish<'a, 'b> { fn format_accessibility(&self, p: &mut Prettier<'a>) -> Option> { match self { - ClassMemberish::AccessorProperty(def) => def.accessibility.map(|v| ss!(v.as_str())), - ClassMemberish::PropertyDefinition(def) => def.accessibility.map(|v| ss!(v.as_str())), + ClassMemberish::AccessorProperty(def) => def.accessibility.map(|v| text!(v.as_str())), + ClassMemberish::PropertyDefinition(def) => def.accessibility.map(|v| text!(v.as_str())), } } @@ -250,7 +251,7 @@ pub(super) fn print_class_property<'a>( if let Some(decarators) = node.decorators() { for decorator in decarators { - parts.push(ss!("@")); + parts.push(text!("@")); parts.push(decorator.expression.format(p)); parts.extend(hardline!()); } @@ -262,35 +263,35 @@ pub(super) fn print_class_property<'a>( } if node.is_declare() { - parts.push(ss!("declare ")); + parts.push(text!("declare ")); } if node.is_static() { - parts.push(ss!("static ")); + parts.push(text!("static ")); } if node.is_abstract() { - parts.push(ss!("abstract ")); + parts.push(text!("abstract ")); } if node.is_override() { - parts.push(ss!("override ")); + parts.push(text!("override ")); } if node.is_readonly() { - parts.push(ss!("readonly ")); + parts.push(text!("readonly ")); } parts.push(node.format_key(p)); if node.is_optional() { - parts.push(ss!("?")); + parts.push(text!("?")); } else if node.is_definite() { - parts.push(ss!("!")); + parts.push(text!("!")); } if let Some(type_annotation) = node.format_type_annotation(p) { - parts.push(ss!(": ")); + parts.push(text!(": ")); parts.push(type_annotation); } @@ -300,12 +301,12 @@ pub(super) fn print_class_property<'a>( ClassMemberish::AccessorProperty(v) => AssignmentLikeNode::AccessorProperty(v), }; let mut result = - assignment::print_assignment(p, node, Doc::Array(parts), Doc::Str(" ="), right_expr); + assignment::print_assignment(p, node, Doc::Array(parts), text!(" ="), right_expr); if p.options.semi { let mut parts = p.vec(); parts.push(result); - parts.push(ss!(";")); + parts.push(text!(";")); result = Doc::Array(parts); } result @@ -389,7 +390,7 @@ fn print_heritage_clauses_implements<'a>(p: &mut Prettier<'a>, class: &Class<'a> if should_indent_only_heritage_clauses(class) { parts.push(Doc::IfBreak(IfBreak { - flat_content: p.boxed(ss!("")), + flat_content: p.boxed(text!("")), break_contents: p.boxed(line!()), group_id: None, // ToDo - how to attach group id })); @@ -399,7 +400,7 @@ fn print_heritage_clauses_implements<'a>(p: &mut Prettier<'a>, class: &Class<'a> parts.push(softline!()); } - parts.push(ss!("implements ")); + parts.push(text!("implements ")); let implements_docs = implements.iter().map(|v| v.format(p)).collect(); diff --git a/crates/oxc_prettier/src/format/function.rs b/crates/oxc_prettier/src/format/function.rs index 6c509ffa9bd91..202cff434e7d9 100644 --- a/crates/oxc_prettier/src/format/function.rs +++ b/crates/oxc_prettier/src/format/function.rs @@ -1,9 +1,11 @@ use oxc_ast::ast::*; use crate::{ - doc::{Doc, DocBuilder}, + dynamic_text, format::function_parameters::should_group_function_parameters, - group, if_break, indent, softline, space, ss, Format, Prettier, + group, if_break, indent, + ir::{Doc, DocBuilder}, + softline, space, text, Format, Prettier, }; pub(super) fn print_function<'a>( @@ -14,26 +16,26 @@ pub(super) fn print_function<'a>( let mut parts = p.vec(); if func.declare { - parts.push(ss!("declare ")); + parts.push(text!("declare ")); } if func.r#async { - parts.push(ss!("async ")); + parts.push(text!("async ")); } if let Some(name) = property_name { - parts.push(p.str(name)); + parts.push(dynamic_text!(p, name)); } else { - parts.push(ss!("function")); + parts.push(text!("function")); if func.generator { - parts.push(ss!("*")); + parts.push(text!("*")); } - parts.push(p.str(" ")); + parts.push(text!(" ")); } if let Some(id) = &func.id { - parts.push(p.str(id.name.as_str())); + parts.push(dynamic_text!(p, id.name.as_str())); } if let Some(type_params) = &func.type_parameters { @@ -49,7 +51,7 @@ pub(super) fn print_function<'a>( })); if let Some(return_type) = &func.return_type { - parts.push(ss!(": ")); + parts.push(text!(": ")); parts.push(return_type.type_annotation.format(p)); } @@ -70,44 +72,44 @@ pub(super) fn print_method<'a>(p: &mut Prettier<'a>, method: &MethodDefinition<' let mut parts = p.vec(); if let Some(accessibility) = &method.accessibility { - parts.push(ss!(accessibility.as_str())); + parts.push(text!(accessibility.as_str())); parts.push(space!()); } if method.r#static { - parts.push(ss!("static ")); + parts.push(text!("static ")); } if matches!(method.r#type, MethodDefinitionType::TSAbstractMethodDefinition) { - parts.push(ss!("abstract ")); + parts.push(text!("abstract ")); } if method.r#override { - parts.push(ss!("override ")); + parts.push(text!("override ")); } match method.kind { MethodDefinitionKind::Constructor | MethodDefinitionKind::Method => {} MethodDefinitionKind::Get => { - parts.push(ss!("get ")); + parts.push(text!("get ")); } MethodDefinitionKind::Set => { - parts.push(ss!("set ")); + parts.push(text!("set ")); } } if method.value.r#async { - parts.push(ss!("async ")); + parts.push(text!("async ")); } if method.value.generator { - parts.push(ss!("*")); + parts.push(text!("*")); } parts.push(method.key.format(p)); if method.optional { - parts.push(ss!("?")); + parts.push(text!("?")); } parts.push(print_method_value(p, &method.value)); @@ -129,7 +131,7 @@ fn print_method_value<'a>(p: &mut Prettier<'a>, function: &Function<'a>) -> Doc< parts.push(group!(p, parameters_doc)); if let Some(ret_typ) = &function.return_type { - parts.push(ss!(": ")); + parts.push(text!(": ")); parts.push(ret_typ.type_annotation.format(p)); } @@ -137,7 +139,7 @@ fn print_method_value<'a>(p: &mut Prettier<'a>, function: &Function<'a>) -> Doc< parts.push(space!()); parts.push(body.format(p)); } else if p.options.semi { - parts.push(ss!(";")); + parts.push(text!(";")); } Doc::Array(parts) @@ -150,7 +152,7 @@ pub(super) fn print_return_or_throw_argument<'a>( ) -> Doc<'a> { let mut parts = p.vec(); - parts.push(ss!(if is_return { "return" } else { "throw" })); + parts.push(text!(if is_return { "return" } else { "throw" })); if let Some(argument) = argument { parts.push(space!()); diff --git a/crates/oxc_prettier/src/format/function_parameters.rs b/crates/oxc_prettier/src/format/function_parameters.rs index fa273d3ad9da5..34ced557a1d4c 100644 --- a/crates/oxc_prettier/src/format/function_parameters.rs +++ b/crates/oxc_prettier/src/format/function_parameters.rs @@ -2,8 +2,9 @@ use oxc_ast::{ast::*, AstKind}; use crate::{ comments::CommentFlags, - doc::{Doc, DocBuilder, Group}, - hardline, if_break, indent, line, softline, space, ss, Format, Prettier, + hardline, if_break, indent, + ir::{Doc, DocBuilder, Group}, + line, softline, space, text, Format, Prettier, }; pub(super) fn should_hug_the_only_function_parameter( @@ -66,7 +67,7 @@ pub(super) fn print_function_parameters<'a>( let need_parens = !is_arrow_function || p.options.arrow_parens.is_always() || params.items.len() != 1; if need_parens { - parts.push(ss!("(")); + parts.push(text!("(")); } let should_hug_the_only_function_parameter = should_hug_the_only_function_parameter(p, params); @@ -80,7 +81,7 @@ pub(super) fn print_function_parameters<'a>( parts.push(this_param.format(p)); if params.items.len() > 0 { - printed.push(ss!(",")); + printed.push(text!(",")); if should_hug_the_only_function_parameter { printed.push(space!()); @@ -96,23 +97,23 @@ pub(super) fn print_function_parameters<'a>( for (i, param) in params.items.iter().enumerate() { if let Some(accessibility) = ¶m.accessibility { - printed.push(ss!(accessibility.as_str())); + printed.push(text!(accessibility.as_str())); printed.push(space!()); } if param.r#override { - printed.push(ss!("override ")); + printed.push(text!("override ")); } if param.readonly { - printed.push(ss!("readonly ")); + printed.push(text!("readonly ")); } printed.push(param.format(p)); if i == len - 1 && !has_rest { break; } - printed.push(ss!(",")); + printed.push(text!(",")); if should_hug_the_only_function_parameter { printed.push(space!()); } else if p.is_next_line_empty(param.span) { @@ -128,9 +129,9 @@ pub(super) fn print_function_parameters<'a>( if should_hug_the_only_function_parameter { let mut array = p.vec(); - array.push(ss!("(")); + array.push(text!("(")); array.extend(printed); - array.push(ss!(")")); + array.push(text!(")")); return Doc::Array(array); } @@ -144,7 +145,7 @@ pub(super) fn print_function_parameters<'a>( parts.push(if_break!(p, if skip_dangling_comma { "" } else { "," })); parts.push(softline!()); if need_parens { - parts.push(ss!(")")); + parts.push(text!(")")); } if p.args.expand_first_arg { diff --git a/crates/oxc_prettier/src/format/misc.rs b/crates/oxc_prettier/src/format/misc.rs index de96a26322802..ec276b91b7476 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, line, space, ss, DocBuilder, Prettier}; +use crate::{array, indent, ir::Doc, line, space, text, DocBuilder, Prettier}; pub(super) fn adjust_clause<'a>( p: &Prettier<'a>, @@ -10,7 +10,7 @@ pub(super) fn adjust_clause<'a>( force_space: bool, ) -> Doc<'a> { if matches!(node, Statement::EmptyStatement(_)) { - return ss!(";"); + return text!(";"); } if matches!(node, Statement::BlockStatement(_)) || force_space { diff --git a/crates/oxc_prettier/src/format/mod.rs b/crates/oxc_prettier/src/format/mod.rs index cd5eec5376728..46afb7b3e8ed5 100644 --- a/crates/oxc_prettier/src/format/mod.rs +++ b/crates/oxc_prettier/src/format/mod.rs @@ -32,11 +32,12 @@ use oxc_ast::{ast::*, AstKind}; use oxc_span::GetSpan; use oxc_syntax::identifier::{is_identifier_name, is_line_terminator}; -use self::{array::Array, object::ObjectLike, template_literal::TemplateLiteralPrinter}; use crate::{ - array, - doc::{Doc, DocBuilder, Group, Separator}, - format, group, hardline, indent, line, softline, space, ss, string, wrap, Prettier, + array, dynamic_text, + format::{array::Array, object::ObjectLike, template_literal::TemplateLiteralPrinter}, + group, hardline, indent, + ir::{Doc, DocBuilder, Group, Separator}, + line, softline, space, text, wrap, Prettier, }; pub trait Format<'a> { @@ -56,29 +57,32 @@ where impl<'a> Format<'a> for Program<'a> { #[allow(clippy::cast_possible_truncation)] fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - p.enter_node(AstKind::Program(p.alloc(self))); - let mut parts = p.vec(); - if let Some(hashbang) = &self.hashbang { - parts.push(hashbang.format(p)); - } - if let Some(doc) = block::print_block_body( - p, - &self.body, - Some(&self.directives), - false, - /* is_root */ true, - ) { - parts.push(doc); - } - p.leave_node(); - Doc::Array(parts) + wrap!(p, self, Program, { + let mut parts = p.vec(); + + if let Some(hashbang) = &self.hashbang { + parts.push(hashbang.format(p)); + } + + if let Some(doc) = block::print_block_body( + p, + &self.body, + Some(&self.directives), + false, + /* is_root */ true, + ) { + parts.push(doc); + } + + Doc::Array(parts) + }) } } impl<'a> Format<'a> for Hashbang<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let mut parts = p.vec(); - parts.push(ss!(self.span.source_text(p.source_text))); + parts.push(dynamic_text!(p, self.span.source_text(p.source_text))); parts.extend(hardline!()); // Preserve original newline if let Some(c) = p.source_text[self.span.end as usize..].chars().nth(1) { @@ -93,11 +97,10 @@ impl<'a> Format<'a> for Hashbang<'a> { impl<'a> Format<'a> for Directive<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let mut parts = p.vec(); - parts.push(Doc::Str(string::print_string( + parts.push(dynamic_text!( p, - self.directive.as_str(), - p.options.single_quote, - ))); + string::print_string(p, self.directive.as_str(), p.options.single_quote,) + )); if let Some(semi) = p.semi() { parts.push(semi); } @@ -148,7 +151,7 @@ impl<'a> Format<'a> for ExpressionStatement<'a> { impl<'a> Format<'a> for EmptyStatement { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Str("") + text!("") } } @@ -157,15 +160,15 @@ impl<'a> Format<'a> for IfStatement<'a> { wrap!(p, self, IfStatement, { let mut parts = p.vec(); - let test_doc = format!(p, self.test); - let consequent = format!(p, self.consequent); + let test_doc = self.test.format(p); + let consequent = self.consequent.format(p); let consequent = misc::adjust_clause(p, &self.consequent, consequent, false); let opening = group![ p, - ss!("if ("), + text!("if ("), group!(p, indent!(p, softline!(), test_doc), softline!()), - ss!(")"), + text!(")"), consequent ]; parts.push(opening); @@ -177,8 +180,8 @@ impl<'a> Format<'a> for IfStatement<'a> { } else { parts.extend(hardline!()); } - parts.push(ss!("else")); - let alternate_doc = format!(p, alternate); + parts.push(text!("else")); + let alternate_doc = alternate.format(p); parts.push(group!( p, misc::adjust_clause( @@ -204,33 +207,33 @@ impl<'a> Format<'a> for BlockStatement<'a> { impl<'a> Format<'a> for ForStatement<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { wrap!(p, self, ForStatement, { - let body = format!(p, self.body); + let body = self.body.format(p); let body = misc::adjust_clause(p, &self.body, body, false); if self.init.is_none() && self.test.is_none() && self.update.is_none() { - return group![p, ss!("for (;;)"), body]; + return group![p, text!("for (;;)"), body]; } let parts_head = { let mut parts_head = p.vec(); parts_head.push(softline!()); if let Some(init) = &self.init { - parts_head.push(format!(p, init)); + parts_head.push(init.format(p)); } - parts_head.push(ss!(";")); + parts_head.push(text!(";")); parts_head.push(line!()); if let Some(init) = &self.test { - parts_head.push(format!(p, init)); + parts_head.push(init.format(p)); } - parts_head.push(ss!(";")); + parts_head.push(text!(";")); parts_head.push(line!()); if let Some(init) = &self.update { - parts_head.push(format!(p, init)); + parts_head.push(init.format(p)); } Doc::Indent(parts_head) }; - group![p, ss!("for ("), group![p, parts_head, softline!()], ss!(")"), body] + group![p, text!("for ("), group![p, parts_head, softline!()], text!(")"), body] }) } } @@ -248,12 +251,12 @@ impl<'a> Format<'a> for ForInStatement<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { wrap!(p, self, ForInStatement, { let mut parts = p.vec(); - parts.push(ss!("for (")); - parts.push(format!(p, self.left)); - parts.push(ss!(" in ")); - parts.push(format!(p, self.right)); - parts.push(ss!(")")); - let body = format!(p, self.body); + parts.push(text!("for (")); + parts.push(self.left.format(p)); + parts.push(text!(" in ")); + parts.push(self.right.format(p)); + parts.push(text!(")")); + let body = self.body.format(p); parts.push(misc::adjust_clause(p, &self.body, body, false)); Doc::Group(Group::new(parts)) }) @@ -264,16 +267,16 @@ impl<'a> Format<'a> for ForOfStatement<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { wrap!(p, self, ForOfStatement, { let mut parts = p.vec(); - parts.push(ss!("for")); + parts.push(text!("for")); if self.r#await { - parts.push(ss!(" await")); + parts.push(text!(" await")); } - parts.push(ss!(" (")); - parts.push(format!(p, self.left)); - parts.push(ss!(" of ")); - parts.push(format!(p, self.right)); - parts.push(ss!(")")); - let body = format!(p, self.body); + parts.push(text!(" (")); + parts.push(self.left.format(p)); + parts.push(text!(" of ")); + parts.push(self.right.format(p)); + parts.push(text!(")")); + let body = self.body.format(p); parts.push(misc::adjust_clause(p, &self.body, body, false)); Doc::Group(Group::new(parts)) }) @@ -294,11 +297,11 @@ impl<'a> Format<'a> for WhileStatement<'a> { wrap!(p, self, WhileStatement, { let mut parts = p.vec(); - parts.push(ss!("while (")); - parts.push(group!(p, indent!(p, softline!(), format!(p, self.test)), softline!())); - parts.push(ss!(")")); + parts.push(text!("while (")); + parts.push(group!(p, indent!(p, softline!(), self.test.format(p)), softline!())); + parts.push(text!(")")); - let body = format!(p, self.body); + let body = self.body.format(p); parts.push(misc::adjust_clause(p, &self.body, body, false)); Doc::Group(Group::new(parts)) @@ -311,9 +314,9 @@ impl<'a> Format<'a> for DoWhileStatement<'a> { wrap!(p, self, DoWhileStatement, { let mut parts = p.vec(); - let clause = format!(p, self.body); + let clause = self.body.format(p); let clause = misc::adjust_clause(p, &self.body, clause, false); - let do_body = group!(p, ss!("do"), clause); + let do_body = group!(p, text!("do"), clause); parts.push(do_body); @@ -323,9 +326,9 @@ impl<'a> Format<'a> for DoWhileStatement<'a> { parts.extend(hardline!()); } - parts.push(ss!("while (")); - parts.push(group!(p, indent!(p, softline!(), format!(p, self.test)), softline!())); - parts.push(ss!(")")); + parts.push(text!("while (")); + parts.push(group!(p, indent!(p, softline!(), self.test.format(p)), softline!())); + parts.push(text!(")")); if let Some(semi) = p.semi() { parts.push(semi); } @@ -338,11 +341,11 @@ impl<'a> Format<'a> for DoWhileStatement<'a> { impl<'a> Format<'a> for ContinueStatement<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let mut parts = p.vec(); - parts.push(ss!("continue")); + parts.push(text!("continue")); if let Some(label) = &self.label { parts.push(space!()); - parts.push(format!(p, label)); + parts.push(label.format(p)); } Doc::Array(parts) @@ -352,15 +355,15 @@ impl<'a> Format<'a> for ContinueStatement<'a> { impl<'a> Format<'a> for BreakStatement<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let mut parts = p.vec(); - parts.push(ss!("break")); + parts.push(text!("break")); if let Some(label) = &self.label { parts.push(space!()); - parts.push(format!(p, label)); + parts.push(label.format(p)); } if p.options.semi { - parts.push(Doc::Str(";")); + parts.push(text!(";")); } Doc::Array(parts) @@ -374,16 +377,16 @@ impl<'a> Format<'a> for SwitchStatement<'a> { let mut header_parts = p.vec(); - header_parts.push(ss!("switch (")); + header_parts.push(text!("switch (")); - header_parts.push(indent!(p, softline!(), format!(p, self.discriminant))); + header_parts.push(indent!(p, softline!(), self.discriminant.format(p))); header_parts.push(softline!()); - header_parts.push(ss!(")")); + header_parts.push(text!(")")); parts.push(Doc::Group(Group::new(header_parts))); - parts.push(ss!(" {")); + parts.push(text!(" {")); let mut cases_parts = p.vec(); let len = self.cases.len(); @@ -391,7 +394,7 @@ impl<'a> Format<'a> for SwitchStatement<'a> { cases_parts.push({ let mut parts = p.vec(); parts.extend(hardline!()); - parts.push(format!(p, case)); + parts.push(case.format(p)); Doc::Indent(parts) }); if i != len - 1 && p.is_next_line_empty(case.span) { @@ -401,7 +404,7 @@ impl<'a> Format<'a> for SwitchStatement<'a> { parts.extend(cases_parts); parts.extend(hardline!()); - parts.push(ss!("}")); + parts.push(text!("}")); Doc::Array(parts) }) @@ -413,11 +416,11 @@ impl<'a> Format<'a> for SwitchCase<'a> { let mut parts = p.vec(); if let Some(test) = &self.test { - parts.push(ss!("case ")); - parts.push(format!(p, test)); - parts.push(ss!(":")); + parts.push(text!("case ")); + parts.push(test.format(p)); + parts.push(text!(":")); } else { - parts.push(ss!("default:")); + parts.push(text!("default:")); } let consequent: Vec<_> = Vec::from_iter_in( @@ -444,7 +447,7 @@ impl<'a> Format<'a> for SwitchCase<'a> { } else { consequent_parts.extend(hardline!()); } - consequent_parts.push(format!(p, stmt)); + consequent_parts.push(stmt.format(p)); } if !consequent_parts.is_empty() { @@ -470,29 +473,31 @@ impl<'a> Format<'a> for ReturnStatement<'a> { impl<'a> Format<'a> for LabeledStatement<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { if matches!(self.body, Statement::EmptyStatement(_)) { - return array!(p, self.label.format(p), ss!(":;")); + return array!(p, self.label.format(p), text!(":;")); } - array!(p, self.label.format(p), ss!(": "), format!(p, self.body)) + array!(p, self.label.format(p), text!(": "), self.body.format(p)) } } impl<'a> Format<'a> for TryStatement<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - p.enter_node(AstKind::TryStatement(p.alloc(self))); - let mut parts = p.vec(); - parts.push(ss!("try ")); - parts.push(format!(p, self.block)); - if let Some(handler) = &self.handler { - parts.push(space!()); - parts.push(format!(p, handler)); - } - if let Some(finalizer) = &self.finalizer { - parts.push(ss!(" finally ")); - parts.push(format!(p, finalizer)); - } - p.leave_node(); - Doc::Array(parts) + wrap!(p, self, TryStatement, { + let mut parts = p.vec(); + + parts.push(text!("try ")); + parts.push(self.block.format(p)); + if let Some(handler) = &self.handler { + parts.push(space!()); + parts.push(handler.format(p)); + } + if let Some(finalizer) = &self.finalizer { + parts.push(text!(" finally ")); + parts.push(finalizer.format(p)); + } + + Doc::Array(parts) + }) } } @@ -501,13 +506,13 @@ impl<'a> Format<'a> for CatchClause<'a> { wrap!(p, self, CatchClause, { let mut parts = p.vec(); - parts.push(ss!("catch ")); + parts.push(text!("catch ")); if let Some(param) = &self.param { - parts.push(ss!("(")); - parts.push(format!(p, param.pattern)); - parts.push(ss!(") ")); + parts.push(text!("(")); + parts.push(param.pattern.format(p)); + parts.push(text!(") ")); } - parts.push(format!(p, self.body)); + parts.push(self.body.format(p)); Doc::Array(parts) }) @@ -525,9 +530,9 @@ impl<'a> Format<'a> for WithStatement<'a> { let body_doc = self.body.format(p); group![ p, - ss!("with ("), - format!(p, self.object), - ss!(")"), + text!("with ("), + self.object.format(p), + text!(")"), misc::adjust_clause(p, &self.body, body_doc, false) ] } @@ -536,10 +541,10 @@ impl<'a> Format<'a> for WithStatement<'a> { impl<'a> Format<'a> for DebuggerStatement { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let mut parts = p.vec(); - parts.push(Doc::Str("debugger")); + parts.push(text!("debugger")); if p.options.semi { - parts.push(Doc::Str(";")); + parts.push(text!(";")); } Doc::Array(parts) @@ -590,10 +595,10 @@ impl<'a> Format<'a> for VariableDeclaration<'a> { let mut parts = p.vec(); if self.declare { - parts.push(ss!("declare ")); + parts.push(text!("declare ")); } - parts.push(ss!(kind)); + parts.push(text!(kind)); parts.push(space!()); let is_hardline = !p.parent_kind().is_iteration_statement() @@ -603,7 +608,7 @@ impl<'a> Format<'a> for VariableDeclaration<'a> { if decls_len > 1 { let mut d_parts = p.vec(); if i != 0 { - d_parts.push(p.str(",")); + d_parts.push(text!(",")); if is_hardline { d_parts.extend(hardline!()); } else { @@ -639,18 +644,18 @@ impl<'a> Format<'a> for TSTypeAliasDeclaration<'a> { let mut parts = p.vec(); if self.declare { - parts.push(ss!("declare ")); + parts.push(text!("declare ")); } - parts.push(ss!("type ")); - parts.push(format!(p, self.id)); + parts.push(text!("type ")); + parts.push(self.id.format(p)); if let Some(params) = &self.type_parameters { parts.push(params.format(p)); } - parts.push(ss!(" = ")); - parts.push(format!(p, self.type_annotation)); + parts.push(text!(" = ")); + parts.push(self.type_annotation.format(p)); if let Some(semi) = p.semi() { parts.push(semi); @@ -707,91 +712,91 @@ impl<'a> Format<'a> for TSType<'a> { impl<'a> Format<'a> for TSAnyKeyword { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Str("any") + text!("any") } } impl<'a> Format<'a> for TSBigIntKeyword { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Str("bigint") + text!("bigint") } } impl<'a> Format<'a> for TSBooleanKeyword { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Str("boolean") + text!("boolean") } } impl<'a> Format<'a> for TSIntrinsicKeyword { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Str("intrinsic") + text!("intrinsic") } } impl<'a> Format<'a> for TSNeverKeyword { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Str("never") + text!("never") } } impl<'a> Format<'a> for TSNullKeyword { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Str("null") + text!("null") } } impl<'a> Format<'a> for TSNumberKeyword { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Str("number") + text!("number") } } impl<'a> Format<'a> for TSObjectKeyword { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Str("object") + text!("object") } } impl<'a> Format<'a> for TSStringKeyword { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Str("string") + text!("string") } } impl<'a> Format<'a> for TSSymbolKeyword { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Str("symbol") + text!("symbol") } } impl<'a> Format<'a> for TSThisType { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Str("this") + text!("this") } } impl<'a> Format<'a> for TSUndefinedKeyword { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Str("undefined") + text!("undefined") } } impl<'a> Format<'a> for TSUnknownKeyword { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Str("unknown") + text!("unknown") } } impl<'a> Format<'a> for TSVoidKeyword { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Str("void") + text!("void") } } impl<'a> Format<'a> for TSArrayType<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - array![p, self.element_type.format(p), ss!("[]")] + array![p, self.element_type.format(p), text!("[]")] } } @@ -800,11 +805,11 @@ impl<'a> Format<'a> for TSConditionalType<'a> { let mut parts = p.vec(); parts.push(self.check_type.format(p)); - parts.push(ss!(" extends ")); + parts.push(text!(" extends ")); parts.push(self.extends_type.format(p)); - parts.push(ss!(" ? ")); + parts.push(text!(" ? ")); parts.push(self.true_type.format(p)); - parts.push(ss!(" : ")); + parts.push(text!(" : ")); parts.push(self.false_type.format(p)); Doc::Array(parts) @@ -815,11 +820,11 @@ impl<'a> Format<'a> for TSConstructorType<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let mut parts = p.vec(); if self.r#abstract { - parts.push(ss!("abstract ")); + parts.push(text!("abstract ")); } - parts.push(ss!("new ")); + parts.push(text!("new ")); parts.push(self.params.format(p)); - parts.push(array![p, ss!(" => "), self.return_type.type_annotation.format(p)]); + parts.push(array![p, text!(" => "), self.return_type.type_annotation.format(p)]); Doc::Array(parts) } } @@ -834,7 +839,7 @@ impl<'a> Format<'a> for TSFunctionType<'a> { parts.push(self.params.format(p)); - parts.push(ss!(" => ")); + parts.push(text!(" => ")); parts.push(self.return_type.type_annotation.format(p)); Doc::Array(parts) @@ -844,10 +849,10 @@ impl<'a> Format<'a> for TSFunctionType<'a> { impl<'a> Format<'a> for TSThisParameter<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let mut parts = p.vec(); - parts.push(ss!("this")); + parts.push(text!("this")); if let Some(type_annotation) = &self.type_annotation { - parts.push(ss!(": ")); + parts.push(text!(": ")); parts.push(type_annotation.type_annotation.format(p)); } @@ -860,16 +865,16 @@ impl<'a> Format<'a> for TSImportType<'a> { let mut parts = p.vec(); if self.is_type_of { - parts.push(ss!("typeof ")); + parts.push(text!("typeof ")); } - parts.push(ss!("import(")); + parts.push(text!("import(")); parts.push(self.parameter.format(p)); // ToDo: attributes - parts.push(ss!(")")); + parts.push(text!(")")); if let Some(qualifier) = &self.qualifier { - parts.push(ss!(".")); + parts.push(text!(".")); parts.push(qualifier.format(p)); } @@ -884,17 +889,17 @@ impl<'a> Format<'a> for TSImportType<'a> { impl<'a> Format<'a> for TSIndexedAccessType<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let mut parts = p.vec(); - parts.push(format!(p, self.object_type)); - parts.push(ss!("[")); - parts.push(format!(p, self.index_type)); - parts.push(ss!("]")); + parts.push(self.object_type.format(p)); + parts.push(text!("[")); + parts.push(self.index_type.format(p)); + parts.push(text!("]")); Doc::Array(parts) } } impl<'a> Format<'a> for TSInferType<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - array!(p, ss!("infer "), format!(p, self.type_parameter)) + array!(p, text!("infer "), self.type_parameter.format(p)) } } @@ -905,7 +910,7 @@ impl<'a> Format<'a> for TSIntersectionType<'a> { for ts_type in &self.types { if add_symbol { - parts.push(ss!(" & ")); + parts.push(text!(" & ")); } else { add_symbol = true; } @@ -937,40 +942,40 @@ impl<'a> Format<'a> for TSMappedType<'a> { let mut parts: Vec<'_, Doc<'_>> = p.vec(); match self.readonly { - TSMappedTypeModifierOperator::Plus => parts.push(ss!("+readonly ")), - TSMappedTypeModifierOperator::Minus => parts.push(ss!("-readonly ")), - TSMappedTypeModifierOperator::True => parts.push(ss!("readonly ")), + TSMappedTypeModifierOperator::Plus => parts.push(text!("+readonly ")), + TSMappedTypeModifierOperator::Minus => parts.push(text!("-readonly ")), + TSMappedTypeModifierOperator::True => parts.push(text!("readonly ")), TSMappedTypeModifierOperator::None => (), } - parts.push(ss!("[")); + parts.push(text!("[")); parts.push(self.type_parameter.format(p)); if let Some(name_type) = &self.name_type { - parts.push(ss!(" as ")); + parts.push(text!(" as ")); parts.push(name_type.format(p)); } - parts.push(ss!("]")); + parts.push(text!("]")); match self.optional { - TSMappedTypeModifierOperator::Plus => parts.push(ss!("+?")), - TSMappedTypeModifierOperator::Minus => parts.push(ss!("-?")), - TSMappedTypeModifierOperator::True => parts.push(ss!("?")), + TSMappedTypeModifierOperator::Plus => parts.push(text!("+?")), + TSMappedTypeModifierOperator::Minus => parts.push(text!("-?")), + TSMappedTypeModifierOperator::True => parts.push(text!("?")), TSMappedTypeModifierOperator::None => (), } if let Some(type_annotation) = &self.type_annotation { - parts.push(ss!(": ")); + parts.push(text!(": ")); parts.push(type_annotation.format(p)); } let mut result = p.vec(); - result.push(ss!("{ ")); + result.push(text!("{ ")); // ToDo: check ident/grouping in method/method-signature.ts result.push(Doc::Group(Group::new(parts))); - result.push(ss!(" }")); + result.push(text!(" }")); Doc::Array(result) } @@ -983,10 +988,10 @@ impl<'a> Format<'a> for TSNamedTupleMember<'a> { parts.push(self.label.format(p)); if self.optional { - parts.push(ss!("?")); + parts.push(text!("?")); } - parts.push(ss!(": ")); + parts.push(text!(": ")); parts.push(self.element_type.format(p)); Doc::Array(parts) @@ -995,19 +1000,19 @@ impl<'a> Format<'a> for TSNamedTupleMember<'a> { impl<'a> Format<'a> for TSRestType<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - array!(p, ss!("..."), self.type_annotation.format(p)) + array!(p, text!("..."), self.type_annotation.format(p)) } } impl<'a> Format<'a> for TSOptionalType<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - array!(p, self.type_annotation.format(p), ss!("?")) + array!(p, self.type_annotation.format(p), text!("?")) } } impl<'a> Format<'a> for TSQualifiedName<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - array!(p, self.left.format(p), ss!("."), self.right.format(p)) + array!(p, self.left.format(p), text!("."), self.right.format(p)) } } @@ -1036,7 +1041,7 @@ impl<'a> Format<'a> for TSTypeOperator<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let mut parts = p.vec(); - parts.push(ss!(self.operator.to_str())); + parts.push(text!(self.operator.to_str())); parts.push(space!()); parts.push(self.type_annotation.format(p)); @@ -1049,12 +1054,12 @@ impl<'a> Format<'a> for TSTypePredicate<'a> { let mut parts = p.vec(); if self.asserts { - parts.push(ss!("asserts ")); + parts.push(text!("asserts ")); } parts.push(self.parameter_name.format(p)); if let Some(type_annotation) = &self.type_annotation { - parts.push(ss!(" is ")); + parts.push(text!(" is ")); parts.push(type_annotation.type_annotation.format(p)); } @@ -1075,7 +1080,7 @@ impl<'a> Format<'a> for TSTypeQuery<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let mut parts = p.vec(); - parts.push(ss!("typeof ")); + parts.push(text!("typeof ")); match &self.expr_name { TSTypeQueryExprName::TSImportType(import_type) => parts.push(import_type.format(p)), @@ -1098,9 +1103,9 @@ impl<'a> Format<'a> for TSTypeQuery<'a> { impl<'a> Format<'a> for TSTypeReference<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let mut parts = p.vec(); - parts.push(format!(p, self.type_name)); + parts.push(self.type_name.format(p)); if let Some(params) = &self.type_parameters { - parts.push(format!(p, params)); + parts.push(params.format(p)); } Doc::Array(parts) } @@ -1119,7 +1124,7 @@ impl<'a> Format<'a> for TSUnionType<'a> { for ts_type in &self.types { if add_symbol { - parts.push(ss!(" | ")); + parts.push(text!(" | ")); } else { add_symbol = true; } @@ -1154,11 +1159,11 @@ impl<'a> Format<'a> for TSInterfaceDeclaration<'a> { let mut parts = p.vec(); if self.declare { - parts.push(ss!("declare ")); + parts.push(text!("declare ")); } - parts.push(ss!("interface ")); - parts.push(format!(p, self.id)); + parts.push(text!("interface ")); + parts.push(self.id.format(p)); if let Some(type_parameters) = &self.type_parameters { parts.push(type_parameters.format(p)); @@ -1171,11 +1176,11 @@ impl<'a> Format<'a> for TSInterfaceDeclaration<'a> { let mut extends_parts = p.vec(); let mut display_comma = false; - extends_parts.push(ss!("extends ")); + extends_parts.push(text!("extends ")); for extend in extends { if display_comma { - extends_parts.push(ss!(", ")); + extends_parts.push(text!(", ")); } else { display_comma = true; } @@ -1191,12 +1196,12 @@ impl<'a> Format<'a> for TSInterfaceDeclaration<'a> { } } - parts.push(ss!("{")); + parts.push(text!("{")); if self.body.body.len() > 0 { let mut indent_parts = p.vec(); for sig in &self.body.body { indent_parts.extend(hardline!()); - indent_parts.push(format!(p, sig)); + indent_parts.push(sig.format(p)); if let Some(semi) = p.semi() { indent_parts.push(semi); @@ -1205,7 +1210,7 @@ impl<'a> Format<'a> for TSInterfaceDeclaration<'a> { parts.push(Doc::Indent(indent_parts)); parts.extend(hardline!()); } - parts.push(ss!("}")); + parts.push(text!("}")); Doc::Array(parts) } } @@ -1214,14 +1219,14 @@ impl<'a> Format<'a> for TSEnumDeclaration<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let mut parts = p.vec(); if self.declare { - parts.push(ss!("declare ")); + parts.push(text!("declare ")); } if self.r#const { - parts.push(ss!("const ")); + parts.push(text!("const ")); } - parts.push(ss!("enum ")); + parts.push(text!("enum ")); parts.push(self.id.format(p)); - parts.push(ss!(" {")); + parts.push(text!(" {")); if self.members.len() > 0 { let mut indent_parts = p.vec(); for member in &self.members { @@ -1231,7 +1236,7 @@ impl<'a> Format<'a> for TSEnumDeclaration<'a> { parts.push(Doc::Indent(indent_parts)); parts.extend(hardline!()); } - parts.push(ss!("}")); + parts.push(text!("}")); Doc::Array(parts) } @@ -1243,11 +1248,11 @@ impl<'a> Format<'a> for TSEnumMember<'a> { parts.push(self.id.format(p)); if let Some(initializer) = &self.initializer { - parts.push(ss!(" = ")); + parts.push(text!(" = ")); parts.push(initializer.format(p)); } - parts.push(ss!(",")); + parts.push(text!(",")); Doc::Array(parts) } @@ -1267,13 +1272,13 @@ impl<'a> Format<'a> for TSModuleDeclaration<'a> { let mut parts = p.vec(); if self.declare { - parts.push(ss!("declare ")); + parts.push(text!("declare ")); } - parts.push(ss!(self.kind.as_str())); + parts.push(text!(self.kind.as_str())); parts.push(space!()); parts.push(self.id.format(p)); - parts.push(ss!(" {")); + parts.push(text!(" {")); if let Some(body) = &self.body { if !body.is_empty() { @@ -1287,7 +1292,7 @@ impl<'a> Format<'a> for TSModuleDeclaration<'a> { } } - parts.push(ss!("}")); + parts.push(text!("}")); Doc::Array(parts) } @@ -1336,15 +1341,15 @@ impl<'a> Format<'a> for TSImportEqualsDeclaration<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let mut parts = p.vec(); - parts.push(ss!("import ")); + parts.push(text!("import ")); if self.import_kind == ImportOrExportKind::Type { - parts.push(ss!("type ")); + parts.push(text!("type ")); } - parts.push(format!(p, self.id)); - parts.push(ss!(" = ")); - parts.push(format!(p, self.module_reference)); + parts.push(self.id.format(p)); + parts.push(text!(" = ")); + parts.push(self.module_reference.format(p)); if let Some(semi) = p.semi() { parts.push(semi); @@ -1356,8 +1361,8 @@ impl<'a> Format<'a> for TSImportEqualsDeclaration<'a> { impl<'a> Format<'a> for TSModuleReference<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { match self { - TSModuleReference::IdentifierReference(it) => format!(p, it), - TSModuleReference::QualifiedName(it) => format!(p, it), + TSModuleReference::IdentifierReference(it) => it.format(p), + TSModuleReference::QualifiedName(it) => it.format(p), TSModuleReference::ExternalModuleReference(v) => v.format(p), } } @@ -1366,15 +1371,15 @@ impl<'a> Format<'a> for TSModuleReference<'a> { impl<'a> Format<'a> for TSTypeName<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { match self { - TSTypeName::IdentifierReference(it) => format!(p, it), - TSTypeName::QualifiedName(it) => format!(p, it), + TSTypeName::IdentifierReference(it) => it.format(p), + TSTypeName::QualifiedName(it) => it.format(p), } } } impl<'a> Format<'a> for TSExternalModuleReference<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - array!(p, ss!("require("), format!(p, self.expression), ss!(")")) + array!(p, text!("require("), self.expression.format(p), text!(")")) } } @@ -1383,22 +1388,22 @@ impl<'a> Format<'a> for TSTypeParameter<'a> { let mut parts = p.vec(); if self.r#in { - parts.push(ss!("in ")); + parts.push(text!("in ")); } if self.out { - parts.push(ss!("out ")); + parts.push(text!("out ")); } parts.push(self.name.format(p)); if let Some(constraint) = &self.constraint { - parts.push(ss!(" extends ")); + parts.push(text!(" extends ")); parts.push(constraint.format(p)); } if let Some(default) = &self.default { - parts.push(ss!(" = ")); + parts.push(text!(" = ")); parts.push(default.format(p)); } @@ -1411,11 +1416,11 @@ impl<'a> Format<'a> for TSTypeParameterDeclaration<'a> { let mut parts = p.vec(); let mut print_comma = false; - parts.push(ss!("<")); + parts.push(text!("<")); for param in &self.params { if print_comma { - parts.push(ss!(", ")); + parts.push(text!(", ")); } else { print_comma = true; } @@ -1423,7 +1428,7 @@ impl<'a> Format<'a> for TSTypeParameterDeclaration<'a> { parts.push(param.format(p)); } - parts.push(ss!(">")); + parts.push(text!(">")); Doc::Array(parts) } @@ -1434,11 +1439,11 @@ impl<'a> Format<'a> for TSTypeParameterInstantiation<'a> { let mut parts = p.vec(); let mut print_comma = false; - parts.push(ss!("<")); + parts.push(text!("<")); for param in &self.params { if print_comma { - parts.push(ss!(", ")); + parts.push(text!(", ")); } else { print_comma = true; } @@ -1446,7 +1451,7 @@ impl<'a> Format<'a> for TSTypeParameterInstantiation<'a> { parts.push(param.format(p)); } - parts.push(ss!(">")); + parts.push(text!(">")); Doc::Array(parts) } @@ -1530,9 +1535,9 @@ impl<'a> Format<'a> for FormalParameter<'a> { impl<'a> Format<'a> for ImportDeclaration<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let mut parts = p.vec(); - parts.push(ss!("import")); + parts.push(text!("import")); if self.import_kind.is_type() { - parts.push(ss!(" type")); + parts.push(text!(" type")); } if let Some(specifiers) = &self.specifiers { @@ -1548,7 +1553,7 @@ impl<'a> Format<'a> for ImportDeclaration<'a> { || specifiers.get(1).is_some_and(validate_namespace); parts.push(module::print_module_specifiers(p, specifiers, is_default, is_namespace)); - parts.push(ss!(" from")); + parts.push(text!(" from")); } parts.push(space!()); parts.push(self.source.format(p)); @@ -1577,12 +1582,12 @@ impl<'a> Format<'a> for ImportDeclarationSpecifier<'a> { impl<'a> Format<'a> for ImportSpecifier<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - let typed = if self.import_kind.is_type() { ss!("type ") } else { ss!("") }; + let typed = if self.import_kind.is_type() { text!("type ") } else { text!("") }; if self.imported.span() == self.local.span { array![p, typed, self.local.format(p)] } else { - array![p, typed, self.imported.format(p), ss!(" as "), self.local.format(p)] + array![p, typed, self.imported.format(p), text!(" as "), self.local.format(p)] } } } @@ -1595,7 +1600,7 @@ impl<'a> Format<'a> for ImportDefaultSpecifier<'a> { impl<'a> Format<'a> for ImportNamespaceSpecifier<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - array!(p, ss!("* as "), self.local.format(p)) + array!(p, text!("* as "), self.local.format(p)) } } @@ -1612,7 +1617,7 @@ impl<'a> Format<'a> for WithClause<'a> { impl<'a> Format<'a> for ImportAttribute<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - array!(p, self.key.format(p), ss!(": "), self.value.format(p)) + array!(p, self.key.format(p), text!(": "), self.value.format(p)) } } @@ -1645,13 +1650,13 @@ impl<'a> Format<'a> for ExportNamedDeclaration<'a> { impl<'a> Format<'a> for TSExportAssignment<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - array!(p, ss!(" = "), self.expression.format(p)) + array!(p, text!(" = "), self.expression.format(p)) } } impl<'a> Format<'a> for TSNamespaceExportDeclaration<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - array!(p, ss!(" as namespace "), self.id.format(p), ss!(";")) + array!(p, text!(" as namespace "), self.id.format(p), text!(";")) } } @@ -1660,7 +1665,7 @@ impl<'a> Format<'a> for ExportSpecifier<'a> { if self.exported.span() == self.local.span() { self.local.format(p) } else { - array![p, self.local.format(p), ss!(" as "), self.exported.format(p)] + array![p, self.local.format(p), text!(" as "), self.exported.format(p)] } } } @@ -1678,9 +1683,9 @@ impl<'a> Format<'a> for ModuleExportName<'a> { impl<'a> Format<'a> for ExportAllDeclaration<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let mut parts = p.vec(); - parts.push(ss!(" *")); + parts.push(text!(" *")); if let Some(exported) = &self.exported { - parts.push(ss!(" as ")); + parts.push(text!(" as ")); parts.push(exported.format(p)); } Doc::Array(parts) @@ -1752,37 +1757,37 @@ impl<'a> Format<'a> for Expression<'a> { impl<'a> Format<'a> for IdentifierReference<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - wrap!(p, self, IdentifierReference, { p.str(self.name.as_str()) }) + wrap!(p, self, IdentifierReference, { dynamic_text!(p, self.name.as_str()) }) } } impl<'a> Format<'a> for IdentifierName<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - p.str(self.name.as_str()) + dynamic_text!(p, self.name.as_str()) } } impl<'a> Format<'a> for BindingIdentifier<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - wrap!(p, self, BindingIdentifier, { p.str(self.name.as_str()) }) + wrap!(p, self, BindingIdentifier, { dynamic_text!(p, self.name.as_str()) }) } } impl<'a> Format<'a> for LabelIdentifier<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - p.str(self.name.as_str()) + dynamic_text!(p, self.name.as_str()) } } impl<'a> Format<'a> for BooleanLiteral { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Str(if self.value { "true" } else { "false" }) + text!(if self.value { "true" } else { "false" }) } } impl<'a> Format<'a> for NullLiteral { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Str("null") + text!("null") } } @@ -1844,7 +1849,7 @@ impl<'a> Format<'a> for NumericLiteral<'a> { } } - p.str(&string) + dynamic_text!(p, &string) }) } } @@ -1852,8 +1857,8 @@ impl<'a> Format<'a> for NumericLiteral<'a> { impl<'a> Format<'a> for BigIntLiteral<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { match self.span.source_text(p.source_text).cow_to_ascii_lowercase() { - Cow::Borrowed(s) => Doc::Str(s), - Cow::Owned(s) => p.str(&s), + Cow::Borrowed(s) => dynamic_text!(p, s), + Cow::Owned(s) => dynamic_text!(p, &s), } } } @@ -1861,10 +1866,10 @@ impl<'a> Format<'a> for BigIntLiteral<'a> { impl<'a> Format<'a> for RegExpLiteral<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let mut parts = p.vec(); - parts.push(ss!("/")); - parts.push(p.str(self.regex.pattern.source_text(p.source_text).as_ref())); - parts.push(ss!("/")); - parts.push(format!(p, self.regex.flags)); + parts.push(text!("/")); + parts.push(dynamic_text!(p, self.regex.pattern.source_text(p.source_text).as_ref())); + parts.push(text!("/")); + parts.push(self.regex.flags.format(p)); Doc::Array(parts) } } @@ -1874,14 +1879,14 @@ impl<'a> Format<'a> for StringLiteral<'a> { wrap!(p, self, StringLiteral, { let raw = &p.source_text[(self.span.start + 1) as usize..(self.span.end - 1) as usize]; // TODO: implement `makeString` from prettier/src/utils/print-string.js - Doc::Str(string::print_string(p, raw, p.options.single_quote)) + dynamic_text!(p, string::print_string(p, raw, p.options.single_quote)) }) } } impl<'a> Format<'a> for ThisExpression { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Str("this") + text!("this") } } @@ -1900,13 +1905,13 @@ impl<'a> Format<'a> for MemberExpression<'a> { impl<'a> Format<'a> for ComputedMemberExpression<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let mut parts = p.vec(); - parts.push(format!(p, self.object)); + parts.push(self.object.format(p)); if self.optional { - parts.push(ss!("?.")); + parts.push(text!("?.")); } - parts.push(ss!("[")); - parts.push(format!(p, self.expression)); - parts.push(ss!("]")); + parts.push(text!("[")); + parts.push(self.expression.format(p)); + parts.push(text!("]")); Doc::Array(parts) } } @@ -1914,12 +1919,12 @@ impl<'a> Format<'a> for ComputedMemberExpression<'a> { impl<'a> Format<'a> for StaticMemberExpression<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let mut parts = p.vec(); - parts.push(format!(p, self.object)); + parts.push(self.object.format(p)); if self.optional { - parts.push(ss!("?")); + parts.push(text!("?")); } - parts.push(ss!(".")); - parts.push(format!(p, self.property)); + parts.push(text!(".")); + parts.push(self.property.format(p)); Doc::Array(parts) } } @@ -1927,9 +1932,9 @@ impl<'a> Format<'a> for StaticMemberExpression<'a> { impl<'a> Format<'a> for PrivateFieldExpression<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let mut parts = p.vec(); - parts.push(format!(p, self.object)); - parts.push(if self.optional { ss!("?.") } else { ss!(".") }); - parts.push(format!(p, self.field)); + parts.push(self.object.format(p)); + parts.push(if self.optional { text!("?.") } else { text!(".") }); + parts.push(self.field.format(p)); Doc::Array(parts) } } @@ -1959,14 +1964,14 @@ impl<'a> Format<'a> for ArrayExpressionElement<'a> { match self { Self::SpreadElement(expr) => expr.format(p), match_expression!(Self) => self.to_expression().format(p), - Self::Elision(elision) => Doc::Str(""), + Self::Elision(elision) => text!(""), } } } impl<'a> Format<'a> for SpreadElement<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - wrap!(p, self, SpreadElement, { array![p, ss!("..."), format!(p, self.argument)] }) + wrap!(p, self, SpreadElement, { array![p, text!("..."), self.argument.format(p)] }) } } @@ -2001,11 +2006,11 @@ impl<'a> Format<'a> for ObjectProperty<'a> { let mut method = self.method; match self.kind { PropertyKind::Get => { - parts.push(ss!("get ")); + parts.push(text!("get ")); method = true; } PropertyKind::Set => { - parts.push(ss!("set ")); + parts.push(text!("set ")); method = true; } PropertyKind::Init => (), @@ -2021,9 +2026,9 @@ impl<'a> Format<'a> for ObjectProperty<'a> { })); } } else { - parts.push(format!(p, self.key)); - parts.push(ss!(": ")); - parts.push(format!(p, self.value)); + parts.push(self.key.format(p)); + parts.push(text!(": ")); + parts.push(self.value.format(p)); } return Doc::Group(Group::new(parts)); } @@ -2033,16 +2038,16 @@ impl<'a> Format<'a> for ObjectProperty<'a> { } let left_doc = if self.computed { - array!(p, ss!("["), format!(p, self.key), ss!("]")) + array!(p, text!("["), self.key.format(p), text!("]")) } else { - format!(p, self.key) + self.key.format(p) }; assignment::print_assignment( p, assignment::AssignmentLikeNode::ObjectProperty(self), left_doc, - ss!(":"), + text!(":"), Some(&self.value), ) }) @@ -2058,14 +2063,14 @@ impl<'a> Format<'a> for PropertyKey<'a> { }; if is_parent_computed { let mut parts = p.vec(); - parts.push(ss!("[")); + parts.push(text!("[")); let doc = match self { PropertyKey::StaticIdentifier(ident) => ident.format(p), PropertyKey::PrivateIdentifier(ident) => ident.format(p), match_expression!(PropertyKey) => self.to_expression().format(p), }; parts.push(doc); - parts.push(ss!("]")); + parts.push(text!("]")); return Doc::Array(parts); } @@ -2091,7 +2096,10 @@ impl<'a> Format<'a> for PropertyKey<'a> { match self { PropertyKey::StaticIdentifier(ident) => { if need_quote { - Doc::Str(string::print_string(p, &ident.name, p.options.single_quote)) + dynamic_text!( + p, + string::print_string(p, &ident.name, p.options.single_quote) + ) } else { ident.format(p) } @@ -2105,24 +2113,26 @@ impl<'a> Format<'a> for PropertyKey<'a> { && (p.options.quote_props.as_needed() || (p.options.quote_props.consistent()/* && !needsQuoteProps.get(parent) */)) { - string!(p, literal.value.as_str()) + dynamic_text!(p, literal.value.as_str()) } else { - Doc::Str(string::print_string( + dynamic_text!( p, - literal.value.as_str(), - p.options.single_quote, - )) + string::print_string(p, literal.value.as_str(), p.options.single_quote,) + ) } } PropertyKey::NumericLiteral(literal) => { if need_quote { - Doc::Str(string::print_string(p, literal.raw, p.options.single_quote)) + dynamic_text!( + p, + string::print_string(p, literal.raw, p.options.single_quote) + ) } else { literal.format(p) } } PropertyKey::Identifier(ident) => { - array!(p, ss!("["), ident.format(p), ss!("]")) + array!(p, text!("["), ident.format(p), text!("]")) } match_expression!(PropertyKey) => self.to_expression().format(p), } @@ -2140,13 +2150,13 @@ impl<'a> Format<'a> for YieldExpression<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { wrap!(p, self, YieldExpression, { let mut parts = p.vec(); - parts.push(ss!("yield")); + parts.push(text!("yield")); if self.delegate { - parts.push(ss!("*")); + parts.push(text!("*")); } if let Some(argument) = &self.argument { parts.push(space!()); - parts.push(format!(p, argument)); + parts.push(argument.format(p)); } Doc::Array(parts) }) @@ -2157,9 +2167,9 @@ impl<'a> Format<'a> for UpdateExpression<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { wrap!(p, self, UpdateExpression, { if self.prefix { - array![p, ss!(self.operator.as_str()), format!(p, self.argument)] + array![p, text!(self.operator.as_str()), self.argument.format(p)] } else { - array![p, format!(p, self.argument), ss!(self.operator.as_str())] + array![p, self.argument.format(p), text!(self.operator.as_str())] } }) } @@ -2169,11 +2179,11 @@ impl<'a> Format<'a> for UnaryExpression<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { wrap!(p, self, UnaryExpression, { let mut parts = p.vec(); - parts.push(string!(p, self.operator.as_str())); + parts.push(dynamic_text!(p, self.operator.as_str())); if self.operator.is_keyword() { parts.push(space!()); } - parts.push(format!(p, self.argument)); + parts.push(self.argument.format(p)); Doc::Array(parts) }) } @@ -2202,11 +2212,11 @@ impl<'a> Format<'a> for PrivateInExpression<'a> { wrap!(p, self, PrivateInExpression, { array![ p, - format!(p, self.left), + self.left.format(p), space!(), - ss!(self.operator.as_str()), + text!(self.operator.as_str()), space!(), - format!(p, self.right) + self.right.format(p) ] }) } @@ -2300,7 +2310,7 @@ impl<'a> Format<'a> for ObjectAssignmentTarget<'a> { impl<'a> Format<'a> for AssignmentTargetWithDefault<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - array!(p, format!(p, self.binding), ss!(" = "), format!(p, self.init)) + array!(p, self.binding.format(p), text!(" = "), self.init.format(p)) } } @@ -2318,7 +2328,7 @@ impl<'a> Format<'a> for AssignmentTargetPropertyIdentifier<'a> { let mut parts = p.vec(); parts.push(self.binding.format(p)); if let Some(init) = &self.init { - parts.push(ss!(" = ")); + parts.push(text!(" = ")); parts.push(init.format(p)); } Doc::Array(parts) @@ -2327,13 +2337,13 @@ impl<'a> Format<'a> for AssignmentTargetPropertyIdentifier<'a> { impl<'a> Format<'a> for AssignmentTargetPropertyProperty<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - array!(p, self.name.format(p), ss!(": "), self.binding.format(p)) + array!(p, self.name.format(p), text!(": "), self.binding.format(p)) } } impl<'a> Format<'a> for AssignmentTargetRest<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - array![p, ss!("..."), self.target.format(p)] + array![p, text!("..."), self.target.format(p)] } } @@ -2357,21 +2367,21 @@ impl<'a> Format<'a> for ImportExpression<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { wrap!(p, self, ImportExpression, { let mut parts = p.vec(); - parts.push(ss!("import")); - parts.push(ss!("(")); + parts.push(text!("import")); + parts.push(text!("(")); let mut indent_parts = p.vec(); indent_parts.push(softline!()); - indent_parts.push(format!(p, self.source)); + indent_parts.push(self.source.format(p)); if !self.arguments.is_empty() { for arg in &self.arguments { - indent_parts.push(ss!(",")); + indent_parts.push(text!(",")); indent_parts.push(line!()); - indent_parts.push(format!(p, arg)); + indent_parts.push(arg.format(p)); } } parts.push(group!(p, Doc::Indent(indent_parts))); parts.push(softline!()); - parts.push(ss!(")")); + parts.push(text!(")")); Doc::Group(Group::new(parts)) }) @@ -2387,7 +2397,7 @@ impl<'a> Format<'a> for TemplateLiteral<'a> { impl<'a> Format<'a> for TemplateElement<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { // TODO: `replaceEndOfLine` - p.str(self.value.raw.as_str()) + dynamic_text!(p, self.value.raw.as_str()) } } @@ -2396,15 +2406,15 @@ impl<'a> Format<'a> for TaggedTemplateExpression<'a> { wrap!(p, self, TaggedTemplateExpression, { let mut parts = p.vec(); - parts.push(format!(p, self.tag)); + parts.push(self.tag.format(p)); if let Some(type_parameters) = &self.type_parameters { - parts.push(string!(p, "<")); - parts.push(format!(p, type_parameters)); - parts.push(string!(p, ">")); + parts.push(dynamic_text!(p, "<")); + parts.push(type_parameters.format(p)); + parts.push(dynamic_text!(p, ">")); } - parts.push(format!(p, self.quasi)); + parts.push(self.quasi.format(p)); Doc::Array(parts) }) @@ -2413,7 +2423,7 @@ impl<'a> Format<'a> for TaggedTemplateExpression<'a> { impl<'a> Format<'a> for Super { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - Doc::Str("super") + text!("super") } } @@ -2421,8 +2431,8 @@ impl<'a> Format<'a> for AwaitExpression<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { wrap!(p, self, AwaitExpression, { let mut parts = p.vec(); - parts.push(ss!("await ")); - parts.push(format!(p, self.argument)); + parts.push(text!("await ")); + parts.push(self.argument.format(p)); Doc::Array(parts) }) } @@ -2430,7 +2440,7 @@ impl<'a> Format<'a> for AwaitExpression<'a> { impl<'a> Format<'a> for ChainExpression<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - wrap!(p, self, ChainExpression, { format!(p, self.expression) }) + wrap!(p, self, ChainExpression, { self.expression.format(p) }) } } @@ -2457,7 +2467,7 @@ impl<'a> Format<'a> for NewExpression<'a> { impl<'a> Format<'a> for MetaProperty<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - array![p, format!(p, self.meta), ss!("."), format!(p, self.property)] + array![p, self.meta.format(p), text!("."), self.property.format(p)] } } @@ -2501,13 +2511,13 @@ impl<'a> Format<'a> for TSClassImplements<'a> { impl<'a> Format<'a> for TSTypeAssertion<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - array!(p, ss!("<"), self.type_annotation.format(p), ss!(">"), self.expression.format(p)) + array!(p, text!("<"), self.type_annotation.format(p), text!(">"), self.expression.format(p)) } } impl<'a> Format<'a> for TSSatisfiesExpression<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - array!(p, self.expression.format(p), ss!(" satisfies "), self.type_annotation.format(p)) + array!(p, self.expression.format(p), text!(" satisfies "), self.type_annotation.format(p)) } } @@ -2519,13 +2529,13 @@ impl<'a> Format<'a> for TSInstantiationExpression<'a> { impl<'a> Format<'a> for TSNonNullExpression<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - array!(p, self.expression.format(p), ss!("!")) + array!(p, self.expression.format(p), text!("!")) } } impl<'a> Format<'a> for JSXIdentifier<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - ss!(self.name.as_str()) + dynamic_text!(p, self.name.as_str()) } } @@ -2541,7 +2551,7 @@ impl<'a> Format<'a> for JSXMemberExpressionObject<'a> { impl<'a> Format<'a> for JSXMemberExpression<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - array!(p, self.object.format(p), ss!("."), self.property.format(p)) + array!(p, self.object.format(p), text!("."), self.property.format(p)) } } @@ -2559,7 +2569,7 @@ impl<'a> Format<'a> for JSXElementName<'a> { impl<'a> Format<'a> for JSXNamespacedName<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - array!(p, self.namespace.format(p), ss!(":"), self.property.format(p)) + array!(p, self.namespace.format(p), text!(":"), self.property.format(p)) } } @@ -2578,7 +2588,7 @@ impl<'a> Format<'a> for JSXAttribute<'a> { parts.push(self.name.format(p)); if let Some(value) = &self.value { - parts.push(ss!("=")); + parts.push(text!("=")); parts.push(value.format(p)); } @@ -2588,7 +2598,7 @@ impl<'a> Format<'a> for JSXAttribute<'a> { impl<'a> Format<'a> for JSXEmptyExpression { fn format(&self, _: &mut Prettier<'a>) -> Doc<'a> { - ss!("") + text!("") } } @@ -2642,7 +2652,7 @@ impl<'a> Format<'a> for JSXExpression<'a> { impl<'a> Format<'a> for JSXExpressionContainer<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - array!(p, ss!("{"), self.expression.format(p), ss!("}")) + array!(p, text!("{"), self.expression.format(p), text!("}")) } } @@ -2659,7 +2669,7 @@ impl<'a> Format<'a> for JSXAttributeValue<'a> { impl<'a> Format<'a> for JSXSpreadAttribute<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - array!(p, ss!("..."), self.argument.format(p)) + array!(p, text!("..."), self.argument.format(p)) } } @@ -2676,7 +2686,7 @@ impl<'a> Format<'a> for JSXOpeningElement<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let mut parts = p.vec(); - parts.push(ss!("<")); + parts.push(text!("<")); parts.push(self.name.format(p)); if let Some(type_parameters) = &self.type_parameters { @@ -2690,10 +2700,10 @@ impl<'a> Format<'a> for JSXOpeningElement<'a> { if self.self_closing { parts.push(space!()); - parts.push(ss!("/")); + parts.push(text!("/")); } - parts.push(ss!(">")); + parts.push(text!(">")); Doc::Array(parts) } @@ -2701,7 +2711,7 @@ impl<'a> Format<'a> for JSXOpeningElement<'a> { impl<'a> Format<'a> for JSXClosingElement<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - array!(p, ss!("")) + array!(p, text!("")) } } @@ -2725,25 +2735,25 @@ impl<'a> Format<'a> for JSXElement<'a> { impl<'a> Format<'a> for JSXOpeningFragment { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - ss!("<>") + text!("<>") } } impl<'a> Format<'a> for JSXClosingFragment { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - ss!("") + text!("") } } impl<'a> Format<'a> for JSXText<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - ss!(self.value.as_str()) + dynamic_text!(p, self.value.as_str()) } } impl<'a> Format<'a> for JSXSpreadChild<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - array!(p, ss!("..."), self.expression.format(p)) + array!(p, text!("..."), self.expression.format(p)) } } @@ -2778,7 +2788,7 @@ impl<'a> Format<'a> for JSXFragment<'a> { impl<'a> Format<'a> for StaticBlock<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { wrap!(p, self, StaticBlock, { - array![p, ss!("static "), block::print_block(p, &self.body, None)] + array![p, text!("static "), block::print_block(p, &self.body, None)] }) } } @@ -2806,8 +2816,8 @@ impl<'a> Format<'a> for AccessorProperty<'a> { impl<'a> Format<'a> for PrivateIdentifier<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let mut parts = p.vec(); - parts.push(ss!("#")); - parts.push(p.str(self.name.as_str())); + parts.push(text!("#")); + parts.push(dynamic_text!(p, self.name.as_str())); Doc::Array(parts) } } @@ -2823,11 +2833,11 @@ impl<'a> Format<'a> for BindingPattern<'a> { }); if self.optional { - parts.push(ss!("?")); + parts.push(text!("?")); } if let Some(typ) = &self.type_annotation { - parts.push(array![p, ss!(": "), typ.type_annotation.format(p)]); + parts.push(array![p, text!(": "), typ.type_annotation.format(p)]); } Doc::Array(parts) } @@ -2846,14 +2856,14 @@ impl<'a> Format<'a> for BindingProperty<'a> { if self.shorthand { self.value.format(p) } else { - group!(p, format!(p, self.key), ss!(": "), format!(p, self.value)) + group!(p, self.key.format(p), text!(": "), self.value.format(p)) } } } impl<'a> Format<'a> for BindingRestElement<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - array!(p, ss!("..."), format!(p, self.argument)) + array!(p, text!("..."), self.argument.format(p)) } } @@ -2866,7 +2876,7 @@ impl<'a> Format<'a> for ArrayPattern<'a> { impl<'a> Format<'a> for AssignmentPattern<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { wrap!(p, self, AssignmentPattern, { - array![p, format!(p, self.left), ss!(" = "), format!(p, self.right)] + array![p, self.left.format(p), text!(" = "), self.right.format(p)] }) } } @@ -2898,7 +2908,8 @@ impl<'a> Format<'a> for RegExpFlags { if self.contains(Self::Y) { string.push('y'); } - p.str(&string.iter().collect::()) + let sorted = &string.iter().collect::(); + dynamic_text!(p, &sorted) } } @@ -2907,14 +2918,14 @@ impl<'a> Format<'a> for TSIndexSignature<'a> { let mut parts = p.vec(); if self.readonly { - parts.push(ss!("readonly ")); + parts.push(text!("readonly ")); } - parts.push(ss!("[")); + parts.push(text!("[")); for param in &self.parameters { parts.push(param.format(p)); } - parts.push(ss!("]: ")); + parts.push(text!("]: ")); parts.push(self.type_annotation.type_annotation.format(p)); Doc::Array(parts) @@ -2925,8 +2936,8 @@ impl<'a> Format<'a> for TSIndexSignatureName<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { array!( p, - ss!(self.name.as_str()), - ss!(": "), + dynamic_text!(p, self.name.as_str()), + text!(": "), self.type_annotation.type_annotation.format(p) ) } @@ -2936,16 +2947,16 @@ impl<'a> Format<'a> for TSPropertySignature<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let mut parts = p.vec(); if self.readonly { - parts.push(ss!("readonly ")); + parts.push(text!("readonly ")); } - parts.push(format!(p, self.key)); + parts.push(self.key.format(p)); if let Some(ty) = &self.type_annotation { if self.optional { - parts.push(ss!("?")); + parts.push(text!("?")); } - parts.push(ss!(":")); + parts.push(text!(":")); parts.push(space!()); - parts.push(format!(p, ty.type_annotation)); + parts.push(ty.type_annotation.format(p)); } Doc::Array(parts) } @@ -2962,7 +2973,7 @@ impl<'a> Format<'a> for TSCallSignatureDeclaration<'a> { parts.push(self.params.format(p)); if let Some(return_type) = &self.return_type { - parts.push(ss!(": ")); + parts.push(text!(": ")); parts.push(return_type.type_annotation.format(p)); } @@ -2974,7 +2985,7 @@ impl<'a> Format<'a> for TSConstructSignatureDeclaration<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { let mut parts = p.vec(); - parts.push(ss!("new ")); + parts.push(text!("new ")); if let Some(type_parameters) = &self.type_parameters { parts.push(type_parameters.format(p)); @@ -2983,7 +2994,7 @@ impl<'a> Format<'a> for TSConstructSignatureDeclaration<'a> { parts.push(self.params.format(p)); if let Some(return_type) = &self.return_type { - parts.push(ss!(": ")); + parts.push(text!(": ")); parts.push(return_type.type_annotation.format(p)); } @@ -2996,17 +3007,17 @@ impl<'a> Format<'a> for TSMethodSignature<'a> { let mut parts = p.vec(); if self.computed { - parts.push(ss!("[")); + parts.push(text!("[")); } parts.push(self.key.format(p)); if self.computed { - parts.push(ss!("]")); + parts.push(text!("]")); } if self.optional { - parts.push(ss!("?")); + parts.push(text!("?")); } if let Some(type_parameters) = &self.type_parameters { @@ -3016,7 +3027,7 @@ impl<'a> Format<'a> for TSMethodSignature<'a> { parts.push(self.params.format(p)); if let Some(return_type) = &self.return_type { - parts.push(ss!(": ")); + parts.push(text!(": ")); parts.push(return_type.type_annotation.format(p)); } @@ -3038,6 +3049,6 @@ impl<'a> Format<'a> for TSSignature<'a> { impl<'a> Format<'a> for TSAsExpression<'a> { fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { - array![p, format!(p, self.expression), ss!(" as "), format!(p, self.type_annotation)] + array![p, self.expression.format(p), text!(" as "), self.type_annotation.format(p)] } } diff --git a/crates/oxc_prettier/src/format/module.rs b/crates/oxc_prettier/src/format/module.rs index bc71534349960..37ef649f49eac 100644 --- a/crates/oxc_prettier/src/format/module.rs +++ b/crates/oxc_prettier/src/format/module.rs @@ -4,8 +4,9 @@ use oxc_allocator::Vec; use oxc_ast::ast::*; use crate::{ - doc::{Doc, DocBuilder, Separator}, - group, if_break, indent, line, softline, space, ss, Format, Prettier, + group, if_break, indent, + ir::{Doc, DocBuilder, Separator}, + line, softline, space, text, Format, Prettier, }; pub(super) fn print_export_declaration<'a>( @@ -15,10 +16,10 @@ pub(super) fn print_export_declaration<'a>( debug_assert!(decl.is_export()); let mut parts = p.vec(); - parts.push(ss!("export")); + parts.push(text!("export")); if decl.is_default_export() { - parts.push(ss!(" default ")); + parts.push(text!(" default ")); } parts.push(match decl { @@ -31,7 +32,7 @@ pub(super) fn print_export_declaration<'a>( }); if let Some(source) = decl.source() { - parts.push(ss!(" from ")); + parts.push(text!(" from ")); parts.push(source.format(p)); } @@ -57,12 +58,12 @@ fn print_semicolon_after_export_declaration<'a>( match decl { ModuleDeclaration::ExportDefaultDeclaration(decl) => match decl.declaration { - match_expression!(ExportDefaultDeclarationKind) => Some(ss!(";")), + match_expression!(ExportDefaultDeclarationKind) => Some(text!(";")), _ => None, }, ModuleDeclaration::ExportNamedDeclaration(decl) => { let Some(declaration) = &decl.declaration else { - return Some(ss!(";")); + return Some(text!(";")); }; match declaration { @@ -70,11 +71,11 @@ fn print_semicolon_after_export_declaration<'a>( | Declaration::VariableDeclaration(_) | Declaration::ClassDeclaration(_) | Declaration::TSModuleDeclaration(_) => None, - _ => Some(ss!(";")), + _ => Some(text!(";")), } } ModuleDeclaration::ExportAllDeclaration(_) | ModuleDeclaration::TSExportAssignment(_) => { - Some(ss!(";")) + Some(text!(";")) } _ => None, } @@ -88,7 +89,7 @@ pub fn print_module_specifiers<'a, T: Format<'a>>( ) -> Doc<'a> { let mut parts = p.vec(); if specifiers.is_empty() { - parts.push(ss!(" {}")); + parts.push(text!(" {}")); } else { parts.push(space!()); @@ -96,14 +97,14 @@ pub fn print_module_specifiers<'a, T: Format<'a>>( if include_default { parts.push(specifiers_iter.pop_front().unwrap().format(p)); if !specifiers_iter.is_empty() { - parts.push(p.str(", ")); + parts.push(text!(", ")); } } if include_namespace { parts.push(specifiers_iter.pop_front().unwrap().format(p)); if !specifiers_iter.is_empty() { - parts.push(p.str(", ")); + parts.push(text!(", ")); } } @@ -115,7 +116,7 @@ pub fn print_module_specifiers<'a, T: Format<'a>>( specifiers_iter.iter().map(|s| s.format(p)).collect::>(); parts.push(group![ p, - ss!("{"), + text!("{"), indent![ p, if p.options.bracket_spacing { line!() } else { softline!() }, @@ -123,10 +124,10 @@ pub fn print_module_specifiers<'a, T: Format<'a>>( ], if_break!(p, if p.should_print_es5_comma() { "," } else { "" }, "", None), if p.options.bracket_spacing { line!() } else { softline!() }, - ss!("}"), + text!("}"), ]); } else { - parts.push(ss!("{")); + parts.push(text!("{")); if p.options.bracket_spacing { parts.push(space!()); } @@ -134,7 +135,7 @@ pub fn print_module_specifiers<'a, T: Format<'a>>( if p.options.bracket_spacing { parts.push(space!()); } - parts.push(ss!("}")); + parts.push(text!("}")); } } } diff --git a/crates/oxc_prettier/src/format/object.rs b/crates/oxc_prettier/src/format/object.rs index 3e321b671d1d5..a77e3abdcbb3c 100644 --- a/crates/oxc_prettier/src/format/object.rs +++ b/crates/oxc_prettier/src/format/object.rs @@ -6,8 +6,9 @@ use oxc_span::Span; use super::{misc, Format}; use crate::{ - doc::{Doc, DocBuilder, Group}, - group, if_break, line, softline, ss, Prettier, + dynamic_text, group, if_break, + ir::{Doc, DocBuilder, Group}, + line, softline, text, Prettier, }; #[derive(Debug, Clone, Copy)] @@ -81,7 +82,7 @@ impl<'a, 'b> ObjectLike<'a, 'b> { } } - fn member_separator(self, p: &'b Prettier<'a>) -> &'a str { + fn member_separator(self, p: &'b Prettier<'a>) -> &'static str { match self { Self::TSTypeLiteral(_) => { if p.semi().is_some() { @@ -99,8 +100,8 @@ pub(super) fn print_object_properties<'a>( p: &mut Prettier<'a>, object: ObjectLike<'a, '_>, ) -> Doc<'a> { - let left_brace = ss!("{"); - let right_brace = ss!("}"); + let left_brace = text!("{"); + let right_brace = text!("}"); let should_break = false; let member_separator = object.member_separator(p); @@ -109,7 +110,7 @@ pub(super) fn print_object_properties<'a>( group![p, left_brace, softline!(), right_brace] } else { let mut parts = p.vec(); - parts.push(ss!("{")); + parts.push(text!("{")); parts.push(Doc::Indent({ let len = object.len(); let has_rest = object.has_rest(); @@ -122,7 +123,7 @@ pub(super) fn print_object_properties<'a>( break; } - indent_parts.push(ss!(member_separator)); + indent_parts.push(text!(member_separator)); indent_parts.push(line!()); } match object { @@ -151,7 +152,7 @@ pub(super) fn print_object_properties<'a>( parts.push(if_break!(p, member_separator, "", None)); } parts.push(if p.options.bracket_spacing { line!() } else { softline!() }); - parts.push(ss!("}")); + parts.push(text!("}")); if matches!(p.current_kind(), AstKind::Program(_)) { let should_break = diff --git a/crates/oxc_prettier/src/format/statement.rs b/crates/oxc_prettier/src/format/statement.rs index 6927c59b0d67d..99b50025e4085 100644 --- a/crates/oxc_prettier/src/format/statement.rs +++ b/crates/oxc_prettier/src/format/statement.rs @@ -4,8 +4,9 @@ use oxc_span::GetSpan; use super::Format; use crate::{ - doc::{Doc, DocBuilder}, - hardline, Prettier, + hardline, + ir::{Doc, DocBuilder}, + Prettier, }; pub(super) fn print_statement_sequence<'a>( diff --git a/crates/oxc_prettier/src/format/template_literal.rs b/crates/oxc_prettier/src/format/template_literal.rs index 497d3fe3478bc..f64deb5208140 100644 --- a/crates/oxc_prettier/src/format/template_literal.rs +++ b/crates/oxc_prettier/src/format/template_literal.rs @@ -1,9 +1,9 @@ use oxc_ast::ast::*; use crate::{ - doc::{Doc, DocBuilder}, format::Format, - ss, Prettier, + ir::{Doc, DocBuilder}, + text, Prettier, }; #[allow(clippy::enum_variant_names)] @@ -37,7 +37,7 @@ pub(super) fn print_template_literal<'a, 'b>( template_literal: &'b TemplateLiteralPrinter<'a, 'b>, ) -> Doc<'a> { let mut parts = p.vec(); - parts.push(ss!("`")); + parts.push(text!("`")); for (index, quais) in template_literal.quasis().iter().enumerate() { parts.push(quais.format(p)); @@ -45,12 +45,12 @@ pub(super) fn print_template_literal<'a, 'b>( break; }; - parts.push(ss!("${")); + parts.push(text!("${")); parts.push(expr_doc); - parts.push(ss!("}")); + parts.push(text!("}")); } - parts.push(ss!("`")); + parts.push(text!("`")); Doc::Array(parts) } diff --git a/crates/oxc_prettier/src/format/ternary.rs b/crates/oxc_prettier/src/format/ternary.rs index 7efd76b9571a3..7c4d32c4a9606 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, line, ss, DocBuilder, Format, Prettier}; +use crate::{group, indent, ir::Doc, line, text, DocBuilder, Format, Prettier}; pub(super) fn print_ternary<'a>(p: &mut Prettier<'a>, expr: &ConditionalExpression<'a>) -> Doc<'a> { group![ @@ -9,10 +9,10 @@ pub(super) fn print_ternary<'a>(p: &mut Prettier<'a>, expr: &ConditionalExpressi indent!( p, line!(), - ss!("? "), + text!("? "), expr.consequent.format(p), line!(), - ss!(": "), + text!(": "), expr.alternate.format(p) ) ] diff --git a/crates/oxc_prettier/src/ir/builder.rs b/crates/oxc_prettier/src/ir/builder.rs new file mode 100644 index 0000000000000..d1a16b981b540 --- /dev/null +++ b/crates/oxc_prettier/src/ir/builder.rs @@ -0,0 +1,54 @@ +use oxc_allocator::{Allocator, Box, String, Vec}; + +use crate::{ + array, + ir::{Doc, Line}, + line, text, +}; + +#[derive(Clone, Copy)] +pub enum Separator { + #[allow(unused)] + Softline, + Hardline, + CommaLine, // [",", line] +} + +pub trait DocBuilder<'a> { + fn allocator(&self) -> &'a Allocator; + + #[inline] + fn vec(&self) -> Vec<'a, T> { + Vec::new_in(self.allocator()) + } + fn vec_single(&self, value: T) -> Vec<'a, T> { + let mut vec = Vec::with_capacity_in(1, self.allocator()); + vec.push(value); + vec + } + + #[inline] + fn string(&self, s: &str) -> Doc<'a> { + Doc::Str(String::from_str_in(s, self.allocator()).into_bump_str()) + } + + #[inline] + fn boxed(&self, doc: Doc<'a>) -> Box<'a, Doc<'a>> { + Box::new_in(doc, self.allocator()) + } + + fn join(&self, separator: Separator, docs: std::vec::Vec>) -> Vec<'a, Doc<'a>> { + let mut parts = self.vec(); + for (i, doc) in docs.into_iter().enumerate() { + if i != 0 { + parts.push(match separator { + Separator::Softline => Doc::Line(Line::softline()), + Separator::Hardline => Doc::Line(Line::hardline()), + Separator::CommaLine => array![self, text!(","), line!()], + }); + } + parts.push(doc); + } + parts + } +} diff --git a/crates/oxc_prettier/src/ir/display.rs b/crates/oxc_prettier/src/ir/display.rs new file mode 100644 index 0000000000000..77a45a318ebb3 --- /dev/null +++ b/crates/oxc_prettier/src/ir/display.rs @@ -0,0 +1,132 @@ +use crate::ir::{Doc, Line}; + +impl<'a> std::fmt::Display for Doc<'a> { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", print_doc_to_debug(self)) + } +} + +// https://github.com/prettier/prettier/blob/3.3.3/src/document/debug.js +fn print_doc_to_debug(doc: &Doc<'_>) -> std::string::String { + use std::string::String; + let mut string = String::new(); + match doc { + Doc::Str(s) => { + string.push('"'); + string.push_str(s); + string.push('"'); + } + Doc::Array(docs) => { + string.push_str("[\n"); + for (idx, doc) in docs.iter().enumerate() { + string.push_str(&print_doc_to_debug(doc)); + if idx != docs.len() - 1 { + string.push_str(", "); + } + } + string.push_str("]\n"); + } + Doc::Indent(contents) => { + string.push_str("indent(["); + for (idx, doc) in contents.iter().enumerate() { + string.push_str(&print_doc_to_debug(doc)); + if idx != contents.len() - 1 { + string.push_str(", "); + } + } + string.push_str("])"); + } + Doc::IndentIfBreak(indent_if_break) => { + string.push_str("indentIfBreak("); + string.push_str("[\n"); + for (idx, doc) in indent_if_break.contents.iter().enumerate() { + string.push_str(&print_doc_to_debug(doc)); + if idx != indent_if_break.contents.len() - 1 { + string.push_str(", "); + } + } + + if let Some(id) = indent_if_break.group_id { + string.push_str(&format!(", {{id: {id}}}")); + } + + string.push_str("])"); + } + Doc::Group(group) => { + if group.expanded_states.is_some() { + string.push_str("conditionalGroup([\n"); + } + + string.push_str("group([\n"); + for (idx, doc) in group.contents.iter().enumerate() { + string.push_str(&print_doc_to_debug(doc)); + if idx != group.contents.len() - 1 { + string.push_str(", "); + } + } + string.push_str("], { shouldBreak: "); + string.push_str(&group.should_break.to_string()); + if let Some(id) = group.id { + string.push_str(&format!(", id: {id}")); + } + string.push_str(" })"); + + if let Some(expanded_states) = &group.expanded_states { + string.push_str(",\n"); + for (idx, doc) in expanded_states.iter().enumerate() { + string.push_str(&print_doc_to_debug(doc)); + if idx != expanded_states.len() - 1 { + string.push_str(", "); + } + } + string.push_str("])"); + } + } + 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!( + "ifBreak({}, {}", + print_doc_to_debug(&if_break.break_contents), + print_doc_to_debug(&if_break.flat_content) + )); + if let Some(group_id) = if_break.group_id { + string.push_str(&format!(", {{ groupId: {group_id} }}")); + } + string.push(')'); + } + Doc::Fill(fill) => { + string.push_str("fill([\n"); + let parts = fill.parts(); + for (idx, doc) in parts.iter().enumerate() { + string.push_str(&print_doc_to_debug(doc)); + if idx != parts.len() - 1 { + string.push_str(", "); + } + } + string.push_str("])"); + } + Doc::LineSuffix(docs) => { + string.push_str("lineSuffix("); + for (idx, doc) in docs.iter().enumerate() { + string.push_str(&print_doc_to_debug(doc)); + if idx != docs.len() - 1 { + string.push_str(", "); + } + } + string.push(')'); + } + Doc::BreakParent => { + string.push_str("BreakParent"); + } + } + + string +} diff --git a/crates/oxc_prettier/src/ir/doc.rs b/crates/oxc_prettier/src/ir/doc.rs new file mode 100644 index 0000000000000..5c10a340cd89f --- /dev/null +++ b/crates/oxc_prettier/src/ir/doc.rs @@ -0,0 +1,164 @@ +//! Prettier IR +//! +//! References: +//! * + +use oxc_allocator::{Allocator, Box, String, Vec}; + +use crate::{array, line, text, GroupId}; + +#[derive(Debug)] +pub enum Doc<'a> { + Str(&'a str), + // perf: can we use &[Doc] here? + Array(Vec<'a, Doc<'a>>), + /// Increase the level of indentation. + Indent(Vec<'a, Doc<'a>>), + IndentIfBreak(IndentIfBreak<'a>), + /// Mark a group of items which the printer should try to fit on one line. + /// This is the basic command to tell the printer when to break. + /// Groups are usually nested, and the printer will try to fit everything on one line, + /// but if it doesn't fit it will break the outermost group first and try again. + /// It will continue breaking groups until everything fits (or there are no more groups to break). + Group(Group<'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(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. + LineSuffix(Vec<'a, Doc<'a>>), + /// Print something if the current `group` or the current element of `fill` breaks and something else if it doesn't. + IfBreak(IfBreak<'a>), + /// This is an alternative type of group which behaves like text layout: + /// it's going to add a break whenever the next element doesn't fit in the line anymore. + /// The difference with `group` is that it's not going to break all the separators, just the ones that are at the end of lines. + Fill(Fill<'a>), + /// Include this anywhere to force all parent groups to break. + 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>>, + pub should_break: bool, + pub expanded_states: Option>>, + pub id: Option, +} + +impl<'a> Group<'a> { + pub fn new(contents: Vec<'a, Doc<'a>>) -> Self { + Self { contents, should_break: false, id: None, expanded_states: None } + } + + pub fn new_conditional_group( + contents: Vec<'a, Doc<'a>>, + expanded_states: Vec<'a, Doc<'a>>, + ) -> Self { + Self { contents, should_break: false, id: None, expanded_states: Some(expanded_states) } + } + + pub fn with_break(mut self, yes: bool) -> Self { + self.should_break = yes; + self + } + + pub fn with_id(mut self, id: GroupId) -> Self { + self.id = Some(id); + self + } +} +#[derive(Debug)] +pub struct IndentIfBreak<'a> { + pub contents: Vec<'a, Doc<'a>>, + pub group_id: Option, +} + +impl<'a> IndentIfBreak<'a> { + pub fn new(contents: Vec<'a, Doc<'a>>) -> Self { + Self { contents, group_id: None } + } + + pub fn with_id(mut self, id: GroupId) -> Self { + self.group_id = Some(id); + self + } +} + +#[derive(Debug)] +pub struct Fill<'a> { + pub parts: Vec<'a, Doc<'a>>, +} + +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)) + } else { + None + } + } + + 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 + } +} + +#[derive(Debug)] +pub struct IfBreak<'a> { + pub break_contents: Box<'a, Doc<'a>>, + pub flat_content: Box<'a, Doc<'a>>, + pub group_id: Option, +} diff --git a/crates/oxc_prettier/src/ir/mod.rs b/crates/oxc_prettier/src/ir/mod.rs new file mode 100644 index 0000000000000..ab05969eb7295 --- /dev/null +++ b/crates/oxc_prettier/src/ir/mod.rs @@ -0,0 +1,6 @@ +mod builder; +mod display; +mod doc; + +pub use builder::*; +pub use doc::*; diff --git a/crates/oxc_prettier/src/lib.rs b/crates/oxc_prettier/src/lib.rs index fe09665c3e184..778c18d060eb2 100644 --- a/crates/oxc_prettier/src/lib.rs +++ b/crates/oxc_prettier/src/lib.rs @@ -5,8 +5,8 @@ mod binaryish; mod comments; -mod doc; mod format; +mod ir; mod macros; mod needs_parens; mod options; @@ -22,8 +22,8 @@ use oxc_syntax::identifier::is_line_terminator; pub use crate::options::{ArrowParens, EndOfLine, PrettierOptions, QuoteProps, TrailingComma}; use crate::{ - doc::{Doc, DocBuilder}, format::Format, + ir::{Doc, DocBuilder}, printer::Printer, }; diff --git a/crates/oxc_prettier/src/macros.rs b/crates/oxc_prettier/src/macros.rs index 088eaed5347d7..3cf63f1d0560b 100644 --- a/crates/oxc_prettier/src/macros.rs +++ b/crates/oxc_prettier/src/macros.rs @@ -1,17 +1,11 @@ //! Utility macros for constructing the IR +/// Wrap a static text #[macro_export] -macro_rules! format { - ($p:ident, $s:expr) => {{ - $s.format($p) - }}; -} - -/// Wrap a static string (ss) -#[macro_export] -macro_rules! ss { +macro_rules! text { ($s:expr) => {{ - Doc::Str($s) + let s: &'static str = $s; + Doc::Str(s) }}; } @@ -22,117 +16,112 @@ macro_rules! space { }}; } +/// Wrap a dynamic text #[macro_export] -macro_rules! string { +macro_rules! dynamic_text { ($p:ident, $s:expr) => {{ - $p.str($s) + $p.string($s) }}; } #[macro_export] macro_rules! indent { - ($p:ident, $( $x:expr ),* $(,)?) => { - { - let mut temp_vec = $p.vec(); - $( - temp_vec.push($x); - )* - Doc::Indent(temp_vec) - } - }; + ($p:ident, $( $x:expr ),* $(,)?) => {{ + let mut temp_vec = $p.vec(); + $( + temp_vec.push($x); + )* + Doc::Indent(temp_vec) + }}; } #[macro_export] macro_rules! indent_if_break { - ($p:ident, $( $x:expr ),* $(,)?) => { - { - let mut temp_vec = $p.vec(); - $( - temp_vec.push($x); - )* - Doc::IndentIfBreak(temp_vec) - } - }; + ($p:ident, $( $x:expr ),* $(,)?) => {{ + let mut temp_vec = $p.vec(); + $( + temp_vec.push($x); + )* + Doc::IndentIfBreak(temp_vec) + }}; } #[macro_export] macro_rules! line { - () => { - Doc::Line($crate::doc::Line::default()) - }; + () => {{ + use $crate::ir::Line; + Doc::Line(Line::default()) + }}; } #[macro_export] macro_rules! softline { - () => { - Doc::Line($crate::doc::Line::softline()) - }; + () => {{ + use $crate::ir::Line; + Doc::Line(Line::softline()) + }}; } #[macro_export] macro_rules! hardline { - () => { - [Doc::Line($crate::doc::Line::hardline()), Doc::BreakParent] - }; + () => {{ + use $crate::ir::Line; + [Doc::Line(Line::hardline()), Doc::BreakParent] + }}; } #[macro_export] macro_rules! array { - ($p:ident, $( $x:expr ),* $(,)?) => { - { - let mut temp_vec = $p.vec(); - $( - temp_vec.push($x); - )* - Doc::Array(temp_vec) - } - }; + ($p:ident, $( $x:expr ),* $(,)?) => {{ + let mut temp_vec = $p.vec(); + $( + temp_vec.push($x); + )* + Doc::Array(temp_vec) + }}; } #[macro_export] macro_rules! group { - ($p:ident, $( $x:expr ),* $(,)?) => { - { - let mut temp_vec = $p.vec(); - $( - temp_vec.push($x); - )* - Doc::Group($crate::doc::Group::new(temp_vec)) - } - }; + ($p:ident, $( $x:expr ),* $(,)?) => {{ + use $crate::ir::Group; + let mut temp_vec = $p.vec(); + $( + temp_vec.push($x); + )* + Doc::Group(Group::new(temp_vec)) + }}; } #[macro_export] macro_rules! conditional_group { - ($p:ident, $c: expr, $( $x:expr ),* $(,)?) => { - { - let mut temp_vec = $p.vec(); - $( - temp_vec.push($x); - )* - let contents = $p.vec_single($c); - Doc::Group($crate::doc::Group::new_conditional_group(contents, temp_vec)) - } - }; + ($p:ident, $c: expr, $( $x:expr ),* $(,)?) => {{ + use $crate::ir::Group; + let mut temp_vec = $p.vec(); + $( + temp_vec.push($x); + )* + let contents = $p.vec_single($c); + Doc::Group(Group::new_conditional_group(contents, temp_vec)) + }}; } #[macro_export] macro_rules! group_break { - ($p:ident, $( $x:expr ),* $(,)?) => { - { - let mut temp_vec = $p.vec(); - $( - temp_vec.push($x); - )* - Doc::Group($crate::doc::Group::new(temp_vec).with_break(true)) - } - }; + ($p:ident, $( $x:expr ),* $(,)?) => {{ + use $crate::ir::Group; + let mut temp_vec = $p.vec(); + $( + temp_vec.push($x); + )* + Doc::Group(Group::new(temp_vec).with_break(true)) + }}; } #[macro_export] macro_rules! if_break { ($p:ident, $s:expr, $flat:expr, $group_id:expr) => {{ - use $crate::doc::IfBreak; + use $crate::ir::IfBreak; Doc::IfBreak(IfBreak { break_contents: $p.boxed(Doc::Str($s)), flat_content: $p.boxed(Doc::Str($flat)), @@ -149,27 +138,33 @@ macro_rules! if_break { #[macro_export] macro_rules! line_suffix { - ($p:ident, $( $x:expr ),* $(,)?) => { - { - let mut temp_vec = $p.vec(); - $( - temp_vec.push($x); - )* - Doc::LineSuffix(temp_vec) - } - }; + ($p:ident, $( $x:expr ),* $(,)?) => {{ + let mut temp_vec = $p.vec(); + $( + temp_vec.push($x); + )* + Doc::LineSuffix(temp_vec) + }}; } +// --- + #[macro_export] macro_rules! wrap { ($p:ident, $self:expr, $kind:ident, $block:block) => {{ let kind = AstKind::$kind($p.alloc($self)); $p.enter_node(kind); + let leading = $p.print_leading_comments(kind.span()); + let doc = $block; - let doc = $p.wrap_parens(doc, kind); + let doc = if $p.need_parens(kind) { array![$p, text!("("), doc, text!(")")] } else { doc }; + + // TODO: dangling comments? let trailing = $p.print_trailing_comments(kind.span()); + let doc = $p.print_comments(leading, doc, trailing); + $p.leave_node(); doc }}; diff --git a/crates/oxc_prettier/src/needs_parens.rs b/crates/oxc_prettier/src/needs_parens.rs index ae2b7f4d28c55..45100ae3b640e 100644 --- a/crates/oxc_prettier/src/needs_parens.rs +++ b/crates/oxc_prettier/src/needs_parens.rs @@ -22,18 +22,10 @@ use oxc_syntax::{ precedence::GetPrecedence, }; -use crate::{array, binaryish::BinaryishOperator, doc::Doc, ss, DocBuilder, Prettier}; +use crate::{binaryish::BinaryishOperator, Prettier}; impl<'a> Prettier<'a> { - pub(crate) fn wrap_parens(&mut self, doc: Doc<'a>, kind: AstKind<'a>) -> Doc<'a> { - if self.need_parens(kind) { - array![self, ss!("("), doc, ss!(")")] - } else { - doc - } - } - - fn need_parens(&mut self, kind: AstKind<'a>) -> bool { + pub(crate) fn need_parens(&mut self, kind: AstKind<'a>) -> bool { if matches!(kind, AstKind::Program(_)) || kind.is_statement() || kind.is_declaration() { return false; } diff --git a/crates/oxc_prettier/src/printer/command.rs b/crates/oxc_prettier/src/printer/command.rs index 8a1c60eeefb38..9e18ec72bf03a 100644 --- a/crates/oxc_prettier/src/printer/command.rs +++ b/crates/oxc_prettier/src/printer/command.rs @@ -1,4 +1,4 @@ -use crate::doc::Doc; +use crate::ir::Doc; pub struct Command<'a> { pub indent: Indent, diff --git a/crates/oxc_prettier/src/printer/mod.rs b/crates/oxc_prettier/src/printer/mod.rs index 35525a90e6918..be2159458da12 100644 --- a/crates/oxc_prettier/src/printer/mod.rs +++ b/crates/oxc_prettier/src/printer/mod.rs @@ -10,9 +10,9 @@ use std::collections::VecDeque; use oxc_allocator::Allocator; use rustc_hash::FxHashMap; -use self::command::{Command, Indent, Mode}; use crate::{ - doc::{Doc, DocBuilder, Fill, IfBreak, IndentIfBreak, Line}, + ir::{Doc, DocBuilder, Fill, IfBreak, IndentIfBreak, Line}, + printer::command::{Command, Indent, Mode}, GroupId, PrettierOptions, }; diff --git a/crates/oxc_prettier/src/utils/document.rs b/crates/oxc_prettier/src/utils/document.rs index a147dca1c7225..79ac4c72ff395 100644 --- a/crates/oxc_prettier/src/utils/document.rs +++ b/crates/oxc_prettier/src/utils/document.rs @@ -1,4 +1,4 @@ -use crate::{doc::IndentIfBreak, Doc}; +use crate::ir::{Doc, IndentIfBreak}; pub fn will_break(doc: &mut Doc<'_>) -> bool { let check_array =