Skip to content
This repository has been archived by the owner on Aug 31, 2023. It is now read-only.

refactor(rome_formatter): Comments all in one #3212

Closed
wants to merge 45 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
45 commits
Select commit Hold shift + click to select a range
265aa3e
refactor(formatter): Refactor comments
MichaReiser Sep 6, 2022
0a494b5
Some more improvements
Sep 7, 2022
a3a20bd
refactor(formatter): Member chain part 1
MichaReiser Sep 7, 2022
eda7004
change `dangling` comments to be for nodes rather than tokens
MichaReiser Sep 7, 2022
bcb99fe
Move some things around
MichaReiser Sep 7, 2022
1047d00
Format IfStatement comments
MichaReiser Sep 8, 2022
117ecca
Format While comments
MichaReiser Sep 8, 2022
40716ae
Format try statement comments
MichaReiser Sep 8, 2022
b806f53
Member chain comments
MichaReiser Sep 8, 2022
208de59
Variable declarator formatting
MichaReiser Sep 8, 2022
757c1c8
Small refactors
MichaReiser Sep 9, 2022
6e77fc1
Start for statement comments
MichaReiser Sep 9, 2022
e261cc8
Format `for` comments
Sep 9, 2022
baa18ec
Format JSX comments
Sep 9, 2022
255fd09
Print comments AFTER the script/module directive
Sep 9, 2022
4ddd020
Format arrow comments
Sep 9, 2022
36df552
Format function declaration comments
Sep 9, 2022
b845e41
Format function conditional comments
Sep 9, 2022
3f6a886
Fix ignored comment range
Sep 9, 2022
df28643
Refactor multimap to support out of order comments
MichaReiser Sep 10, 2022
97496e9
static initializer block
MichaReiser Sep 10, 2022
ea25fea
Use `FxHashMap`, Remove dangling formatted state with `formats_dangli…
MichaReiser Sep 10, 2022
215dbfc
Support JSX Ignore comments
Sep 11, 2022
1d6b516
Skipped Trivia
Sep 12, 2022
fe08034
Accept some snapshots
MichaReiser Sep 12, 2022
112bf7e
Format Unknown nodes
MichaReiser Sep 12, 2022
3b9b178
Remove Comment Format Element
MichaReiser Sep 12, 2022
d2c5e02
If formatting
MichaReiser Sep 12, 2022
a427b56
Various fixes
MichaReiser Sep 12, 2022
e6ed0d8
Label statement comments
MichaReiser Sep 12, 2022
6a1ed34
Switch comments
MichaReiser Sep 12, 2022
84fc7e5
Format type arguments/parameters, format leading trivia of cleaned name
MichaReiser Sep 12, 2022
cfccdee
Format classes
MichaReiser Sep 12, 2022
8e92f02
Format interfaces
MichaReiser Sep 12, 2022
1deee96
Fix member chain commands handling
MichaReiser Sep 12, 2022
c226174
Module blocks, comments before call arguments
MichaReiser Sep 13, 2022
a7e374b
Format TS mapped types
MichaReiser Sep 13, 2022
42a3814
Fix same line comments inside of empty class bodies
MichaReiser Sep 13, 2022
c591177
Fix range formatting with leading comments
MichaReiser Sep 13, 2022
9066344
Format union type comments
MichaReiser Sep 13, 2022
f19747d
Keep trailing comments with parenthesized node
MichaReiser Sep 15, 2022
d426ce4
Constructor and call signature formatting
MichaReiser Sep 15, 2022
b473491
Format import & export comments
MichaReiser Sep 15, 2022
d8d2121
Clippy & Rustfmt
MichaReiser Sep 15, 2022
eb5aea6
More clippy
MichaReiser Sep 15, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 5 additions & 0 deletions crates/rome_formatter/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ cfg-if = "1.0.0"
indexmap = "1.8.2"
schemars = { version = "0.8.10", optional = true }
rustc-hash = "1.1.0"
countme = "3.0.1"

[dev-dependencies]
rome_js_parser = { path = "../rome_js_parser"}
rome_js_syntax = { path = "../rome_js_syntax" }

[features]
serde = ["dep:serde", "schemars", "rome_rowan/serde"]
231 changes: 5 additions & 226 deletions crates/rome_formatter/src/builders.rs
Original file line number Diff line number Diff line change
@@ -1,15 +1,11 @@
use crate::prelude::*;
use crate::{
format_element, write, Argument, Arguments, BufferSnapshot, FormatState, GroupId, TextRange,
TextSize,
};
use crate::{format_element, write, Argument, Arguments, GroupId, TextRange, TextSize};
use crate::{Buffer, VecBuffer};
use rome_rowan::{Language, SyntaxNode, SyntaxToken, SyntaxTokenText, TextLen};
use std::borrow::Cow;
use std::cell::Cell;
use std::marker::PhantomData;
use std::num::NonZeroU8;
use std::ops::Deref;

/// A line break that only gets printed if the enclosing `Group` doesn't fit on a single line.
/// It's omitted if the enclosing `Group` fits on a single line.
Expand Down Expand Up @@ -457,73 +453,6 @@ impl<Context> Format<Context> for LineSuffixBoundary {
}
}

/// Marks some content as a comment trivia.
///
/// This does not directly influence how this content will be printed, but some
/// parts of the formatter may chose to handle this element in a specific way
///
/// ## Examples
///
/// ```
/// use rome_formatter::{format, write, format_args};
/// use rome_formatter::prelude::*;
///
/// let elements = format!(
/// SimpleFormatContext::default(),
/// [
/// group(&format_args![
/// comment(&format_args![text("// test"), hard_line_break()]),
/// format_with(|f| {
/// write!(f, [
/// comment(&format_args![text("/* inline */"), hard_line_break()]).memoized(),
/// text("a"),
/// soft_line_break_or_space(),
/// ])
/// }).memoized(),
/// text("b"),
/// soft_line_break_or_space(),
/// text("c")
/// ])
/// ]
/// ).unwrap();
///
/// assert_eq!(
/// "// test\n/* inline */\na b c",
/// elements.print().as_code()
/// );
/// ```
#[inline]
pub fn comment<Content, Context>(content: &Content) -> FormatComment<Context>
where
Content: Format<Context>,
{
FormatComment {
content: Argument::new(content),
}
}

#[derive(Copy, Clone)]
pub struct FormatComment<'a, Context> {
content: Argument<'a, Context>,
}

impl<Context> Format<Context> for FormatComment<'_, Context> {
fn fmt(&self, f: &mut Formatter<Context>) -> FormatResult<()> {
let mut buffer = VecBuffer::new(f.state_mut());

buffer.write_fmt(Arguments::from(&self.content))?;
let content = buffer.into_vec();

f.write_element(FormatElement::Comment(content.into_boxed_slice()))
}
}

impl<Context> std::fmt::Debug for FormatComment<'_, Context> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_tuple("Comment").field(&"{{content}}").finish()
}
}

/// Marks some content with a label.
///
/// This does not directly influence how this content will be printed, but some
Expand Down Expand Up @@ -1307,7 +1236,7 @@ impl<Context> Format<Context> for Group<'_, Context> {
return f.write_fmt(Arguments::from(&self.content));
}

let mut buffer = GroupBuffer::new(f);
let mut buffer = VecBuffer::new(f.state_mut());

buffer.write_fmt(Arguments::from(&self.content))?;

Expand All @@ -1322,9 +1251,7 @@ impl<Context> Format<Context> for Group<'_, Context> {

let group = format_element::Group::new(content).with_id(self.group_id);

f.write_element(FormatElement::Group(group))?;

Ok(())
f.write_element(FormatElement::Group(group))
}
}

Expand All @@ -1338,154 +1265,6 @@ impl<Context> std::fmt::Debug for Group<'_, Context> {
}
}

/// Custom buffer implementation for `GroupElements` that moves the leading comments out of the group
/// to prevent that a leading line comment expands the token's enclosing group.
///
/// # Examples
///
/// ```javascript
/// /* a comment */
/// [1]
/// ```
///
/// The `/* a comment */` belongs to the `[` group token that is part of a group wrapping the whole
/// `[1]` expression. It's important that the comment `/* a comment */` gets moved out of the group element
/// to avoid that the `[1]` group expands because of the line break inserted by the comment.
struct GroupBuffer<'inner, Context> {
inner: &'inner mut dyn Buffer<Context = Context>,

/// The group inner content
content: Vec<FormatElement>,
}

impl<'inner, Context> GroupBuffer<'inner, Context> {
fn new(inner: &'inner mut dyn Buffer<Context = Context>) -> Self {
Self {
inner,
content: Vec::new(),
}
}

fn into_vec(self) -> Vec<FormatElement> {
self.content
}

fn write_interned(&mut self, interned: Interned) -> FormatResult<()> {
debug_assert!(self.content.is_empty());

match interned.deref() {
FormatElement::Comment(_) => {
self.inner.write_element(FormatElement::Interned(interned))
}
FormatElement::List(list) => {
let mut content_start = 0;

for element in list.iter() {
match element {
element @ FormatElement::Comment(_) => {
content_start += 1;
// Cloning comments should be alright as they are rarely nested
// and the case where all elements of an interned data structure are comments
// are rare
self.inner.write_element(element.clone())?;
}
FormatElement::Interned(interned) => {
self.write_interned(interned.clone())?;
content_start += 1;

if !self.content.is_empty() {
// Interned struct contained non-comment
break;
}
}
_ => {
// Found the first non-comment / nested interned element
break;
}
}
}

// No leading comments, this group has no comments
if content_start == 0 {
self.content.push(FormatElement::Interned(interned));
return Ok(());
}

let content = &list[content_start..];

// It is necessary to mutate the interned elements, write cloned elements
self.write_elements(content.iter().cloned())
}
FormatElement::Interned(interned) => self.write_interned(interned.clone()),
_ => {
self.content.push(FormatElement::Interned(interned));
Ok(())
}
}
}
}

impl<Context> Buffer for GroupBuffer<'_, Context> {
type Context = Context;

fn write_element(&mut self, element: FormatElement) -> FormatResult<()> {
if self.content.is_empty() {
match element {
FormatElement::List(list) => {
self.write_elements(list.into_vec())?;
}
FormatElement::Interned(interned) => match Interned::try_unwrap(interned) {
Ok(owned) => self.write_element(owned)?,
Err(interned) => self.write_interned(interned)?,
},
comment @ FormatElement::Comment { .. } => {
self.inner.write_element(comment)?;
}
element => self.content.push(element),
}
} else {
match element {
FormatElement::List(list) => {
self.content.extend(list.into_vec());
}
element => self.content.push(element),
}
}

Ok(())
}

fn elements(&self) -> &[FormatElement] {
&self.content
}

fn state(&self) -> &FormatState<Self::Context> {
self.inner.state()
}

fn state_mut(&mut self) -> &mut FormatState<Self::Context> {
self.inner.state_mut()
}

fn snapshot(&self) -> BufferSnapshot {
BufferSnapshot::Any(Box::new(GroupElementsBufferSnapshot {
inner: self.inner.snapshot(),
content_len: self.content.len(),
}))
}

fn restore_snapshot(&mut self, snapshot: BufferSnapshot) {
let snapshot = snapshot.unwrap_any::<GroupElementsBufferSnapshot>();
self.inner.restore_snapshot(snapshot.inner);
self.content.truncate(snapshot.content_len);
}
}

struct GroupElementsBufferSnapshot {
inner: BufferSnapshot,
content_len: usize,
}

/// IR element that forces the parent group to print in expanded mode.
///
/// Has no effect if used outside of a group or element that introduce implicit groups (fill element).
Expand Down Expand Up @@ -2213,9 +1992,9 @@ pub fn get_lines_before<L: Language>(next_node: &SyntaxNode<L>) -> usize {
leading_trivia
.pieces()
.take_while(|piece| {
// Stop at the first comment piece, the comment printer
// Stop at the first comment or skipped piece, the comment printer
// will handle newlines between the comment and the node
!piece.is_comments()
!(piece.is_comments() || piece.is_skipped())
})
.filter(|piece| piece.is_newline())
.count()
Expand Down
Loading