feat(prettier): start adding parent stack (#1415)

This commit is contained in:
Boshen 2023-11-19 00:39:24 +08:00 committed by GitHub
parent 781cd5a21a
commit 1dacb645d0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
6 changed files with 150 additions and 83 deletions

View file

@ -56,7 +56,7 @@ impl<'a> Prettier<'a> {
}
#[inline]
pub(crate) fn alloc(&self, doc: Doc<'a>) -> Box<'a, Doc<'a>> {
pub(crate) fn boxed(&self, doc: Doc<'a>) -> Box<'a, Doc<'a>> {
Box(self.allocator.alloc(doc))
}

View file

@ -1,5 +1,5 @@
use oxc_allocator::Vec;
use oxc_ast::ast::*;
use oxc_ast::{ast::*, AstKind};
use crate::{doc::Doc, format::array, hardline, indent, ss, Prettier};
@ -15,6 +15,20 @@ pub(super) fn print_block<'a>(
if let Some(doc) = print_block_body(p, stmts, directives, true, false) {
parts.push(indent![p, hardline!(), doc]);
parts.push(hardline!());
} else {
let parent = p.parent_kind();
if !(matches!(
parent,
AstKind::FunctionBody(_)
| AstKind::ArrowExpression(_)
| AstKind::Function(_)
| AstKind::ForStatement(_)
| AstKind::WhileStatement(_)
| AstKind::DoWhileStatement(_)
) || matches!(p.current_kind(), AstKind::StaticBlock(_)))
{
parts.push(hardline!());
}
}
parts.push(ss!("}"));
Doc::Array(parts)

View file

@ -5,12 +5,6 @@
#![allow(unused_variables)]
use std::borrow::Cow;
use oxc_allocator::{Box, Vec};
use oxc_ast::ast::*;
use oxc_span::GetSpan;
mod array;
mod arrow_function;
mod binaryish;
@ -25,10 +19,16 @@ mod statement;
mod string;
mod ternary;
use std::borrow::Cow;
use oxc_allocator::{Box, Vec};
use oxc_ast::{ast::*, AstKind};
use oxc_span::GetSpan;
use crate::{
array,
doc::{Doc, Separator},
format, group, hardline, indent, softline, ss, string, Prettier,
format, group, hardline, indent, softline, ss, string, wrap, Prettier,
};
use self::{
@ -53,6 +53,7 @@ where
impl<'a> Format<'a> for Program<'a> {
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));
@ -65,6 +66,7 @@ impl<'a> Format<'a> for Program<'a> {
{
parts.push(doc);
}
p.leave_node();
Doc::Array(parts)
}
}
@ -155,42 +157,44 @@ impl<'a> Format<'a> for IfStatement<'a> {
impl<'a> Format<'a> for BlockStatement<'a> {
fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> {
block::print_block(p, &self.body, None)
wrap!(p, self, BlockStatement, { block::print_block(p, &self.body, None) })
}
}
impl<'a> Format<'a> for ForStatement<'a> {
fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> {
let mut parts = p.vec();
wrap!(p, self, ForStatement, {
let mut parts = p.vec();
parts.push(ss!("for ("));
parts.push(ss!("for ("));
let mut parts_head = p.vec();
let mut parts_head = p.vec();
if let Some(init) = &self.init {
parts_head.push(format!(p, init));
}
parts_head.push(ss!(";"));
parts_head.push(Doc::Line);
if let Some(init) = &self.test {
parts_head.push(format!(p, init));
}
parts_head.push(ss!(";"));
parts_head.push(Doc::Line);
if let Some(init) = &self.update {
parts_head.push(format!(p, init));
}
if let Some(init) = &self.init {
parts_head.push(format!(p, init));
}
parts_head.push(ss!(";"));
parts_head.push(Doc::Line);
if let Some(init) = &self.test {
parts_head.push(format!(p, init));
}
parts_head.push(ss!(";"));
parts_head.push(Doc::Line);
if let Some(init) = &self.update {
parts_head.push(format!(p, init));
}
let parts_head = indent!(p, group!(p, Doc::Array(parts_head)));
let parts_head = indent!(p, group!(p, Doc::Array(parts_head)));
parts.push(group!(p, parts_head));
parts.push(group!(p, parts_head));
parts.push(ss!(")"));
parts.push(ss!(")"));
let body = format!(p, self.body);
parts.push(adjust_clause(p, &self.body, body, false));
let body = format!(p, self.body);
parts.push(adjust_clause(p, &self.body, body, false));
Doc::Group(parts)
Doc::Group(parts)
})
}
}
@ -255,43 +259,47 @@ impl<'a> Format<'a> for ForStatementLeft<'a> {
impl<'a> Format<'a> for WhileStatement<'a> {
fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> {
let mut parts = p.vec();
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(ss!("while ("));
parts.push(group!(p, indent!(p, softline!(), format!(p, self.test)), softline!()));
parts.push(ss!(")"));
let body = format!(p, self.body);
parts.push(adjust_clause(p, &self.body, body, false));
let body = format!(p, self.body);
parts.push(adjust_clause(p, &self.body, body, false));
Doc::Group(parts)
Doc::Group(parts)
})
}
}
impl<'a> Format<'a> for DoWhileStatement<'a> {
fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> {
let mut parts = p.vec();
wrap!(p, self, DoWhileStatement, {
let mut parts = p.vec();
let clause = format!(p, self.body);
let clause = adjust_clause(p, &self.body, clause, false);
let do_body = group!(p, ss!("do"), clause);
let clause = format!(p, self.body);
let clause = adjust_clause(p, &self.body, clause, false);
let do_body = group!(p, ss!("do"), clause);
parts.push(do_body);
parts.push(do_body);
if matches!(self.body, Statement::BlockStatement(_)) {
parts.push(ss!(" "));
} else {
parts.push(hardline!());
}
if matches!(self.body, Statement::BlockStatement(_)) {
parts.push(ss!(" "));
} else {
parts.push(hardline!());
}
parts.push(ss!("while ("));
parts.push(group!(p, indent!(p, softline!(), format!(p, self.test)), softline!()));
parts.push(ss!(")"));
if p.options.semi {
parts.push(ss!(";"));
}
parts.push(ss!("while ("));
parts.push(group!(p, indent!(p, softline!(), format!(p, self.test)), softline!()));
parts.push(ss!(")"));
if p.options.semi {
parts.push(ss!(";"));
}
Doc::Array(parts)
Doc::Array(parts)
})
}
}
@ -402,6 +410,7 @@ impl<'a> Format<'a> for LabeledStatement<'a> {
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));
@ -413,6 +422,7 @@ impl<'a> Format<'a> for TryStatement<'a> {
parts.push(ss!(" finally "));
parts.push(format!(p, finalizer));
}
p.leave_node();
Doc::Array(parts)
}
}
@ -854,13 +864,15 @@ impl<'a> Format<'a> for VariableDeclarator<'a> {
impl<'a> Format<'a> for Function<'a> {
fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> {
function::print_function(p, self, None)
wrap!(p, self, Function, { function::print_function(p, self, None) })
}
}
impl<'a> Format<'a> for FunctionBody<'a> {
fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> {
block::print_block(p, &self.statements, Some(&self.directives))
wrap!(p, self, FunctionBody, {
block::print_block(p, &self.statements, Some(&self.directives))
})
}
}
@ -1325,11 +1337,13 @@ impl<'a> Format<'a> for ObjectProperty<'a> {
}
if method {
if let Expression::FunctionExpression(func_expr) = &self.value {
parts.push(function::print_function(
p,
func_expr,
Some(self.key.span().source_text(p.source_text)),
));
wrap!(p, func_expr, Function, {
parts.push(function::print_function(
p,
func_expr,
Some(self.key.span().source_text(p.source_text)),
));
});
}
} else {
parts.push(format!(p, self.key));
@ -1367,7 +1381,7 @@ impl<'a> Format<'a> for PropertyKey<'a> {
impl<'a> Format<'a> for ArrowExpression<'a> {
fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> {
arrow_function::print_arrow_function(p, self)
wrap!(p, self, ArrowExpression, { arrow_function::print_arrow_function(p, self) })
}
}
@ -1815,7 +1829,9 @@ impl<'a> Format<'a> for JSXFragment<'a> {
impl<'a> Format<'a> for StaticBlock<'a> {
fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> {
array![p, ss!("static "), block::print_block(p, &self.body, None)]
wrap!(p, self, StaticBlock, {
array![p, ss!("static "), block::print_block(p, &self.body, None)]
})
}
}

View file

@ -13,12 +13,12 @@ mod printer;
use std::{iter::Peekable, vec};
use doc::Doc;
use oxc_allocator::Allocator;
use oxc_ast::{ast::Program, CommentKind, Trivias};
use oxc_ast::{ast::Program, AstKind, CommentKind, Trivias};
use crate::{doc::Doc, format::Format, printer::Printer};
pub use crate::options::{ArrowParens, EndOfLine, PrettierOptions, QuoteProps, TrailingComma};
use crate::{format::Format, printer::Printer};
pub struct Prettier<'a> {
allocator: &'a Allocator,
@ -29,6 +29,10 @@ pub struct Prettier<'a> {
/// A stack of comments that will be carefully placed in the right places.
trivias: Peekable<vec::IntoIter<(u32, u32, CommentKind)>>,
/// The stack of AST Nodes
/// See <https://github.com/prettier/prettier/blob/main/src/common/ast-path.js>
nodes: Vec<AstKind<'a>>,
}
impl<'a> Prettier<'a> {
@ -38,8 +42,13 @@ impl<'a> Prettier<'a> {
trivias: Trivias,
options: PrettierOptions,
) -> Self {
let trivias = trivias.into_iter().peekable();
Self { allocator, source_text, options, trivias }
Self {
allocator,
source_text,
options,
trivias: trivias.into_iter().peekable(),
nodes: vec![],
}
}
pub fn build(mut self, program: &Program<'a>) -> String {
@ -51,21 +60,46 @@ impl<'a> Prettier<'a> {
program.format(&mut self)
}
pub(crate) fn should_print_es5_comma(&self) -> bool {
fn enter_node(&mut self, kind: AstKind<'a>) {
self.nodes.push(kind);
}
fn leave_node(&mut self) {
self.nodes.pop();
}
fn current_kind(&self) -> AstKind<'a> {
self.nodes[self.nodes.len() - 1]
}
fn parent_kind(&self) -> AstKind<'a> {
self.nodes[self.nodes.len() - 2]
}
/// A hack for erasing the lifetime requirement.
#[allow(clippy::unused_self)]
fn alloc<T>(&self, t: &T) -> &'a T {
// SAFETY:
// This should be safe as long as `src` is an reference from the allocator.
// But honestly, I'm not really sure if this is safe.
unsafe { std::mem::transmute(t) }
}
fn should_print_es5_comma(&self) -> bool {
self.should_print_comma_impl(false)
}
#[allow(unused)]
pub(crate) fn should_print_all_comma(&self) -> bool {
fn should_print_all_comma(&self) -> bool {
self.should_print_comma_impl(true)
}
pub(crate) fn should_print_comma_impl(&self, level_all: bool) -> bool {
fn should_print_comma_impl(&self, level_all: bool) -> bool {
let trailing_comma = self.options.trailing_comma;
trailing_comma.is_all() || (trailing_comma.is_es5() && !level_all)
}
pub(crate) fn is_next_line_empty(&self, end: u32) -> bool {
fn is_next_line_empty(&self, end: u32) -> bool {
self.source_text[end as usize..].chars().nth(1).is_some_and(|c| c == '\n')
}
}

View file

@ -85,6 +85,16 @@ macro_rules! group {
#[macro_export]
macro_rules! if_break {
($p:ident, $s:expr) => {{
Doc::IfBreak($p.alloc(Doc::Str($s)))
Doc::IfBreak($p.boxed(Doc::Str($s)))
}};
}
#[macro_export]
macro_rules! wrap {
($p:ident, $self:expr, $kind:ident, $block:block) => {{
$p.enter_node(AstKind::$kind($p.alloc($self)));
let doc = $block;
$p.leave_node();
doc
}};
}

View file

@ -1,4 +1,4 @@
Compatibility: 105/838 (12.53%)
Compatibility: 108/838 (12.89%)
# Failed
@ -616,9 +616,6 @@ Compatibility: 105/838 (12.53%)
### line-suffix-boundary
* line-suffix-boundary/boundary.js
### logical-assignment
* logical-assignment/logical-assignment.js
### logical_expressions
* logical_expressions/issue-7024.js
* logical_expressions/logical_expression_operators.js
@ -658,7 +655,6 @@ Compatibility: 105/838 (12.53%)
### module-blocks
* module-blocks/comments.js
* module-blocks/module-blocks.js
* module-blocks/non-module-blocks.js
* module-blocks/range.js
* module-blocks/worker.js
@ -765,9 +761,6 @@ Compatibility: 105/838 (12.53%)
* objects/assignment-expression/object-property.js
* objects/assignment-expression/object-value.js
### optional-catch-binding
* optional-catch-binding/optional_catch_binding.js
### optional-chaining
* optional-chaining/chaining.js
* optional-chaining/comments.js