refactor(formatter): Use slice to store content
MODIFY
crates/rome_formatter/src/buffer.rs
crates/rome_formatter/src/buffer.rs
@@ -218,13 +218,7 @@ impl<Context> Buffer for VecBuffer<'_, Context> {
fn write_element(&mut self, element: FormatElement) -> FormatResult<()> {
match element {
- FormatElement::List(list) => {
- if self.elements.is_empty() {
- self.elements = list.into_vec()
- } else {
- self.elements.extend(list.into_vec())
- }
- }
+ FormatElement::List(list) => self.elements.extend(list.into_vec()),
element => self.elements.push(element),
}

MODIFY
crates/rome_formatter/src/builders.rs
crates/rome_formatter/src/builders.rs
@@ -409,8 +409,8 @@ impl<Context> Format<Context> for LineSuffix<'_, Context> {
let mut buffer = VecBuffer::new(f.state_mut());
buffer.write_fmt(Arguments::from(&self.content))?;
- let content = buffer.into_element();
- f.write_element(FormatElement::LineSuffix(Box::new(content)))
+ let content = buffer.into_vec();
+ f.write_element(FormatElement::LineSuffix(content.into_boxed_slice()))
}
}
@@ -607,8 +607,8 @@ impl<Context> Format<Context> for Indent<'_, Context> {
return Ok(());
}
- let content = buffer.into_element();
- f.write_element(FormatElement::Indent(Box::new(content)))
+ let content = buffer.into_vec();
+ f.write_element(FormatElement::Indent(content.into_boxed_slice()))
}
}
@@ -820,9 +820,9 @@ impl<Context> Format<Context> for BlockIndent<'_, Context> {
return Ok(());
}
- let content = buffer.into_element();
+ let content = buffer.into_vec();
- f.write_element(FormatElement::Indent(Box::new(content)))?;
+ f.write_element(FormatElement::Indent(content.into_boxed_slice()))?;
match self.mode {
IndentMode::Soft => write!(f, [soft_line_break()])?,
@@ -1381,9 +1381,10 @@ impl<Context> Format<Context> for IfGroupBreaks<'_, Context> {
return Ok(());
}
- let content = buffer.into_element();
+ let content = buffer.into_vec();
f.write_element(FormatElement::ConditionalGroupContent(
- ConditionalGroupContent::new(content, self.mode).with_group_id(self.group_id),
+ ConditionalGroupContent::new(content.into_boxed_slice(), self.mode)
+ .with_group_id(self.group_id),
))
}
}
@@ -1828,13 +1829,10 @@ impl<'a, 'buf, Context> FillBuilder<'a, 'buf, Context> {
match items.len() {
0 => Ok(()),
1 => self.fmt.write_element(items.pop().unwrap()),
- _ => self.fmt.write_element(FormatElement::Fill(Box::new(Fill {
- list: List::new(items),
- separator: std::mem::replace(
- &mut self.separator,
- FormatElement::List(List::default()),
- ),
- }))),
+ _ => self.fmt.write_element(FormatElement::Fill(Fill {
+ content: items.into_boxed_slice(),
+ separator: Box::new(self.separator.clone()),
+ })),
}
})
}

MODIFY
crates/rome_formatter/src/format_element.rs
crates/rome_formatter/src/format_element.rs
@@ -7,7 +7,7 @@ use std::fmt::{self, Debug, Formatter};
use std::ops::Deref;
use std::rc::Rc;
-type Content = Box<FormatElement>;
+type Content = Box<[FormatElement]>;
/// Language agnostic IR for formatting source code.
///
@@ -42,7 +42,7 @@ pub enum FormatElement {
/// Concatenates multiple elements together with a given separator printed in either
/// flat or expanded mode to fill the print width. See [fill_elements].
- Fill(Box<Fill>),
+ Fill(Fill),
/// A token that should be printed as is, see [token] for documentation and examples.
Token(Token),
@@ -88,27 +88,27 @@ pub struct Verbatim {
/// The reason this range is using verbatim formatting
pub kind: VerbatimKind,
/// The [FormatElement] version of the node/token
- pub element: Box<FormatElement>,
+ pub content: Box<[FormatElement]>,
}
impl Verbatim {
- pub fn new_verbatim(element: FormatElement, length: TextSize) -> Self {
+ pub fn new_verbatim(content: Box<[FormatElement]>, length: TextSize) -> Self {
Self {
- element: Box::new(element),
+ content,
kind: VerbatimKind::Verbatim { length },
}
}
- pub fn new_unknown(element: FormatElement) -> Self {
+ pub fn new_unknown(content: Box<[FormatElement]>) -> Self {
Self {
- element: Box::new(element),
+ content,
kind: VerbatimKind::Unknown,
}
}
- pub fn new_suppressed(element: FormatElement) -> Self {
+ pub fn new_suppressed(content: Box<[FormatElement]>) -> Self {
Self {
- element: Box::new(element),
+ content,
kind: VerbatimKind::Suppressed,
}
}
@@ -142,7 +142,7 @@ impl Debug for FormatElement {
FormatElement::Comment(content) => fmt.debug_tuple("Comment").field(content).finish(),
FormatElement::Verbatim(verbatim) => fmt
.debug_tuple("Verbatim")
- .field(&verbatim.element)
+ .field(&verbatim.content)
.finish(),
FormatElement::BestFitting(best_fitting) => {
write!(fmt, "BestFitting")?;
@@ -202,13 +202,13 @@ impl Deref for List {
/// reaches the specified `line_width`.
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct Fill {
- pub(super) list: List,
- pub(super) separator: FormatElement,
+ pub(super) content: Content,
+ pub(super) separator: Box<FormatElement>,
}
impl Fill {
- pub fn list(&self) -> &List {
- &self.list
+ pub fn content(&self) -> &[FormatElement] {
+ &self.content
}
pub fn separator(&self) -> &FormatElement {
@@ -367,9 +367,9 @@ pub struct ConditionalGroupContent {
}
impl ConditionalGroupContent {
- pub fn new(content: FormatElement, mode: PrintMode) -> Self {
+ pub fn new(content: Box<[FormatElement]>, mode: PrintMode) -> Self {
Self {
- content: Box::new(content),
+ content,
mode,
group_id: None,
}
@@ -508,16 +508,15 @@ impl FormatElement {
match self {
FormatElement::Space => false,
FormatElement::Line(line_mode) => matches!(line_mode, LineMode::Hard | LineMode::Empty),
- FormatElement::Indent(content) => content.will_break(),
- FormatElement::Group(Group { content, .. }) | FormatElement::Comment(content) => {
- content.iter().any(FormatElement::will_break)
- }
- FormatElement::ConditionalGroupContent(group) => group.content.will_break(),
+ FormatElement::Group(Group { content, .. })
+ | FormatElement::ConditionalGroupContent(ConditionalGroupContent { content, .. })
+ | FormatElement::Comment(content)
+ | FormatElement::Fill(Fill { content, .. })
+ | FormatElement::Verbatim(Verbatim { content, .. })
+ | FormatElement::Indent(content) => content.iter().any(FormatElement::will_break),
FormatElement::List(list) => list.content.iter().any(FormatElement::will_break),
- FormatElement::Fill(fill) => fill.list.content.iter().any(FormatElement::will_break),
FormatElement::Token(token) => token.contains('\n'),
FormatElement::LineSuffix(_) => false,
- FormatElement::Verbatim(verbatim) => verbatim.element.will_break(),
FormatElement::BestFitting(_) => false,
FormatElement::LineSuffixBoundary => false,
FormatElement::ExpandParent => true,
@@ -526,26 +525,19 @@ impl FormatElement {
}
/// Utility function to get the "last element" of a [FormatElement], recursing
- /// into lists and groups for find the last element that's not an empty element,
- /// a line break or a comment
+ /// into lists and groups to find the last element that's not
+ /// a line break, verbatim or a comment.
pub fn last_element(&self) -> Option<&FormatElement> {
match self {
- FormatElement::Fill(fill) => fill
- .list
- .iter()
- .rev()
- .find_map(|element| element.last_element()),
FormatElement::List(list) => {
list.iter().rev().find_map(|element| element.last_element())
}
FormatElement::Line(_) | FormatElement::Comment(_) => None,
- FormatElement::Indent(indent) => indent.last_element(),
- FormatElement::Group(group) => group
- .content
- .iter()
- .rev()
- .find_map(FormatElement::last_element),
+ FormatElement::Group(Group { content, .. }) | FormatElement::Indent(content) => {
+ content.iter().rev().find_map(FormatElement::last_element)
+ }
+ FormatElement::Interned(Interned(inner)) => inner.last_element(),
_ => Some(self),
}
@@ -627,14 +619,14 @@ static_assert!(std::mem::size_of::<rome_rowan::TextRange>() == 8usize);
static_assert!(std::mem::size_of::<crate::format_element::VerbatimKind>() == 8usize);
#[cfg(target_pointer_width = "64")]
-static_assert!(std::mem::size_of::<crate::format_element::Verbatim>() == 16usize);
+static_assert!(std::mem::size_of::<crate::format_element::Verbatim>() == 24usize);
#[cfg(target_pointer_width = "64")]
static_assert!(std::mem::size_of::<crate::format_element::Token>() == 24usize);
#[cfg(not(debug_assertions))]
#[cfg(target_pointer_width = "64")]
-static_assert!(std::mem::size_of::<crate::format_element::ConditionalGroupContent>() == 16usize);
+static_assert!(std::mem::size_of::<crate::format_element::ConditionalGroupContent>() == 24usize);
#[cfg(target_pointer_width = "64")]
static_assert!(std::mem::size_of::<crate::format_element::List>() == 24usize);

MODIFY
crates/rome_formatter/src/printer/mod.rs
crates/rome_formatter/src/printer/mod.rs
@@ -2,9 +2,7 @@ mod printer_options;
pub use printer_options::*;
-use crate::format_element::{
- ConditionalGroupContent, Group, LineMode, List, PrintMode, VerbatimKind,
-};
+use crate::format_element::{ConditionalGroupContent, Group, LineMode, PrintMode, VerbatimKind};
use crate::intersperse::Intersperse;
use crate::{FormatElement, GroupId, Printed, SourceMarker, TextRange};
@@ -129,11 +127,7 @@ impl<'a> Printer<'a> {
PrintMode::Flat if self.state.measured_group_fits => {
// A parent group has already verified that this group fits on a single line
// Thus, just continue in flat mode
- queue.extend(
- content
- .iter()
- .map(|element| PrintElementCall::new(element, args)),
- );
+ queue.extend_with_args(content.iter(), args);
PrintMode::Flat
}
// The printer is either in expanded mode or it's necessary to re-measure if the group fits
@@ -144,15 +138,14 @@ impl<'a> Printer<'a> {
let flat_args = args.with_print_mode(PrintMode::Flat);
if fits_on_line(content.iter(), flat_args, queue, self) {
- queue.extend(
- content.iter().map(|e| PrintElementCall::new(e, flat_args)),
- );
+ queue.extend_with_args(content.iter(), flat_args);
self.state.measured_group_fits = true;
PrintMode::Flat
} else {
- queue.extend(content.iter().map(|e| {
- PrintElementCall::new(e, args.with_print_mode(PrintMode::Expanded))
- }));
+ queue.extend_with_args(
+ content.iter(),
+ args.with_print_mode(PrintMode::Expanded),
+ );
PrintMode::Expanded
}
}
@@ -164,18 +157,15 @@ impl<'a> Printer<'a> {
}
FormatElement::Fill(fill) => {
- self.print_fill(queue, fill.list(), fill.separator(), args);
+ self.print_fill(queue, fill.content(), fill.separator(), args);
}
FormatElement::List(list) => {
- queue.extend(list.iter().map(|t| PrintElementCall::new(t, args)));
+ queue.extend_with_args(list.iter(), args);
}
FormatElement::Indent(content) => {
- queue.enqueue(PrintElementCall::new(
- content,
- args.with_incremented_indent(),
- ));
+ queue.extend_with_args(content.iter(), args.with_incremented_indent());
}
FormatElement::ConditionalGroupContent(ConditionalGroupContent {
@@ -189,7 +179,7 @@ impl<'a> Printer<'a> {
};
if &group_mode == mode {
- queue.enqueue(PrintElementCall::new(content, args));
+ queue.extend_with_args(content.iter(), args);
}
}
@@ -226,7 +216,7 @@ impl<'a> Printer<'a> {
FormatElement::LineSuffix(suffix) => {
self.state
.line_suffixes
- .push(PrintElementCall::new(&**suffix, args));
+ .extend(suffix.iter().map(|e| PrintElementCall::new(e, args)));
}
FormatElement::LineSuffixBoundary => {
const HARD_BREAK: &FormatElement = &FormatElement::Line(LineMode::Hard);
@@ -234,7 +224,7 @@ impl<'a> Printer<'a> {
}
FormatElement::Comment(content) => {
- queue.extend(content.iter().map(|e| PrintElementCall::new(e, args)))
+ queue.extend_with_args(content.iter(), args);
}
FormatElement::Verbatim(verbatim) => {
@@ -245,7 +235,7 @@ impl<'a> Printer<'a> {
));
}
- queue.enqueue(PrintElementCall::new(&verbatim.element, args));
+ queue.extend_with_args(verbatim.content.iter(), args);
}
FormatElement::ExpandParent => {
// No-op, only has an effect on `fits`
@@ -346,7 +336,7 @@ impl<'a> Printer<'a> {
fn print_fill(
&mut self,
queue: &mut ElementCallQueue<'a>,
- content: &'a List,
+ content: &'a [FormatElement],
separator: &'a FormatElement,
args: PrintElementArgs,
) {
@@ -440,11 +430,7 @@ impl<'a> Printer<'a> {
) {
let min_queue_length = queue.0.len();
- queue.extend(
- elements
- .iter()
- .map(|element| PrintElementCall::new(element, args)),
- );
+ queue.extend(elements.iter().map(|e| PrintElementCall::new(e, args)));
while let Some(call) = queue.dequeue() {
self.print_element(queue, call.element, call.args);
@@ -616,6 +602,18 @@ impl<'a> ElementCallQueue<'a> {
self.0.extend(calls.into_iter().rev());
}
+ fn extend_with_args<I>(&mut self, elements: I, args: PrintElementArgs)
+ where
+ I: IntoIterator<Item = &'a FormatElement>,
+ I::IntoIter: DoubleEndedIterator,
+ {
+ self.extend(
+ elements
+ .into_iter()
+ .map(|element| PrintElementCall::new(element, args)),
+ )
+ }
+
pub fn enqueue(&mut self, call: PrintElementCall<'a>) {
self.0.push(call);
}
@@ -653,11 +651,7 @@ where
.with_rest(queue)
.with_queue(ElementCallQueue::new(shared_buffer));
- measure_queue.extend(
- elements
- .into_iter()
- .map(|element| PrintElementCall::new(element, args)),
- );
+ measure_queue.extend(elements.into_iter(), args);
let mut measure_state = MeasureState {
pending_indent: printer.state.pending_indent,
@@ -736,13 +730,12 @@ fn fits_element_on_line<'a, 'rest>(
}
}
- FormatElement::Indent(content) => queue.enqueue(PrintElementCall::new(
- content,
- args.with_incremented_indent(),
- )),
+ FormatElement::Indent(content) => {
+ queue.extend(content.iter(), args.with_incremented_indent())
+ }
FormatElement::Group(group) => {
- queue.extend(group.content.iter().map(|e| PrintElementCall::new(e, args)));
+ queue.extend(group.content.iter(), args);
if let Some(id) = group.id {
state.group_modes.insert_print_mode(id, args.mode);
@@ -759,16 +752,14 @@ fn fits_element_on_line<'a, 'rest>(
};
if group_mode == conditional.mode {
- queue.enqueue(PrintElementCall::new(&conditional.content, args))
+ queue.extend(conditional.content.iter(), args);
}
}
- FormatElement::List(list) => {
- queue.extend(list.iter().map(|t| PrintElementCall::new(t, args)))
- }
+ FormatElement::List(list) => queue.extend(list.iter(), args),
FormatElement::Fill(fill) => queue.queue.0.extend(
- Intersperse::new(fill.list().iter().rev(), fill.separator())
+ Intersperse::new(fill.content().iter().rev(), fill.separator())
.map(|t| PrintElementCall::new(t, args)),
),
@@ -811,13 +802,9 @@ fn fits_element_on_line<'a, 'rest>(
}
}
- FormatElement::Comment(content) => {
- queue.extend(content.iter().map(|e| PrintElementCall::new(e, args)))
- }
+ FormatElement::Comment(content) => queue.extend(content.iter(), args),
- FormatElement::Verbatim(verbatim) => {
- queue.enqueue(PrintElementCall::new(&verbatim.element, args))
- }
+ FormatElement::Verbatim(verbatim) => queue.extend(verbatim.content.iter(), args),
FormatElement::BestFitting(best_fitting) => {
let content = match args.mode {
PrintMode::Flat => best_fitting.most_flat(),
@@ -899,14 +886,19 @@ impl<'a, 'rest> MeasureQueue<'a, 'rest> {
self.queue.enqueue(call);
}
- fn extend<T>(&mut self, calls: T)
+ fn extend<T>(&mut self, elements: T, args: PrintElementArgs)
where
- T: IntoIterator<Item = PrintElementCall<'a>>,
+ T: IntoIterator<Item = &'a FormatElement>,
T::IntoIter: DoubleEndedIterator,
{
// Reverse the calls because elements are removed from the back of the vec
// in reversed insertion order
- self.queue.0.extend(calls.into_iter().rev());
+ self.queue.0.extend(
+ elements
+ .into_iter()
+ .rev()
+ .map(|element| PrintElementCall::new(element, args)),
+ );
}
fn dequeue(&mut self) -> Option<(&'a FormatElement, PrintElementArgs)> {

MODIFY
crates/rome_js_formatter/src/builders.rs
crates/rome_js_formatter/src/builders.rs
@@ -239,10 +239,10 @@ impl Format<JsFormatContext> for FormatVerbatimNode<'_> {
})]
)?;
- let content = buffer.into_element();
+ let content = buffer.into_vec();
let verbatim = Verbatim {
- element: Box::new(content),
+ content: content.into_boxed_slice(),
kind: self.kind,
};

MODIFY
xtask/bench/src/features/formatter.rs
xtask/bench/src/features/formatter.rs
+#[cfg(feature = "dhat-on")]
+use crate::features::print_diff;
use crate::BenchmarkSummary;
use rome_formatter::Printed;
use rome_js_formatter::context::JsFormatContext;
@@ -23,9 +25,31 @@ pub fn benchmark_format_lib(id: &str, root: &JsSyntaxNode) -> BenchmarkSummary {
}
pub fn run_format(root: &JsSyntaxNode) -> Printed {
- format_node(JsFormatContext::default(), root)
- .unwrap()
- .print()
+ #[cfg(feature = "dhat-on")]
+ let stats = {
+ println!("Start");
+ dhat::get_stats().unwrap()
+ };
+
+ let formatted = format_node(JsFormatContext::default(), root).unwrap();
+
+ #[cfg(feature = "dhat-on")]
+ let stats = {
+ println!("Formatted");
+ print_diff(stats, dhat::get_stats().unwrap())
+ };
+
+ let printed = formatted.print();
+ drop(formatted);
+
+ #[cfg(feature = "dhat-on")]
+ {
+ println!("Printed");
+ print_diff(stats, dhat::get_stats().unwrap());
+ }
+
+ #[allow(clippy::let_and_return)]
+ printed
}
impl FormatterMeasurement {

MODIFY
xtask/bench/src/features/mod.rs
xtask/bench/src/features/mod.rs
pub mod analyzer;
pub mod formatter;
pub mod parser;
+
+#[cfg(feature = "dhat-on")]
+fn print_diff(before: dhat::Stats, current: dhat::Stats) -> dhat::Stats {
+ use humansize::{file_size_opts as options, FileSize};
+
+ println!("\tMemory");
+ if let Some(heap) = &current.heap {
+ println!("\t\tCurrent Blocks: {}", heap.curr_blocks);
+ println!(
+ "\t\tCurrent Bytes: {}",
+ heap.curr_bytes.file_size(options::CONVENTIONAL).unwrap()
+ );
+ println!("\t\tMax Blocks: {}", heap.max_blocks);
+ println!(
+ "\t\tMax Bytes: {}",
+ heap.max_bytes.file_size(options::CONVENTIONAL).unwrap()
+ );
+ }
+
+ println!(
+ "\t\tTotal Blocks: {}",
+ current.total_blocks - before.total_blocks
+ );
+ println!(
+ "\t\tTotal Bytes: {}",
+ (current.total_bytes - before.total_bytes)
+ .file_size(options::CONVENTIONAL)
+ .unwrap()
+ );
+
+ current
+}

MODIFY
xtask/bench/src/features/parser.rs
xtask/bench/src/features/parser.rs
+#[cfg(feature = "dhat-on")]
+use crate::features::print_diff;
use crate::BenchmarkSummary;
use itertools::Itertools;
use rome_diagnostics::file::SimpleFile;
@@ -17,37 +19,6 @@ pub struct ParseMeasurement {
diagnostics: Vec<Diagnostic>,
}
-#[cfg(feature = "dhat-on")]
-fn print_diff(before: dhat::Stats, current: dhat::Stats) -> dhat::Stats {
- use humansize::{file_size_opts as options, FileSize};
-
- println!("\tMemory");
- if let Some(heap) = &current.heap {
- println!("\t\tCurrent Blocks: {}", heap.curr_blocks);
- println!(
- "\t\tCurrent Bytes: {}",
- heap.curr_bytes.file_size(options::CONVENTIONAL).unwrap()
- );
- println!("\t\tMax Blocks: {}", heap.max_blocks);
- println!(
- "\t\tMax Bytes: {}",
- heap.max_bytes.file_size(options::CONVENTIONAL).unwrap()
- );
- }
-
- println!(
- "\t\tTotal Blocks: {}",
- current.total_blocks - before.total_blocks
- );
- println!(
- "\t\tTotal Bytes: {}",
- (current.total_bytes - before.total_bytes)
- .file_size(options::CONVENTIONAL)
- .unwrap()
- );
-
- current
-}
pub fn benchmark_parse_lib(id: &str, code: &str, source_type: SourceType) -> BenchmarkSummary {
#[cfg(feature = "dhat-on")]
println!("Start");

MODIFY
xtask/bench/src/lib.rs
xtask/bench/src/lib.rs
@@ -158,21 +158,6 @@ pub fn run(args: RunArgs) {
}
});
group.finish();
- } else {
- //warmup
- match args.feature {
- FeatureToBenchmark::Parser => {
- run_parse(code, source_type);
- }
- FeatureToBenchmark::Formatter => {
- let root = parse(code, 0, source_type).syntax();
- run_format(&root);
- }
- FeatureToBenchmark::Analyzer => {
- let root = parse(code, 0, source_type).tree();
- run_analyzer(&root);
- }
- }
}
let result = match args.feature {

MODIFY
xtask/bench/src/main.rs
xtask/bench/src/main.rs
@@ -11,7 +11,7 @@ static ALLOCATOR: DhatAlloc = DhatAlloc;
fn main() -> Result<(), pico_args::Error> {
#[cfg(feature = "dhat-on")]
- let dhat = dhat::Dhat::start_heap_profiling();
+ let _dhat = dhat::Dhat::start_heap_profiling();
let _d = pushd(project_root());
let mut args = Arguments::from_env();

refactor(formatter): Use slice to store content (#2775)