From 65be4acdd4da5dd49abcfbae29c7e7bdbbdbc145 Mon Sep 17 00:00:00 2001 From: Boshen Date: Mon, 13 Nov 2023 14:34:20 +0800 Subject: [PATCH] feat(prettier): init project and infrastructure (#1260) > [!NOTE] > This is going to be a community project because I don't have the time and energy to work on this alone. # Prettier Background: 22.5K USD bounty for prettier written in Rust?! See https://console.algora.io/challenges/prettier > [!WARNING] > ## Contribution Agreement > > You hereby agree that you contribute for fun and for the purpose of learning, not for the goal of winning the challenge. > > In the unlikely event of winning the challenge, @boshen will ultimately decide on how to spend the money. > > [!IMPORTANT] Please talk to me on [discord](https://discord.com/invite/9uXCAwqQZW) and indicate that you are willing to contribute and agree to the contribution agreement. ## Getting started Create a `test.js` and run the example `just example prettier` from `crates/oxc_prettier/examples/prettier.rs`, follow the code structure and read the references documented at the top of the files. # Tasks - [x] Have the basic infrastructure ready for contribution - [ ] Implement a test runner in Rust which extracts the snapshots and do a comparison over it - [ ] Establish a way to pass all the tests by manually porting code - [ ] Pass as many tests as possible in https://github.com/prettier/prettier/tree/main/tests/format/js --- Cargo.lock | 11 + crates/oxc_prettier/Cargo.toml | 25 + crates/oxc_prettier/README.md | 30 + crates/oxc_prettier/examples/prettier.rs | 23 + crates/oxc_prettier/src/document.rs | 34 + crates/oxc_prettier/src/format.rs | 967 +++++++++++++++++++++++ crates/oxc_prettier/src/lib.rs | 32 + crates/oxc_prettier/src/macros.rs | 48 ++ crates/oxc_prettier/src/printer.rs | 86 ++ 9 files changed, 1256 insertions(+) create mode 100644 crates/oxc_prettier/Cargo.toml create mode 100644 crates/oxc_prettier/README.md create mode 100644 crates/oxc_prettier/examples/prettier.rs create mode 100644 crates/oxc_prettier/src/document.rs create mode 100644 crates/oxc_prettier/src/format.rs create mode 100644 crates/oxc_prettier/src/lib.rs create mode 100644 crates/oxc_prettier/src/macros.rs create mode 100644 crates/oxc_prettier/src/printer.rs diff --git a/Cargo.lock b/Cargo.lock index 9e872ff1d..51dda98d1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1768,6 +1768,17 @@ dependencies = [ "serde_json", ] +[[package]] +name = "oxc_prettier" +version = "0.0.0" +dependencies = [ + "oxc_allocator", + "oxc_ast", + "oxc_parser", + "oxc_span", + "oxc_syntax", +] + [[package]] name = "oxc_query" version = "0.0.0" diff --git a/crates/oxc_prettier/Cargo.toml b/crates/oxc_prettier/Cargo.toml new file mode 100644 index 000000000..9c45ca4a5 --- /dev/null +++ b/crates/oxc_prettier/Cargo.toml @@ -0,0 +1,25 @@ +[package] +name = "oxc_prettier" +version = "0.0.0" +publish = false +authors.workspace = true +description.workspace = true +edition.workspace = true +homepage.workspace = true +keywords.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true +categories.workspace = true + +[lib] +doctest = false + +[dependencies] +oxc_allocator = { workspace = true } +oxc_ast = { workspace = true } +oxc_syntax = { workspace = true } + +[dev-dependencies] +oxc_parser = { workspace = true } +oxc_span = { workspace = true } diff --git a/crates/oxc_prettier/README.md b/crates/oxc_prettier/README.md new file mode 100644 index 000000000..1a3b09766 --- /dev/null +++ b/crates/oxc_prettier/README.md @@ -0,0 +1,30 @@ +> [!NOTE] +> This is going to be a community project because I don't have the time and energy to work on this alone. + +# Prettier + +Background: 22.5K USD bounty for prettier written in Rust?! + +See https://console.algora.io/challenges/prettier + +> [!WARNING] +> ## Contribution Agreement +> +> You hereby agree that you contribute for fun and for the purpose of learning, not for the goal of winning the challenge. +> +> In the unlikely event of winning the challenge, @boshen will ultimately decide on how to spend the money. +> + +> [!IMPORTANT] +Please talk to me on [discord](https://discord.com/invite/9uXCAwqQZW) and indicate that you are willing to contribute and agree to the contribution agreement. + +## Getting started + +Create a `test.js` and run the example `just example prettier` from `crates/oxc_prettier/examples/prettier.rs`, follow the code structure and read the references documented at the top of the files. + +# Tasks + +- [x] Have the basic infrastructure ready for contribution +- [ ] Implement a test runner in Rust which extracts the snapshots and do a comparison over it +- [ ] Establish a way to pass all the tests by manually porting code +- [ ] Pass as many tests as possible in https://github.com/prettier/prettier/tree/main/tests/format/js diff --git a/crates/oxc_prettier/examples/prettier.rs b/crates/oxc_prettier/examples/prettier.rs new file mode 100644 index 000000000..d4db27b1d --- /dev/null +++ b/crates/oxc_prettier/examples/prettier.rs @@ -0,0 +1,23 @@ +use std::{env, path::Path}; + +use oxc_allocator::Allocator; +use oxc_parser::Parser; +use oxc_span::SourceType; + +use oxc_prettier::{Prettier, PrettierOptions}; + +// Instruction: +// create a `test.js`, +// run `cargo run -p oxc_prettier --example prettier` +// or `just example prettier` + +fn main() { + let name = env::args().nth(1).unwrap_or_else(|| "test.js".to_string()); + let path = Path::new(&name); + let source_text = std::fs::read_to_string(path).unwrap_or_else(|_| panic!("{name} not found")); + let allocator = Allocator::default(); + let source_type = SourceType::from_path(path).unwrap(); + let ret = Parser::new(&allocator, &source_text, source_type).parse(); + let output = Prettier::new(&allocator, PrettierOptions).build(&ret.program); + println!("{output}"); +} diff --git a/crates/oxc_prettier/src/document.rs b/crates/oxc_prettier/src/document.rs new file mode 100644 index 000000000..a3137603b --- /dev/null +++ b/crates/oxc_prettier/src/document.rs @@ -0,0 +1,34 @@ +//! Prettier IR +//! +//! References: +//! * + +use oxc_allocator::{String, Vec}; + +use crate::Prettier; + +pub enum Doc<'a> { + Str(&'a str), + // perf: can we use &[Doc] here? + Array(Vec<'a, Doc<'a>>), + Indent(Vec<'a, Doc<'a>>), + Group(Vec<'a, Doc<'a>>), + /// Specify a line break. + /// If an expression fits on one line, the line break will be replaced with a space. + /// Line breaks always indent the next line with the current level of indentation. + Line, + Softline, +} + +/// Doc Builder +impl<'a> Prettier<'a> { + #[inline] + pub fn vec(&self) -> Vec<'a, T> { + Vec::new_in(self.allocator) + } + + #[inline] + pub fn str(&self, s: &str) -> Doc<'a> { + Doc::Str(String::from_str_in(s, self.allocator).into_bump_str()) + } +} diff --git a/crates/oxc_prettier/src/format.rs b/crates/oxc_prettier/src/format.rs new file mode 100644 index 000000000..cf796a7c1 --- /dev/null +++ b/crates/oxc_prettier/src/format.rs @@ -0,0 +1,967 @@ +//! Formatting logic +//! +//! References: +//! * + +#![allow(unused_variables)] + +use oxc_allocator::{Box, Vec}; +#[allow(clippy::wildcard_imports)] +use oxc_ast::ast::*; +use oxc_syntax::operator::BinaryOperator; + +use crate::{document::Doc, format, group, indent, softline, string, Prettier}; + +pub trait Format<'a> { + #[must_use] + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a>; +} + +impl<'a, T> Format<'a> for Box<'a, T> +where + T: Format<'a>, +{ + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + (**self).format(p) + } +} + +impl<'a> Format<'a> for Program<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + let mut parts = p.vec(); + parts.extend(self.body.iter().map(|stmt| stmt.format(p))); + Doc::Array(parts) + } +} + +impl<'a> Format<'a> for Directive { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for Statement<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + match self { + Self::BlockStatement(stmt) => stmt.format(p), + Self::BreakStatement(stmt) => stmt.format(p), + Self::ContinueStatement(stmt) => stmt.format(p), + Self::DebuggerStatement(stmt) => stmt.format(p), + Self::DoWhileStatement(stmt) => stmt.format(p), + Self::EmptyStatement(stmt) => stmt.format(p), + Self::ExpressionStatement(stmt) => stmt.format(p), + Self::ForInStatement(stmt) => stmt.format(p), + Self::ForOfStatement(stmt) => stmt.format(p), + Self::ForStatement(stmt) => stmt.format(p), + Self::IfStatement(stmt) => stmt.format(p), + Self::LabeledStatement(stmt) => stmt.format(p), + Self::ModuleDeclaration(decl) => decl.format(p), + Self::ReturnStatement(stmt) => stmt.format(p), + Self::SwitchStatement(stmt) => stmt.format(p), + Self::ThrowStatement(stmt) => stmt.format(p), + Self::TryStatement(stmt) => stmt.format(p), + Self::WhileStatement(stmt) => stmt.format(p), + Self::WithStatement(stmt) => stmt.format(p), + Self::Declaration(decl) => decl.format(p), + } + } +} + +impl<'a> Format<'a> for ExpressionStatement<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + self.expression.format(p) + } +} + +impl<'a> Format<'a> for EmptyStatement { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for IfStatement<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + let mut parts = p.vec(); + + let opening = group![ + p, + string!(p, "if ("), + group!(p, indent!(p, softline!(), format!(p, self.test), softline!())), + string!(p, ")"), + format!(p, self.consequent) + ]; + parts.push(opening); + + if let Some(alternate) = &self.alternate { + parts.push(string!(p, "else")); + parts.push(group!(p, format!(p, alternate))); + } + + Doc::Array(parts) + } +} + +impl<'a> Format<'a> for BlockStatement<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + let mut parts = p.vec(); + parts.push(p.str("{")); + parts.push(Doc::Softline); + parts.extend(self.body.iter().map(|stmt| stmt.format(p))); + parts.push(Doc::Softline); + parts.push(p.str("}")); + Doc::Array(parts) + } +} + +impl<'a> Format<'a> for ForStatement<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for ForInStatement<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for ForOfStatement<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for ForStatementLeft<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for WhileStatement<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for DoWhileStatement<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for ContinueStatement { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for BreakStatement { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for SwitchStatement<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for SwitchCase<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for ReturnStatement<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + string!(p, "return") + } +} + +impl<'a> Format<'a> for LabeledStatement<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for TryStatement<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for ThrowStatement<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for WithStatement<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for DebuggerStatement { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for ModuleDeclaration<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for Declaration<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + match self { + Self::VariableDeclaration(stmt) => stmt.format(p), + Self::FunctionDeclaration(stmt) => stmt.format(p), + Self::ClassDeclaration(decl) => decl.format(p), + Self::UsingDeclaration(decl) => decl.format(p), + Self::TSTypeAliasDeclaration(decl) => decl.format(p), + Self::TSInterfaceDeclaration(decl) => decl.format(p), + Self::TSEnumDeclaration(decl) => decl.format(p), + Self::TSModuleDeclaration(decl) => decl.format(p), + Self::TSImportEqualsDeclaration(decl) => decl.format(p), + } + } +} + +impl<'a> Format<'a> for VariableDeclaration<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for UsingDeclaration<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for TSTypeAliasDeclaration<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for TSInterfaceDeclaration<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for TSEnumDeclaration<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for TSModuleDeclaration<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for TSImportEqualsDeclaration<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for VariableDeclarator<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for Function<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + let mut parts = p.vec(); + parts.push(p.str("function ")); + if let Some(id) = &self.id { + parts.push(p.str(id.name.as_str())); + } + parts.push(self.params.format(p)); + if let Some(body) = &self.body { + parts.push(p.str(" ")); + parts.push(body.format(p)); + } + Doc::Array(parts) + } +} + +impl<'a> Format<'a> for FunctionBody<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + let mut parts = p.vec(); + parts.push(p.str("{")); + parts.push(Doc::Softline); + parts.extend(self.statements.iter().map(|stmt| stmt.format(p))); + parts.push(Doc::Softline); + parts.push(p.str("}")); + Doc::Array(parts) + } +} + +impl<'a> Format<'a> for FormalParameters<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + let mut parts = p.vec(); + parts.push(p.str("(")); + parts.extend(self.items.iter().map(|stmt| stmt.format(p))); + if let Some(rest) = &self.rest { + parts.push(rest.format(p)); + } + parts.push(p.str(")")); + Doc::Array(parts) + } +} + +impl<'a> Format<'a> for FormalParameter<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for ImportDeclaration<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for Option> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for ImportAttribute { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for ExportNamedDeclaration<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for ExportSpecifier { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for ModuleExportName { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for ExportAllDeclaration<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for ExportDefaultDeclaration<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} +impl<'a> Format<'a> for ExportDefaultDeclarationKind<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for Expression<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + match self { + Self::BooleanLiteral(lit) => lit.format(p), + Self::NullLiteral(lit) => lit.format(p), + Self::NumberLiteral(lit) => lit.format(p), + Self::BigintLiteral(lit) => lit.format(p), + Self::RegExpLiteral(lit) => lit.format(p), + Self::StringLiteral(lit) => lit.format(p), + Self::Identifier(ident) => ident.format(p), + Self::ThisExpression(expr) => expr.format(p), + Self::MemberExpression(expr) => expr.format(p), + Self::CallExpression(expr) => expr.format(p), + Self::ArrayExpression(expr) => expr.format(p), + Self::ObjectExpression(expr) => expr.format(p), + Self::FunctionExpression(expr) => expr.format(p), + Self::ArrowExpression(expr) => expr.format(p), + Self::YieldExpression(expr) => expr.format(p), + Self::UpdateExpression(expr) => expr.format(p), + Self::UnaryExpression(expr) => expr.format(p), + Self::BinaryExpression(expr) => expr.format(p), + Self::PrivateInExpression(expr) => expr.format(p), + Self::LogicalExpression(expr) => expr.format(p), + Self::ConditionalExpression(expr) => expr.format(p), + Self::AssignmentExpression(expr) => expr.format(p), + Self::SequenceExpression(expr) => expr.format(p), + Self::ParenthesizedExpression(expr) => expr.format(p), + Self::ImportExpression(expr) => expr.format(p), + Self::TemplateLiteral(literal) => literal.format(p), + Self::TaggedTemplateExpression(expr) => expr.format(p), + Self::Super(sup) => sup.format(p), + Self::AwaitExpression(expr) => expr.format(p), + Self::ChainExpression(expr) => expr.format(p), + Self::NewExpression(expr) => expr.format(p), + Self::MetaProperty(expr) => expr.format(p), + Self::ClassExpression(expr) => expr.format(p), + Self::JSXElement(el) => el.format(p), + Self::JSXFragment(fragment) => fragment.format(p), + Self::TSAsExpression(expr) => expr.expression.format(p), + Self::TSSatisfiesExpression(expr) => expr.expression.format(p), + Self::TSTypeAssertion(expr) => expr.expression.format(p), + Self::TSNonNullExpression(expr) => expr.expression.format(p), + Self::TSInstantiationExpression(expr) => expr.expression.format(p), + } + } +} + +impl<'a> Format<'a> for IdentifierReference { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + p.str(self.name.as_str()) + } +} + +impl<'a> Format<'a> for IdentifierName { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + p.str(self.name.as_str()) + } +} + +impl<'a> Format<'a> for BindingIdentifier { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + p.str(self.name.as_str()) + } +} + +impl<'a> Format<'a> for LabelIdentifier { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + p.str(self.name.as_str()) + } +} + +impl<'a> Format<'a> for BooleanLiteral { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for NullLiteral { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for NumberLiteral<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for BigintLiteral { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for RegExpLiteral { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for StringLiteral { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for ThisExpression { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for MemberExpression<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for ComputedMemberExpression<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for StaticMemberExpression<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for PrivateFieldExpression<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for CallExpression<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for Argument<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for ArrayExpressionElement<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for SpreadElement<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for ArrayExpression<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for ObjectExpression<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for ObjectPropertyKind<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for ObjectProperty<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for PropertyKey<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for ArrowExpression<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for YieldExpression<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for UpdateExpression<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for UnaryExpression<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for BinaryExpression<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for BinaryOperator { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for PrivateInExpression<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for LogicalExpression<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for ConditionalExpression<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for AssignmentExpression<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for AssignmentTarget<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for SimpleAssignmentTarget<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for AssignmentTargetPattern<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for ArrayAssignmentTarget<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for Option> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for ObjectAssignmentTarget<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for AssignmentTargetMaybeDefault<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for AssignmentTargetWithDefault<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for AssignmentTargetProperty<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for AssignmentTargetPropertyIdentifier<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for AssignmentTargetPropertyProperty<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for SequenceExpression<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for ParenthesizedExpression<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for ImportExpression<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for TemplateLiteral<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for TaggedTemplateExpression<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for Super { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for AwaitExpression<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for ChainExpression<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for NewExpression<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for MetaProperty { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for Class<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for ClassElement<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for JSXIdentifier { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for JSXMemberExpressionObject<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for JSXMemberExpression<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for JSXElementName<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for JSXNamespacedName { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for JSXAttributeName<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for JSXAttribute<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for JSXEmptyExpression { + fn format(&self, _: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for JSXExpression<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for JSXExpressionContainer<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for JSXAttributeValue<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for JSXSpreadAttribute<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for JSXAttributeItem<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for JSXOpeningElement<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for JSXClosingElement<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for JSXElement<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for JSXOpeningFragment { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for JSXClosingFragment { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for JSXText { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for JSXSpreadChild<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for JSXChild<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for JSXFragment<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for StaticBlock<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for MethodDefinition<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for PropertyDefinition<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for AccessorProperty<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for PrivateIdentifier { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for BindingPattern<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for ObjectPattern<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for BindingProperty<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for RestElement<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for ArrayPattern<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} + +impl<'a> Format<'a> for AssignmentPattern<'a> { + fn format(&self, p: &mut Prettier<'a>) -> Doc<'a> { + Doc::Line + } +} diff --git a/crates/oxc_prettier/src/lib.rs b/crates/oxc_prettier/src/lib.rs new file mode 100644 index 000000000..9d6237bdc --- /dev/null +++ b/crates/oxc_prettier/src/lib.rs @@ -0,0 +1,32 @@ +//! Prettier +//! +//! A port of + +mod document; +mod format; +mod macros; +mod printer; + +use oxc_allocator::Allocator; +use oxc_ast::ast::Program; + +use crate::{format::Format, printer::Printer}; + +pub struct PrettierOptions; + +pub struct Prettier<'a> { + allocator: &'a Allocator, + + _options: PrettierOptions, +} + +impl<'a> Prettier<'a> { + pub fn new(allocator: &'a Allocator, _options: PrettierOptions) -> Self { + Self { allocator, _options } + } + + pub fn build(mut self, program: &Program<'a>) -> String { + let doc = program.format(&mut self); + Printer::new(doc).build() + } +} diff --git a/crates/oxc_prettier/src/macros.rs b/crates/oxc_prettier/src/macros.rs new file mode 100644 index 000000000..ffade8f67 --- /dev/null +++ b/crates/oxc_prettier/src/macros.rs @@ -0,0 +1,48 @@ +//! Utility macros for constructing the IR + +#[macro_export] +macro_rules! format { + ($p:ident, $s:expr) => {{ + $s.format($p) + }}; +} + +#[macro_export] +macro_rules! string { + ($p:ident, $s:expr) => {{ + $p.str($s) + }}; +} + +#[macro_export] +macro_rules! group { + ($p:ident, $( $x:expr ),* ) => { + { + let mut temp_vec = $p.vec(); + $( + temp_vec.push($x); + )* + Doc::Group(temp_vec) + } + }; +} + +#[macro_export] +macro_rules! indent { + ($p:ident, $( $x:expr ),* ) => { + { + let mut temp_vec = $p.vec(); + $( + temp_vec.push($x); + )* + Doc::Indent(temp_vec) + } + }; +} + +#[macro_export] +macro_rules! softline { + () => { + Doc::Softline + }; +} diff --git a/crates/oxc_prettier/src/printer.rs b/crates/oxc_prettier/src/printer.rs new file mode 100644 index 000000000..ab00222ae --- /dev/null +++ b/crates/oxc_prettier/src/printer.rs @@ -0,0 +1,86 @@ +//! [Doc] Printer +//! +//! References: +//! * + +#![allow(unused)] + +use crate::document::Doc; + +struct Command<'a> { + indent: Indent, + mode: Mode, + doc: Doc<'a>, +} + +impl<'a> Command<'a> { + fn new(indent: Indent, mode: Mode, doc: Doc<'a>) -> Self { + Self { indent, mode, doc } + } +} + +#[derive(Clone, Copy)] +enum Mode { + Break, + Flat, +} + +#[derive(Clone, Copy)] +struct Indent { + value: &'static str, + length: u8, +} + +impl Indent { + fn root() -> Self { + Self { value: "", length: 0 } + } +} + +pub struct Printer<'a> { + doc: Doc<'a>, +} + +impl<'a> Printer<'a> { + pub fn new(doc: Doc<'a>) -> Self { + Self { doc } + } + + pub fn build(self) -> String { + self.print_doc_to_string() + } +} + +impl<'a> Printer<'a> { + /// Turn Doc into a string + /// + /// Reference: + /// * + fn print_doc_to_string(self) -> String { + let mut out = vec![]; + let mut cmds: Vec = vec![Command::new(Indent::root(), Mode::Break, self.doc)]; + + while let Some(Command { indent, doc, mode }) = cmds.pop() { + match doc { + Doc::Str(string) => { + out.extend(string.as_bytes()); + } + Doc::Array(docs) => { + cmds.extend(docs.into_iter().rev().map(|doc| Command::new(indent, mode, doc))); + } + Doc::Indent(docs) => { + cmds.extend(docs.into_iter().rev().map(|doc| Command::new(indent, mode, doc))); + } + Doc::Group(docs) => { + cmds.extend(docs.into_iter().rev().map(|doc| Command::new(indent, mode, doc))); + } + Doc::Line | Doc::Softline => { + out.push(b'\n'); + } + } + } + + // SAFETY: We should have constructed valid UTF8 strings + unsafe { String::from_utf8_unchecked(out) } + } +}