feat(prettier): print leading comments with newlines (#1434)

This commit is contained in:
Boshen 2023-11-19 22:46:55 +08:00 committed by GitHub
parent 2ba69f1f01
commit 0218ae8641
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 76 additions and 34 deletions

View file

@ -113,7 +113,7 @@ impl<'a, 'b> DisableDirectivesBuilder<'a, 'b> {
// Get the span up to the next new line
let stop = self.source_text[span.end as usize..]
.lines()
.take(if comment.is_single_line() { 1 } else { 2 })
.take(2)
.fold(span.end, |acc, line| acc + line.len() as u32);
if text.trim().is_empty() {
self.add_interval(span.end, stop, DisabledRule::All);

View file

@ -45,9 +45,8 @@ expression: ban_ts_comment
╭─[ban_ts_comment.tsx:2:1]
2 │ if (false) {
3 │ // @ts-expect-error: Unreachable code error
· ─────────────────────────────────────────
· ─────────────────────────────────────────
4 │ console.log('hello');
5 │ }
╰────
⚠ Include a description after the @ts-expect-error directive to explain why the @ts-expect-error is necessary. The description must be 3 characters or longer.
@ -123,9 +122,8 @@ expression: ban_ts_comment
╭─[ban_ts_comment.tsx:2:1]
2 │ if (false) {
3 │ // @ts-ignore: Unreachable code error
· ───────────────────────────────────
· ───────────────────────────────────
4 │ console.log('hello');
5 │ }
╰────
⚠ Include a description after the @ts-ignore directive to explain why the @ts-ignore is necessary. The description must be 3 characters or longer.
@ -201,9 +199,8 @@ expression: ban_ts_comment
╭─[ban_ts_comment.tsx:2:1]
2 │ if (false) {
3 │ // @ts-nocheck: Unreachable code error
· ────────────────────────────────────
· ────────────────────────────────────
4 │ console.log('hello');
5 │ }
╰────
⚠ Include a description after the @ts-nocheck directive to explain why the @ts-nocheck is necessary. The description must be 3 characters or longer.
@ -267,9 +264,8 @@ expression: ban_ts_comment
╭─[ban_ts_comment.tsx:2:1]
2 │ if (false) {
3 │ // @ts-check: Unreachable code error
· ──────────────────────────────────
· ──────────────────────────────────
4 │ console.log('hello');
5 │ }
╰────
⚠ Include a description after the @ts-check directive to explain why the @ts-check is necessary. The description must be 3 characters or longer.

View file

@ -1,15 +1,13 @@
---
source: crates/oxc_linter/src/tester.rs
assertion_line: 105
expression: no_abusive_eslint_disable
---
⚠ eslint-plugin-unicorn(no-abusive-eslint-disable): Unexpected `eslint-disable` comment that does not specify any rules to disable.
╭─[no_abusive_eslint_disable.tsx:1:1]
1 │
2 │ // eslint-disable-next-line @scopewithoutplugin
· ─────────────────────────────────────────────
· ─────────────────────────────────────────────
3 │ eval();
4 │
╰────
help: Specify the rules you want to disable.
@ -24,7 +22,7 @@ expression: no_abusive_eslint_disable
╭─[no_abusive_eslint_disable.tsx:2:1]
2 │ foo();
3 │ eval(); // eslint-disable-line
· ────────────────────
· ────────────────────
4 │
╰────
help: Specify the rules you want to disable.
@ -58,9 +56,8 @@ expression: no_abusive_eslint_disable
╭─[no_abusive_eslint_disable.tsx:1:1]
1 │
2 │ // eslint-disable-next-line
· ─────────────────────────
· ─────────────────────────
3 │ eval();
4 │
╰────
help: Specify the rules you want to disable.

View file

@ -104,9 +104,8 @@ expression: no_commented_out_tests
╭─[no_commented_out_tests.tsx:1:1]
1 │
2 │ // test(
· ──────
· ──────
3 │ // "foo", function () {}
4 │ // )
╰────
help: Remove or uncomment this comment

View file

@ -375,14 +375,19 @@ impl<'a> Lexer<'a> {
}
/// Section 12.4 Single Line Comment
#[allow(clippy::cast_possible_truncation)]
fn skip_single_line_comment(&mut self) -> Kind {
while let Some(c) = self.current.chars.next().as_ref() {
if is_line_terminator(*c) {
break;
let start = self.current.token.start;
while let Some(c) = self.current.chars.next() {
if is_line_terminator(c) {
self.current.token.is_on_new_line = true;
self.trivia_builder
.add_single_line_comment(start, self.offset() - c.len_utf8() as u32);
return Kind::Comment;
}
}
self.current.token.is_on_new_line = true;
self.trivia_builder.add_single_line_comment(self.current.token.start, self.offset());
// EOF
self.trivia_builder.add_single_line_comment(start, self.offset());
Kind::Comment
}

View file

@ -1,7 +1,7 @@
#![allow(non_upper_case_globals)]
//! Comment helpers
#![allow(non_upper_case_globals)]
use bitflags::bitflags;
use oxc_ast::CommentKind;
@ -9,7 +9,7 @@ use oxc_span::Span;
use crate::{
doc::{Doc, Separator},
hardline, indent, Prettier,
hardline, indent, line, ss, Prettier,
};
bitflags! {
@ -50,11 +50,28 @@ impl<'a> Prettier<'a> {
while let Some((start, end, kind)) = self.trivias.peek().copied() {
// Comment before the span
if end <= range.start {
self.trivias.next();
parts.push(self.print_comment(start, end, kind));
if kind.is_multi_line() {
let line_break = if self.has_newline(end) {
if self.has_newline(start) {
hardline!()
} else {
line!()
}
} else {
ss!(" ")
};
parts.push(line_break);
} else {
parts.push(hardline!());
}
let end = self.get_comment_end(kind, end);
if self.skip_newline(self.skip_spaces(end)).is_some_and(|i| self.has_newline(i)) {
parts.push(hardline!());
}
self.trivias.next();
} else {
break;
}
@ -96,4 +113,13 @@ impl<'a> Prettier<'a> {
let comment = Span::new(start - 2, end + end_offset).source_text(self.source_text);
Doc::Str(comment)
}
#[allow(clippy::cast_possible_truncation)]
fn get_comment_end(&self, kind: CommentKind, end: u32) -> Option<u32> {
if kind.is_single_line() {
self.source_text[end as usize..].chars().next().map(|c| end + c.len_utf8() as u32)
} else {
Some(end)
}
}
}

View file

@ -15,6 +15,7 @@ use std::{iter::Peekable, vec};
use oxc_allocator::Allocator;
use oxc_ast::{ast::Program, AstKind, CommentKind, Trivias};
use oxc_syntax::identifier::is_line_terminator;
use crate::{doc::Doc, format::Format, printer::Printer};
@ -107,4 +108,29 @@ impl<'a> Prettier<'a> {
fn is_next_line_empty(&self, end: u32) -> bool {
self.source_text[end as usize..].chars().nth(1).is_some_and(|c| c == '\n')
}
#[allow(clippy::cast_possible_truncation)]
fn skip_newline(&self, start_index: Option<u32>) -> Option<u32> {
let start_index = start_index?;
let c = self.source_text[start_index as usize..].chars().next()?;
is_line_terminator(c).then(|| start_index + c.len_utf8() as u32)
}
fn skip_spaces(&self, start_index: Option<u32>) -> Option<u32> {
let mut start_index = start_index?;
for c in self.source_text[start_index as usize..].chars() {
if matches!(c, ' ' | '\t') {
start_index += 1;
} else {
break;
}
}
Some(start_index)
}
fn has_newline(&self, start_index: u32) -> bool {
let idx = self.skip_spaces(Some(start_index));
let idx2 = self.skip_newline(idx);
idx != idx2
}
}

View file

@ -1,4 +1,4 @@
Compatibility: 115/771 (14.92%)
Compatibility: 120/771 (15.56%)
# Failed
@ -99,7 +99,6 @@ Compatibility: 115/771 (14.92%)
### babel-plugins
* babel-plugins/async-do-expressions.js
* babel-plugins/async-generators.js
* babel-plugins/bigint.js
* babel-plugins/class-properties.js
* babel-plugins/class-static-block.js
@ -109,7 +108,6 @@ Compatibility: 115/771 (14.92%)
* babel-plugins/deferred-import-evaluation.js
* babel-plugins/destructuring-private.js
* babel-plugins/do-expressions.js
* babel-plugins/dynamic-import.js
* babel-plugins/explicit-resource-management.js
* babel-plugins/export-default-from.js
* babel-plugins/export-namespace-from.js
@ -121,7 +119,6 @@ Compatibility: 115/771 (14.92%)
* babel-plugins/import-meta.js
* babel-plugins/import-reflection.js
* babel-plugins/jsx.js
* babel-plugins/logical-assignment-operators.js
* babel-plugins/module-blocks.js
* babel-plugins/nullish-coalescing-operator.js
* babel-plugins/numeric-separator.js
@ -508,7 +505,6 @@ Compatibility: 115/771 (14.92%)
### generator
* generator/anonymous.js
* generator/async.js
* generator/function-name-starts-with-get.js
### identifier/for-of
* identifier/for-of/let.js
@ -636,9 +632,6 @@ Compatibility: 115/771 (14.92%)
### nullish-coalescing
* nullish-coalescing/nullish_coalesing_operator.js
### numeric-separators
* numeric-separators/number.js
### object-prop-break-in
* object-prop-break-in/comment.js
* object-prop-break-in/short-keys.js