mirror of
https://github.com/danbulant/oxc
synced 2026-05-24 12:21:58 +00:00
feat(prettier): add the basics of comment printing (#1313)
This commit is contained in:
parent
4967ca2cdb
commit
5f316626f6
11 changed files with 134 additions and 15 deletions
|
|
@ -37,6 +37,16 @@ pub enum CommentKind {
|
|||
MultiLine,
|
||||
}
|
||||
|
||||
impl CommentKind {
|
||||
pub fn is_single_line(self) -> bool {
|
||||
matches!(self, Self::SingleLine)
|
||||
}
|
||||
|
||||
pub fn is_multi_line(self) -> bool {
|
||||
matches!(self, Self::MultiLine)
|
||||
}
|
||||
}
|
||||
|
||||
impl Comment {
|
||||
pub fn new(end: u32, kind: CommentKind) -> Self {
|
||||
Self { kind, end }
|
||||
|
|
@ -46,12 +56,16 @@ impl Comment {
|
|||
self.end
|
||||
}
|
||||
|
||||
pub fn kind(&self) -> CommentKind {
|
||||
self.kind
|
||||
}
|
||||
|
||||
pub fn is_single_line(self) -> bool {
|
||||
matches!(self.kind, CommentKind::SingleLine)
|
||||
self.kind.is_single_line()
|
||||
}
|
||||
|
||||
pub fn is_multi_line(self) -> bool {
|
||||
matches!(self.kind, CommentKind::MultiLine)
|
||||
self.kind.is_multi_line()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -52,6 +52,7 @@ impl FormatRunner {
|
|||
let allocator = Allocator::default();
|
||||
let source_type = SourceType::from_path(path).unwrap();
|
||||
let ret = Parser::new(&allocator, &source_text, source_type).parse();
|
||||
let _ = Prettier::new(&allocator, PrettierOptions::default()).build(&ret.program);
|
||||
let _ = Prettier::new(&allocator, &source_text, ret.trivias, PrettierOptions::default())
|
||||
.build(&ret.program);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ doctest = false
|
|||
oxc_allocator = { workspace = true }
|
||||
oxc_ast = { workspace = true }
|
||||
oxc_syntax = { workspace = true }
|
||||
oxc_span = { workspace = true }
|
||||
|
||||
[dev-dependencies]
|
||||
oxc_parser = { workspace = true }
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ fn main() {
|
|||
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::default()).build(&ret.program);
|
||||
let output = Prettier::new(&allocator, &source_text, ret.trivias, PrettierOptions::default())
|
||||
.build(&ret.program);
|
||||
println!("{output}");
|
||||
}
|
||||
|
|
|
|||
56
crates/oxc_prettier/src/comment.rs
Normal file
56
crates/oxc_prettier/src/comment.rs
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
//! Comment helpers
|
||||
|
||||
use oxc_ast::CommentKind;
|
||||
use oxc_span::Span;
|
||||
|
||||
use crate::{
|
||||
doc::{Doc, Separator},
|
||||
Prettier,
|
||||
};
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[allow(unused)]
|
||||
pub enum CommentFlags {
|
||||
/// Check comment is a leading comment
|
||||
Leading,
|
||||
/// Check comment is a trailing comment
|
||||
Trailing,
|
||||
/// Check comment is a dangling comment
|
||||
Dangling,
|
||||
/// Check comment is a block comment
|
||||
Block,
|
||||
/// Check comment is a line comment
|
||||
Line,
|
||||
/// Check comment is a `prettier-ignore` comment
|
||||
PrettierIgnore,
|
||||
/// Check comment is the first attached comment
|
||||
First,
|
||||
/// Check comment is the last attached comment
|
||||
Last,
|
||||
}
|
||||
|
||||
impl<'a> Prettier<'a> {
|
||||
#[allow(unused)]
|
||||
pub(crate) fn has_comment(_span: Span, _flags: CommentFlags) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
pub(crate) fn print_dangling_comments(&mut self, range: Span) -> Option<Doc<'a>> {
|
||||
let mut parts = vec![];
|
||||
while let Some((start, end, kind)) = self.trivias.peek().copied() {
|
||||
if end <= range.end {
|
||||
parts.push(self.print_comment(start, end, kind));
|
||||
self.trivias.next();
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
(!parts.is_empty()).then(|| self.join(Separator::Hardline, parts))
|
||||
}
|
||||
|
||||
fn print_comment(&self, start: u32, end: u32, kind: CommentKind) -> Doc<'a> {
|
||||
let end_offset = if kind.is_multi_line() { 2 } else { 0 };
|
||||
let comment = Span::new(start - 2, end + end_offset).source_text(self.source_text);
|
||||
Doc::Str(comment)
|
||||
}
|
||||
}
|
||||
|
|
@ -47,15 +47,36 @@ impl<'a> Doc<'a> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy)]
|
||||
#[allow(unused)]
|
||||
pub enum Separator {
|
||||
Softline,
|
||||
Hardline,
|
||||
}
|
||||
|
||||
/// Doc Builder
|
||||
impl<'a> Prettier<'a> {
|
||||
#[inline]
|
||||
pub fn vec<T>(&self) -> Vec<'a, T> {
|
||||
pub(crate) fn vec<T>(&self) -> Vec<'a, T> {
|
||||
Vec::new_in(self.allocator)
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub fn str(&self, s: &str) -> Doc<'a> {
|
||||
pub(crate) fn str(&self, s: &str) -> Doc<'a> {
|
||||
Doc::Str(String::from_str_in(s, self.allocator).into_bump_str())
|
||||
}
|
||||
|
||||
pub(crate) fn join(&self, separator: Separator, docs: std::vec::Vec<Doc<'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::Softline,
|
||||
Separator::Hardline => Doc::Hardline,
|
||||
});
|
||||
}
|
||||
parts.push(doc);
|
||||
}
|
||||
Doc::Array(parts)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -30,6 +30,9 @@ 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)));
|
||||
if let Some(doc) = p.print_dangling_comments(self.span) {
|
||||
parts.push(doc);
|
||||
}
|
||||
Doc::Array(parts)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,13 +2,19 @@
|
|||
//!
|
||||
//! A port of <https://github.com/prettier/prettier>
|
||||
|
||||
mod comment;
|
||||
mod doc;
|
||||
mod format;
|
||||
mod macros;
|
||||
mod printer;
|
||||
|
||||
use std::{
|
||||
iter::{Peekable, Rev},
|
||||
vec,
|
||||
};
|
||||
|
||||
use oxc_allocator::Allocator;
|
||||
use oxc_ast::ast::Program;
|
||||
use oxc_ast::{ast::Program, CommentKind, Trivias};
|
||||
|
||||
use crate::{format::Format, printer::Printer};
|
||||
|
||||
|
|
@ -32,12 +38,23 @@ impl Default for PrettierOptions {
|
|||
pub struct Prettier<'a> {
|
||||
allocator: &'a Allocator,
|
||||
|
||||
source_text: &'a str,
|
||||
|
||||
options: PrettierOptions,
|
||||
|
||||
/// A stack of comments that will be carefully placed in the right places.
|
||||
trivias: Peekable<Rev<vec::IntoIter<(u32, u32, CommentKind)>>>,
|
||||
}
|
||||
|
||||
impl<'a> Prettier<'a> {
|
||||
pub fn new(allocator: &'a Allocator, options: PrettierOptions) -> Self {
|
||||
Self { allocator, options }
|
||||
pub fn new(
|
||||
allocator: &'a Allocator,
|
||||
source_text: &'a str,
|
||||
trivias: Trivias,
|
||||
options: PrettierOptions,
|
||||
) -> Self {
|
||||
let trivias = trivias.into_iter().rev().peekable();
|
||||
Self { allocator, source_text, options, trivias }
|
||||
}
|
||||
|
||||
pub fn build(mut self, program: &Program<'a>) -> String {
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ impl<'a> JSDocBuilder<'a> {
|
|||
fn find_jsdoc_comment(&self, span: Span) -> Option<&'a str> {
|
||||
let (start, comment) = self.trivias.comments().range(..span.start).next()?;
|
||||
|
||||
if comment.is_single_line() {
|
||||
if comment.kind().is_single_line() {
|
||||
return None;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -23,10 +23,14 @@ fn bench_prettier(criterion: &mut Criterion) {
|
|||
b.iter(|| {
|
||||
let allocator1 = Allocator::default();
|
||||
let allocator2 = Allocator::default();
|
||||
let program = Parser::new(&allocator1, source_text, SourceType::default())
|
||||
.parse()
|
||||
.program;
|
||||
let _ = Prettier::new(&allocator2, PrettierOptions::default()).build(&program);
|
||||
let ret = Parser::new(&allocator1, source_text, SourceType::default()).parse();
|
||||
let _ = Prettier::new(
|
||||
&allocator2,
|
||||
source_text,
|
||||
ret.trivias,
|
||||
PrettierOptions::default(),
|
||||
)
|
||||
.build(&ret.program);
|
||||
});
|
||||
},
|
||||
);
|
||||
|
|
|
|||
|
|
@ -146,6 +146,7 @@ printWidth: 80
|
|||
let allocator = Allocator::default();
|
||||
let source_type = SourceType::from_path(path).unwrap();
|
||||
let ret = Parser::new(&allocator, source_text, source_type).parse();
|
||||
Prettier::new(&allocator, PrettierOptions::default()).build(&ret.program)
|
||||
Prettier::new(&allocator, source_text, ret.trivias, PrettierOptions::default())
|
||||
.build(&ret.program)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue