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

feat(rome_js_formatter): call arguments #2711

Merged
merged 16 commits into from
Jun 17, 2022
Merged
58 changes: 54 additions & 4 deletions crates/rome_cli/examples/input.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,54 @@
function foo() { let var1 = [true, false]
let broken = [ 45, 54]
let var2 = (var1, var2) => {}
}
tap.test(
"RecordImport.advance", (t) => {
mockFS(
(callback) => {
batch.setResults(
[fs.createReadStream(dataFile)], (err) => {
getBatches(
(err, batches) => {
checkStates(batches, ["started"]);

RecordImport.advance(
(err) => {
t.error(err, "Error should be empty.");

getBatches(
(err, batches) => {
checkStates(batches, ["process.completed"]);

// Need to manually move to the next step
batch.importRecords(
(err) => {
t.error(err, "Error should be empty.");

getBatches(
(err, batches) => {
checkStates(batches, ["import.completed"]);

RecordImport.advance(
(err) => {
t.error(err, "Error should be empty.");
},
);

t.ok(batch.getCurState().name(i18n));
},
);
},
);

t.ok(batch.getCurState().name(i18n));
},
);
},
);

t.ok(batch.getCurState().name(i18n));
},
);
},
);
},
);
},
);
8 changes: 8 additions & 0 deletions crates/rome_formatter/src/format_element.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use rome_rowan::SyntaxTokenText;
use std::borrow::Cow;
use std::fmt::{self, Debug, Formatter};
use std::ops::Deref;
use std::rc::Rc;

type Content = Box<FormatElement>;

Expand Down Expand Up @@ -65,6 +66,10 @@ pub enum FormatElement {
/// A list of different variants representing the same content. The printer picks the best fitting content.
/// Line breaks inside of a best fitting don't propagate to parent groups.
BestFitting(BestFitting),

/// Reference counted format element content. Useful when the same content must be emitted twice
/// because it avoids deep cloning of the inner content and instead only requires bumping a ref counter.
Rc(Rc<FormatElement>),
}

#[derive(Clone, Copy, Eq, PartialEq, Debug)]
Expand Down Expand Up @@ -144,6 +149,7 @@ impl Debug for FormatElement {
best_fitting.fmt(fmt)
}
FormatElement::ExpandParent => write!(fmt, "ExpandParent"),
FormatElement::Rc(inner) => inner.fmt(fmt),
}
}
}
Expand Down Expand Up @@ -464,6 +470,7 @@ impl FormatElement {
pub fn is_empty(&self) -> bool {
match self {
FormatElement::List(list) => list.is_empty(),
FormatElement::Rc(inner) => inner.is_empty(),
_ => false,
}
}
Expand Down Expand Up @@ -491,6 +498,7 @@ impl FormatElement {
FormatElement::BestFitting(_) => false,
FormatElement::LineSuffixBoundary => false,
FormatElement::ExpandParent => true,
FormatElement::Rc(inner) => inner.will_break(),
}
}

Expand Down
26 changes: 12 additions & 14 deletions crates/rome_formatter/src/format_extensions.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
use crate::prelude::*;
use std::cell::RefCell;
use std::marker::PhantomData;
use std::rc::Rc;

use crate::{write, Buffer, VecBuffer};

Expand Down Expand Up @@ -118,15 +119,15 @@ pub trait MemoizeFormat<Context> {
///
/// // Calls `format` for everytime the object gets formatted
/// assert_eq!(
/// format!(SimpleFormatContext::default(), [token("Formatted 1 times."), token("Formatted 2 times.")]),
/// format!(SimpleFormatContext::default(), [normal, normal])
/// "Formatted 1 times. Formatted 2 times.",
/// format!(SimpleFormatContext::default(), [normal, space_token(), normal]).unwrap().print().as_code()
/// );
///
/// // Memoized memoizes the result and calls `format` only once.
/// let memoized = normal.memoized();
/// assert_eq!(
/// format!(SimpleFormatContext::default(), [token("Formatted 3 times."), token("Formatted 3 times.")]),
/// format![SimpleFormatContext::default(), [memoized, memoized]]
/// "Formatted 3 times. Formatted 3 times.",
/// format![SimpleFormatContext::default(), [memoized, space_token(), memoized]].unwrap().print().as_code()
/// );
/// ```
///
Expand All @@ -141,9 +142,10 @@ pub trait MemoizeFormat<Context> {
impl<T, Context> MemoizeFormat<Context> for T where T: Format<Context> {}

/// Memoizes the output of its inner [Format] to avoid re-formatting a potential expensive object.
#[derive(Debug)]
pub struct Memoized<F, Context> {
inner: F,
memory: RefCell<Option<FormatResult<Vec<FormatElement>>>>,
memory: RefCell<Option<FormatResult<Rc<FormatElement>>>>,
options: PhantomData<Context>,
}

Expand All @@ -169,9 +171,7 @@ where
if let Some(memory) = self.memory.borrow().as_ref() {
return match memory {
Ok(elements) => {
for element in elements {
f.write_element(element.clone())?;
}
f.write_element(FormatElement::Rc(elements.clone()))?;

Ok(())
}
Expand All @@ -184,12 +184,10 @@ where

match result {
Ok(_) => {
let elements = buffer.into_vec();
for element in &elements {
f.write_element(element.clone())?;
}

*self.memory.borrow_mut() = Some(Ok(elements));
let elements = buffer.into_element();
let reference = Rc::new(elements);
f.write_element(FormatElement::Rc(reference.clone()))?;
*self.memory.borrow_mut() = Some(Ok(reference));

Ok(())
}
Expand Down
4 changes: 3 additions & 1 deletion crates/rome_formatter/src/printed_tokens.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,9 @@ impl PrintedTokens {
match (descendants.next(), offsets.next()) {
(Some(descendant), Some(offset)) => match descendant.text_trimmed_range().start() {
descendant_offset if descendant_offset < *offset => {
panic!("token has not been seen by the formatter: {descendant:#?}.\nUse `formatter.format_replaced` if you intentionally remove or replace a token from the formatted output.")
panic!("token has not been seen by the formatter: {descendant:#?}.\
\nUse `format_replaced` if you want to replace a token from the formatted output.\
\nUse `format_removed` if you to remove a token from the formatted output")
}
descendant_offset if descendant_offset > *offset => {
panic!("tracked offset {offset:?} doesn't match any token of {root:#?}. Have you passed a token from another tree?");
Expand Down
3 changes: 2 additions & 1 deletion crates/rome_formatter/src/printer/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,6 @@ impl<'a> Printer<'a> {
if fits_on_line(&[variant], args.with_print_mode(mode), queue, self)
{
self.state.measured_group_fits = true;

queue.enqueue(PrintElementCall::new(
variant,
args.with_print_mode(mode),
Expand All @@ -294,6 +293,7 @@ impl<'a> Printer<'a> {
}
}
}
FormatElement::Rc(content) => queue.enqueue(PrintElementCall::new(content, args)),
}
}

Expand Down Expand Up @@ -800,6 +800,7 @@ fn fits_element_on_line<'a, 'rest>(
return Fits::No;
}
}
FormatElement::Rc(content) => queue.enqueue(PrintElementCall::new(content, args)),
}

Fits::Maybe
Expand Down
129 changes: 100 additions & 29 deletions crates/rome_js_formatter/src/builders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ use crate::{AsFormat, JsCommentStyle};
use rome_formatter::token::{
format_token_trailing_trivia, FormatInserted, FormatInsertedCloseParen,
FormatInsertedOpenParen, FormatLeadingTrivia, FormatOnlyIfBreaks, FormatRemoved,
FormatReplaced, TriviaPrintMode,
FormatReplaced,
};
use rome_formatter::{format_args, write, Argument, Arguments, GroupId, PreambleBuffer, VecBuffer};
use rome_js_syntax::{JsLanguage, JsSyntaxKind, JsSyntaxNode, JsSyntaxToken};
Expand Down Expand Up @@ -410,38 +410,17 @@ impl Format<JsFormatContext> for FormatDelimited<'_, '_> {
mode,
} = self;

f.state_mut().track_token(open_token);
f.state_mut().track_token(close_token);
let open_delimiter = format_open_delimiter(open_token);
let close_delimiter = format_close_delimiter(close_token);

format_leading_trivia(open_token).fmt(f)?;
open_delimiter.format_leading_trivia().fmt(f)?;

let open_token_trailing_trivia = format_with(|f| {
// Not really interested in the pre-amble, but want to know if it was written
let mut buffer = VecBuffer::new(f.state_mut());

write!(buffer, [format_trailing_trivia(open_token)])?;

let trivia = buffer.into_vec();

if !trivia.is_empty() {
f.write_elements(trivia)?;
soft_line_break().fmt(f)?;
}

Ok(())
});

let close_token_leading_trivia = format_with(|f| {
let mut buffer = PreambleBuffer::new(f, soft_line_break());
let open_token_trailing_trivia = open_delimiter.format_trailing_trivia();

write!(
buffer,
[format_leading_trivia(close_token).with_trim_mode(TriviaPrintMode::Trim)]
)
});
let close_token_leading_trivia = close_delimiter.format_leading_trivia();

let delimited = format_with(|f| {
format_trimmed_token(open_token).fmt(f)?;
open_delimiter.format_token().fmt(f)?;

let format_content = format_with(|f| f.write_fmt(Arguments::from(content)));

Expand Down Expand Up @@ -484,7 +463,7 @@ impl Format<JsFormatContext> for FormatDelimited<'_, '_> {
}
};

format_trimmed_token(close_token).fmt(f)
close_delimiter.format_token().fmt(f)
});

let _grouped = match mode {
Expand All @@ -511,3 +490,95 @@ enum DelimitedMode {
SoftBlockIndent(Option<GroupId>),
SoftBlockSpaces(Option<GroupId>),
}

/// Use this function to create an open delimiter, where you can extract the formatting of
/// trivias and token, separately.
///
/// This function assumes that you will use the token to replicate [format_delimited], which means
/// that it will add possible line breaks
pub(crate) fn format_open_delimiter(open_token: &JsSyntaxToken) -> OpenDelimiter {
OpenDelimiter::new(open_token)
}

/// Use this function to create an close delimiter, where you can extract the formatting of
/// trivias and token, separately.
///
/// This function assumes that you will use the token to replicate [format_delimited], which means
/// that it will add possible line breaks
pub(crate) fn format_close_delimiter(close_token: &JsSyntaxToken) -> CloseDelimiter {
CloseDelimiter::new(close_token)
}

pub(crate) struct OpenDelimiter<'t> {
open_token: &'t JsSyntaxToken,
}

impl<'t> OpenDelimiter<'t> {
pub(crate) fn new(open_token: &'t JsSyntaxToken) -> Self {
Self { open_token }
}

/// It extracts the formatted leading trivia of the token, without writing it in the buffer
pub(crate) fn format_leading_trivia(&self) -> impl Format<JsFormatContext> + 't {
format_leading_trivia(self.open_token)
}

/// It extracts the formatted trailing trivia of the token, without writing it in the buffer
pub(crate) fn format_trailing_trivia(&self) -> impl Format<JsFormatContext> + 't {
format_with(|f| {
// Not really interested in the pre-amble, but want to know if it was written
let mut buffer = VecBuffer::new(f.state_mut());

write!(buffer, [format_trailing_trivia(self.open_token)])?;

let trivia = buffer.into_vec();

if !trivia.is_empty() {
f.write_elements(trivia)?;
soft_line_break().fmt(f)?;
}

Ok(())
})
}

/// It extracts the formatted token, without writing it in the buffer
pub(crate) fn format_token(&self) -> impl Format<JsFormatContext> + 't {
format_with(|f| {
f.state_mut().track_token(self.open_token);
write!(f, [format_trimmed_token(self.open_token)])
})
}
}

pub(crate) struct CloseDelimiter<'t> {
close_token: &'t JsSyntaxToken,
}

impl<'t> CloseDelimiter<'t> {
pub(crate) fn new(close_token: &'t JsSyntaxToken) -> Self {
Self { close_token }
}

/// It extracts the formatted leading trivia of the token, without writing it in the buffer
pub(crate) fn format_trailing_trivia(&self) -> impl Format<JsFormatContext> + 't {
format_trailing_trivia(self.close_token)
}

/// It extracts the formatted trailing trivia of the token, without writing it in the buffer
pub(crate) fn format_leading_trivia(&self) -> impl Format<JsFormatContext> + 't {
format_with(|f| {
let mut buffer = PreambleBuffer::new(f, soft_line_break());

write!(buffer, [format_leading_trivia(self.close_token,)])
})
}

/// It extracts the formatted token, without writing it in the buffer
pub(crate) fn format_token(&self) -> impl Format<JsFormatContext> + 't {
format_with(|f| {
f.state_mut().track_token(self.close_token);
write!(f, [format_trimmed_token(self.close_token)])
})
}
}
Loading