perf(rome_formatter): Add `Buffer.elements`
MODIFY
crates/rome_formatter/src/buffer.rs
crates/rome_formatter/src/buffer.rs
use super::{write, Arguments, FormatElement};
-use crate::format_element::{LabelId, List};
+use crate::format_element::List;
use crate::{Format, FormatResult, FormatState};
use std::any::{Any, TypeId};
use std::fmt::Debug;
@@ -30,6 +30,12 @@ pub trait Buffer {
///
fn write_element(&mut self, element: FormatElement) -> FormatResult<()>;
+ /// Returns a slice containing all elements written into this buffer.
+ ///
+ /// Prefer using [BufferExtensions::start_recording] over accessing [Buffer::elements] directly.
+ #[doc(hidden)]
+ fn elements(&self) -> &[FormatElement];
+
/// Glue for usage of the [`write!`] macro with implementors of this trait.
///
/// This method should generally not be invoked manually, but rather through the [`write!`] macro itself.
@@ -132,6 +138,10 @@ impl<W: Buffer<Context = Context> + ?Sized, Context> Buffer for &mut W {
(**self).write_element(element)
}
+ fn elements(&self) -> &[FormatElement] {
+ (**self).elements()
+ }
+
fn write_fmt(&mut self, args: Arguments<Context>) -> FormatResult<()> {
(**self).write_fmt(args)
}
@@ -226,6 +236,10 @@ impl<Context> Buffer for VecBuffer<'_, Context> {
Ok(())
}
+ fn elements(&self) -> &[FormatElement] {
+ self
+ }
+
fn state(&self) -> &FormatState<Self::Context> {
self.state
}
@@ -349,6 +363,10 @@ where
}
}
+ fn elements(&self) -> &[FormatElement] {
+ self.inner.elements()
+ }
+
fn state(&self) -> &FormatState<Self::Context> {
self.inner.state()
}
@@ -400,6 +418,10 @@ where
self.inner.write_element(element)
}
+ fn elements(&self) -> &[FormatElement] {
+ self.inner.elements()
+ }
+
fn state(&self) -> &FormatState<Self::Context> {
self.inner.state()
}
@@ -427,227 +449,110 @@ pub trait BufferExtensions: Buffer + Sized {
Inspect::new(self, inspector)
}
- /// Writes a sequence of elements into this buffer.
- fn write_elements<I>(&mut self, elements: I) -> FormatResult<()>
- where
- I: IntoIterator<Item = FormatElement>,
- {
- for element in elements {
- self.write_element(element)?;
- }
-
- Ok(())
- }
-
- /// It emits a custom buffer called [WillBreakBuffer], which tracks
- /// it he last element written in the main buffer breaks, it does so by
- /// checking if their IR emits an [element](FormatElement) that breaks.
- ///
- /// This functionality can be used only one element and only after the element
- /// is written in the buffer.
+ /// Starts a recording that gives you access to all elements that have been written between the start
+ /// and end of the recording
///
- /// ## Examples
+ /// #Examples
///
/// ```
- /// use rome_formatter::{format, format_args, write, LineWidth};
+ /// use std::ops::Deref;
/// use rome_formatter::prelude::*;
+ /// use rome_formatter::{write, format, SimpleFormatContext};
///
/// let formatted = format!(SimpleFormatContext::default(), [format_with(|f| {
+ /// let mut recording = f.start_recording();
///
- /// let element = format_with(|f| {
- /// write!(f, [
- /// text("hello"),
- /// hard_line_break(),
- /// text("world!")
- /// ])
- /// });
- /// let mut buffer = f.inspect_will_break();
- /// write!(buffer, [element])?;
- /// let does_element_break = buffer.will_break();
- ///
- /// if does_element_break {
- /// write!(f, [hard_line_break(), text("break")])
- /// } else {
- /// write!(f, [text("did not break")])
- /// }
+ /// write!(recording, [text("A")])?;
+ /// write!(recording, [text("B")])?;
///
- /// })]).unwrap();
+ /// write!(recording, [format_with(|f| write!(f, [text("C"), text("D")]))])?;
///
+ /// let recorded = recording.stop();
/// assert_eq!(
- /// "hello\nworld!\nbreak",
- /// formatted.print().as_code()
+ /// recorded.deref(),
+ /// &[
+ /// FormatElement::Text(Text::Static{ text: "A" }),
+ /// FormatElement::Text(Text::Static{ text: "B" }),
+ /// FormatElement::Text(Text::Static{ text: "C" }),
+ /// FormatElement::Text(Text::Static{ text: "D" })
+ /// ]
/// );
- /// ```
- ///
- /// ## Alternatives
///
- /// Use `Memoized.inspect(f)?.will_break()` if you need to know if some content breaks that should
- /// only be written later.
- fn inspect_will_break(&mut self) -> WillBreakBuffer<Self::Context> {
- WillBreakBuffer::new(self)
- }
-
- /// Wraps the current buffer in a [HasLabelBuffer], which tracks
- /// labelled elements written in the main buffer, it does so by
- /// checking if [element](FormatElement) is a [label](FormatElement::Label)
- /// with the expected [label_id](LabelId).
- ///
- /// This functionality can be used only on one element and only after the element
- /// is written in the buffer.
- ///
- /// ## Examples
- ///
- /// ```rust
- /// use rome_formatter::prelude::*;
- /// use rome_formatter::{format, write, LineWidth};
- ///
- /// enum SomeLabelId {}
- ///
- /// let formatted = format!(
- /// SimpleFormatContext::default(),
- /// [format_with(|f| {
- /// let mut buffer = f.inspect_is_labelled::<SomeLabelId>();
- ///
- /// write!(buffer, [
- /// labelled(
- /// LabelId::of::<SomeLabelId>(),
- /// &text("'I have a label'")
- /// )
- /// ])?;
+ /// Ok(())
+ /// })]).unwrap();
///
- /// let is_labelled = buffer.has_label();
+ /// assert_eq!(formatted.print().as_code(), "ABCD");
///
- /// if is_labelled {
- /// write!(f, [text(" has label SomeLabelId")])
- /// } else {
- /// write!(f, [text(" doesn't have label SomeLabelId")])
- /// }
- /// })]
- /// )
- /// .unwrap();
- ///
- /// assert_eq!("'I have a label' has label SomeLabelId", formatted.print().as_code());
/// ```
- ///
- /// /// ## Alternatives
- ///
- /// Use `Memoized.inspect(f)?.has_label(LabelId::of::<SomeLabelId>()` if you need to know if some content breaks that should
- /// only be written later.
- fn inspect_is_labelled<T: ?Sized + 'static>(&mut self) -> HasLabelBuffer<Self::Context> {
- let label_id = LabelId::of::<T>();
- HasLabelBuffer::new(self, label_id)
- }
-}
-
-impl<T> BufferExtensions for T where T: Buffer {}
-
-#[must_use = "must eventually call `is_labelled()` to retrieve the information"]
-pub struct HasLabelBuffer<'buffer, Context> {
- inner: &'buffer mut dyn Buffer<Context = Context>,
- label_id: LabelId,
- has_label: bool,
-}
-
-impl<'buffer, Context> HasLabelBuffer<'buffer, Context> {
- pub fn new(buffer: &'buffer mut dyn Buffer<Context = Context>, label_id: LabelId) -> Self {
- Self {
- inner: buffer,
- label_id,
- has_label: false,
- }
+ #[must_use]
+ fn start_recording(&mut self) -> Recording<Self> {
+ Recording::new(self)
}
- pub fn has_label(self) -> bool {
- self.has_label
- }
+ /// Writes a sequence of elements into this buffer.
+ fn write_elements<I>(&mut self, elements: I) -> FormatResult<()>
+ where
+ I: IntoIterator<Item = FormatElement>,
+ {
+ for element in elements {
+ self.write_element(element)?;
}
-impl<Context> Buffer for HasLabelBuffer<'_, Context> {
- type Context = Context;
-
- fn write_element(&mut self, element: FormatElement) -> FormatResult<()> {
- self.has_label |= element.has_label(self.label_id);
-
- self.inner.write_element(element)
+ Ok(())
}
-
- fn state(&self) -> &FormatState<Self::Context> {
- self.inner.state()
}
- fn state_mut(&mut self) -> &mut FormatState<Self::Context> {
- self.inner.state_mut()
- }
+impl<T> BufferExtensions for T where T: Buffer {}
- fn snapshot(&self) -> BufferSnapshot {
- BufferSnapshot::Any(Box::new(HasLabelledSnapshot {
- inner: self.inner.snapshot(),
- has_label: self.has_label,
- }))
+#[derive(Debug)]
+pub struct Recording<'buf, Buffer> {
+ start: usize,
+ buffer: &'buf mut Buffer,
}
- fn restore_snapshot(&mut self, snapshot: BufferSnapshot) {
- let snapshot = snapshot.unwrap_any::<HasLabelledSnapshot>();
- self.inner.restore_snapshot(snapshot.inner);
- self.has_label = snapshot.has_label;
+impl<'buf, B> Recording<'buf, B>
+where
+ B: Buffer,
+{
+ fn new(buffer: &'buf mut B) -> Self {
+ Self {
+ start: buffer.elements().len(),
+ buffer,
}
}
-struct HasLabelledSnapshot {
- inner: BufferSnapshot,
- has_label: bool,
+ #[inline(always)]
+ pub fn write_fmt(&mut self, arguments: Arguments<B::Context>) -> FormatResult<()> {
+ self.buffer.write_fmt(arguments)
}
-#[must_use = "must eventually call `will_break()` to retrieve the information"]
-pub struct WillBreakBuffer<'buffer, Context> {
- breaks: bool,
- inner: &'buffer mut dyn Buffer<Context = Context>,
+ #[inline(always)]
+ pub fn write_element(&mut self, element: FormatElement) -> FormatResult<()> {
+ self.buffer.write_element(element)
}
-impl<'buffer, Context> WillBreakBuffer<'buffer, Context> {
- pub fn new(buffer: &'buffer mut dyn Buffer<Context = Context>) -> Self {
- Self {
- breaks: false,
- inner: buffer,
- }
- }
+ pub fn stop(self) -> Recorded<'buf> {
+ let buffer: &'buf B = self.buffer;
+ let elements = buffer.elements();
- pub fn will_break(&self) -> bool {
- self.breaks
- }
-}
-
-impl<Context> Buffer for WillBreakBuffer<'_, Context> {
- type Context = Context;
+ let recorded = if self.start > elements.len() {
+ // May happen if buffer was rewinded.
+ &[]
+ } else {
+ &elements[self.start..]
+ };
- fn write_element(&mut self, element: FormatElement) -> FormatResult<()> {
- self.breaks = self.breaks || element.will_break();
- self.inner.write_element(element)
+ Recorded(recorded)
}
-
- fn state(&self) -> &FormatState<Self::Context> {
- self.inner.state()
}
- fn state_mut(&mut self) -> &mut FormatState<Self::Context> {
- self.inner.state_mut()
- }
+#[derive(Debug, Copy, Clone)]
+pub struct Recorded<'a>(&'a [FormatElement]);
- fn snapshot(&self) -> BufferSnapshot {
- BufferSnapshot::Any(Box::new(WillBreakSnapshot {
- inner: self.inner.snapshot(),
- breaks: self.breaks,
- }))
- }
+impl Deref for Recorded<'_> {
+ type Target = [FormatElement];
- fn restore_snapshot(&mut self, snapshot: BufferSnapshot) {
- let snapshot = snapshot.unwrap_any::<WillBreakSnapshot>();
- self.inner.restore_snapshot(snapshot.inner);
- self.breaks = snapshot.breaks;
- }
+ fn deref(&self) -> &Self::Target {
+ self.0
}
-
-struct WillBreakSnapshot {
- inner: BufferSnapshot,
- breaks: bool,
}

MODIFY
crates/rome_formatter/src/builders.rs
crates/rome_formatter/src/builders.rs
use crate::prelude::*;
use crate::{
- format_element, write, Argument, Arguments, BufferSnapshot, FormatState, GroupId,
- PreambleBuffer, TextRange, TextSize,
+ format_element, write, Argument, Arguments, BufferSnapshot, FormatState, GroupId, TextRange,
+ TextSize,
};
use crate::{Buffer, VecBuffer};
use rome_rowan::{Language, SyntaxNode, SyntaxToken, SyntaxTokenText, TextLen};
@@ -528,9 +528,47 @@ impl<Context> std::fmt::Debug for FormatComment<'_, Context> {
///
/// This does not directly influence how this content will be printed, but some
/// parts of the formatter may inspect the [labelled element](FormatElement::Label)
-/// using `inspect_is_labelled` buffer extension and choose a different formatting layout.
+/// using [FormatElement::has_label].
///
-/// See [inspect_is_labelled](BufferExtensions::inspect_is_labelled) for documentation.
+/// ## Examples
+///
+/// ```rust
+/// use rome_formatter::prelude::*;
+/// use rome_formatter::{format, write, LineWidth};
+///
+/// enum SomeLabelId {}
+///
+/// let formatted = format!(
+/// SimpleFormatContext::default(),
+/// [format_with(|f| {
+/// let mut recording = f.start_recording();
+/// write!(recording, [
+/// labelled(
+/// LabelId::of::<SomeLabelId>(),
+/// &text("'I have a label'")
+/// )
+/// ])?;
+///
+/// let recorded = recording.stop();
+///
+/// let is_labelled = recorded.last().map_or(false, |element| element.has_label(LabelId::of::<SomeLabelId>()));
+///
+/// if is_labelled {
+/// write!(f, [text(" has label SomeLabelId")])
+/// } else {
+/// write!(f, [text(" doesn't have label SomeLabelId")])
+/// }
+/// })]
+/// )
+/// .unwrap();
+///
+/// assert_eq!("'I have a label' has label SomeLabelId", formatted.print().as_code());
+/// ```
+///
+/// ## Alternatives
+///
+/// Use `Memoized.inspect(f)?.has_label(LabelId::of::<SomeLabelId>()` if you need to know if some content breaks that should
+/// only be written later.
#[inline]
pub fn labelled<Content, Context>(label_id: LabelId, content: &Content) -> FormatLabelled<Context>
where
@@ -1417,6 +1455,10 @@ impl<Context> Buffer for GroupBuffer<'_, Context> {
Ok(())
}
+ fn elements(&self) -> &[FormatElement] {
+ &self.content
+ }
+
fn state(&self) -> &FormatState<Self::Context> {
self.inner.state()
}
@@ -2122,27 +2164,27 @@ where
/// that appear before the node in the input source.
pub fn entry<L: Language>(&mut self, node: &SyntaxNode<L>, content: &dyn Format<Context>) {
self.result = self.result.and_then(|_| {
- let mut buffer = PreambleBuffer::new(
- self.fmt,
- format_with(|f| {
if self.has_elements {
if get_lines_before(node) > 1 {
- write!(f, [empty_line()])?;
+ write!(self.fmt, [empty_line()])?;
} else {
- self.separator.fmt(f)?;
+ self.separator.fmt(self.fmt)?;
}
}
- Ok(())
- }),
- );
+ self.has_elements = true;
- write!(buffer, [content])?;
+ write!(self.fmt, [content])
+ });
+ }
- self.has_elements = self.has_elements || buffer.did_write_preamble();
+ /// Writes an entry without adding a separating line break or empty line.
+ pub fn entry_no_separator(&mut self, content: &dyn Format<Context>) {
+ self.result = self.result.and_then(|_| {
+ self.has_elements = true;
- Ok(())
- });
+ write!(self.fmt, [content])
+ })
}
/// Adds an iterator of entries to the output. Each entry is a `(node, content)` tuple.

MODIFY
crates/rome_formatter/src/format_element.rs
crates/rome_formatter/src/format_element.rs
@@ -705,7 +705,7 @@ impl FormatElement {
/// Utility function to get the "last element" of a [FormatElement], recursing
/// into lists and groups to find the last element that's not
- /// a line break, verbatim or a comment.
+ /// a line break, or a comment.
pub fn last_element(&self) -> Option<&FormatElement> {
match self {
FormatElement::List(list) => {

MODIFY
crates/rome_formatter/src/formatter.rs
crates/rome_formatter/src/formatter.rs
@@ -221,6 +221,10 @@ impl<Context> Buffer for Formatter<'_, Context> {
self.buffer.write_element(element)
}
+ fn elements(&self) -> &[FormatElement] {
+ self.buffer.elements()
+ }
+
#[inline(always)]
fn write_fmt(&mut self, arguments: Arguments<Self::Context>) -> FormatResult<()> {
for argument in arguments.items() {

MODIFY
crates/rome_formatter/src/lib.rs
crates/rome_formatter/src/lib.rs
@@ -47,10 +47,7 @@ use std::any::TypeId;
use crate::printed_tokens::PrintedTokens;
use crate::printer::{Printer, PrinterOptions};
pub use arguments::{Argument, Arguments};
-pub use buffer::{
- Buffer, BufferExtensions, BufferSnapshot, HasLabelBuffer, Inspect, PreambleBuffer, VecBuffer,
- WillBreakBuffer,
-};
+pub use buffer::{Buffer, BufferExtensions, BufferSnapshot, Inspect, PreambleBuffer, VecBuffer};
pub use builders::{
block_indent, comment, empty_line, get_lines_before, group, hard_line_break, if_group_breaks,
if_group_fits_on_line, indent, labelled, line_suffix, soft_block_indent, soft_line_break,

MODIFY
crates/rome_formatter/src/printer/mod.rs
crates/rome_formatter/src/printer/mod.rs
@@ -1168,7 +1168,7 @@ mod tests {
write!(&mut buffer, [root]).unwrap();
- Printer::new(options).print(&dbg!(buffer.into_element()))
+ Printer::new(options).print(&buffer.into_element())
}
#[test]

MODIFY
crates/rome_js_formatter/src/builders.rs
crates/rome_js_formatter/src/builders.rs
@@ -447,20 +447,20 @@ impl Format<JsFormatContext> for FormatDelimited<'_, '_> {
let mut is_empty = true;
let format_content = format_once(|f| {
- let mut buffer = f.inspect(|element| {
- if !element.is_empty() {
- is_empty = false
- }
- });
+ let mut recording = f.start_recording();
write!(
- buffer,
+ recording,
[
open_token_trailing_trivia,
format_content,
close_token_leading_trivia
]
- )
+ )?;
+
+ is_empty = recording.stop().is_empty();
+
+ Ok(())
});
soft_line_indent_or_space(&format_content).fmt(f)?;
@@ -534,15 +534,11 @@ impl<'t> OpenDelimiter<'t> {
/// 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();
+ let mut recording = f.start_recording();
+ write!(recording, [format_trailing_trivia(self.open_token)])?;
+ let recorded = recording.stop();
- if !trivia.is_empty() {
- f.write_elements(trivia)?;
+ if !recorded.is_empty() {
soft_line_break().fmt(f)?;
}
@@ -578,7 +574,7 @@ impl<'t> CloseDelimiter<'t> {
format_with(|f| {
let mut buffer = PreambleBuffer::new(f, soft_line_break());
- write!(buffer, [format_leading_trivia(self.close_token,)])
+ write!(buffer, [format_leading_trivia(self.close_token)])
})
}

MODIFY
crates/rome_js_formatter/src/js/lists/module_item_list.rs
crates/rome_js_formatter/src/js/lists/module_item_list.rs
use crate::prelude::*;
-use rome_js_syntax::JsModuleItemList;
+use rome_js_syntax::{JsAnyModuleItem, JsAnyStatement, JsModuleItemList};
#[derive(Debug, Clone, Default)]
pub struct FormatJsModuleItemList;
@@ -11,8 +11,15 @@ impl FormatRule<JsModuleItemList> for FormatJsModuleItemList {
let mut join = f.join_nodes_with_hardline();
for module_item in node {
+ match module_item {
+ JsAnyModuleItem::JsAnyStatement(JsAnyStatement::JsEmptyStatement(empty)) => {
+ join.entry_no_separator(&empty.format());
+ }
+ _ => {
join.entry(module_item.syntax(), &format_or_verbatim(&module_item));
}
+ }
+ }
join.finish()
}

MODIFY
crates/rome_js_formatter/src/js/lists/statement_list.rs
crates/rome_js_formatter/src/js/lists/statement_list.rs
use crate::prelude::*;
-use rome_js_syntax::JsStatementList;
+use rome_js_syntax::{JsAnyStatement, JsStatementList};
#[derive(Debug, Clone, Default)]
pub struct FormatJsStatementList;
@@ -11,8 +11,15 @@ impl FormatRule<JsStatementList> for FormatJsStatementList {
let mut join = f.join_nodes_with_hardline();
for statement in node.iter() {
+ match statement {
+ JsAnyStatement::JsEmptyStatement(empty) => {
+ join.entry_no_separator(&empty.format());
+ }
+ _ => {
join.entry(statement.syntax(), &format_or_verbatim(&statement));
}
+ }
+ }
join.finish()
}

MODIFY
crates/rome_js_formatter/src/jsx/tag/element.rs
crates/rome_js_formatter/src/jsx/tag/element.rs
@@ -38,9 +38,9 @@ impl Format<JsFormatContext> for JsxAnyTagWithChildren {
}
ElementLayout::Default => {
- let mut format_opening = format_opening.memoized();
-
- let opening_breaks = format_opening.inspect(f)?.will_break();
+ let mut recording = f.start_recording();
+ write!(recording, [format_opening])?;
+ let opening_breaks = recording.stop().iter().any(FormatElement::will_break);
let multiple_attributes = match self {
JsxAnyTagWithChildren::JsxElement(element) => {
@@ -58,7 +58,6 @@ impl Format<JsFormatContext> for JsxAnyTagWithChildren {
write!(
f,
[
- format_opening,
self.children().format().with_options(list_layout),
format_closing
]

MODIFY
crates/rome_js_formatter/src/ts/lists/type_member_list.rs
crates/rome_js_formatter/src/ts/lists/type_member_list.rs
use crate::prelude::*;
-use rome_formatter::{write, Buffer, VecBuffer};
+use rome_formatter::{write, Buffer};
use rome_js_syntax::{JsSyntaxKind, TsAnyTypeMember, TsTypeMemberList};
use rome_rowan::AstNodeList;
@@ -30,21 +30,19 @@ struct TsTypeMemberItem {
impl Format<JsFormatContext> for TsTypeMemberItem {
fn fmt(&self, f: &mut JsFormatter) -> FormatResult<()> {
- let mut buffer = VecBuffer::new(f.state_mut());
-
- write!(buffer, [self.member.format()])?;
-
- let formatted_element = buffer.into_element();
-
- let is_verbatim = matches!(
- formatted_element.last_element(),
- Some(FormatElement::Verbatim(_))
- );
+ let mut is_verbatim = false;
write!(
f,
[group(&format_once(|f| {
- f.write_element(formatted_element)
+ let mut recording = f.start_recording();
+ write!(recording, [self.member.format()])?;
+
+ is_verbatim = recording.stop().last().map_or(false, |last| {
+ matches!(last.last_element(), Some(FormatElement::Verbatim(_)))
+ });
+
+ Ok(())
}))]
)?;

MODIFY
crates/rome_js_formatter/src/utils/member_chain/mod.rs
crates/rome_js_formatter/src/utils/member_chain/mod.rs
@@ -112,7 +112,7 @@ use crate::utils::member_chain::groups::{
MemberChainGroup, MemberChainGroups, MemberChainGroupsBuilder,
};
use crate::utils::member_chain::simple_argument::SimpleArgument;
-use rome_formatter::{format_args, write, Buffer, Comments, CstFormatContext, PreambleBuffer};
+use rome_formatter::{format_args, write, Buffer, Comments, CstFormatContext};
use rome_js_syntax::{JsAnyExpression, JsCallExpression, JsExpressionStatement, JsLanguage};
use rome_rowan::{AstNode, SyntaxResult};
@@ -179,11 +179,7 @@ impl Format<JsFormatContext> for MemberChain {
))]
)
} else {
- // TODO This line suffix boundary shouldn't be needed but currently is because comments
- // can move over node boundaries. Follow up when re-working member chain formatting
- let mut buffer = PreambleBuffer::new(f, line_suffix_boundary());
-
- write!(buffer, [&self.tail])
+ write!(f, [self.tail])
}
}
}

MODIFY
crates/rome_js_formatter/src/utils/mod.rs
crates/rome_js_formatter/src/utils/mod.rs
@@ -27,7 +27,7 @@ pub(crate) use conditional::{ConditionalJsxChain, JsAnyConditional};
pub(crate) use member_chain::get_member_chain;
pub(crate) use object_like::JsObjectLike;
pub(crate) use object_pattern_like::JsObjectPatternLike;
-use rome_formatter::{format_args, write, Buffer, CommentStyle, VecBuffer};
+use rome_formatter::{format_args, write, Buffer, CommentStyle};
use rome_js_syntax::{JsAnyExpression, JsAnyStatement, JsInitializerClause, JsLanguage, Modifiers};
use rome_js_syntax::{JsSyntaxKind, JsSyntaxNode, JsSyntaxToken};
use rome_rowan::{AstNode, AstNodeList};
@@ -191,18 +191,16 @@ impl<'a> FormatWithSemicolon<'a> {
impl Format<JsFormatContext> for FormatWithSemicolon<'_> {
fn fmt(&self, f: &mut JsFormatter) -> FormatResult<()> {
- let mut buffer = VecBuffer::new(f.state_mut());
+ let mut recording = f.start_recording();
+ write!(recording, [self.content])?;
- write!(buffer, [self.content])?;
-
- let content = buffer.into_element();
-
- let is_unknown = match content.last_element() {
+ let is_unknown = recording
+ .stop()
+ .last()
+ .map_or(false, |last| match last.last_element() {
Some(FormatElement::Verbatim(elem)) => elem.is_unknown(),
_ => false,
- };
-
- f.write_element(content)?;
+ });
if let Some(semicolon) = self.semicolon {
write!(f, [semicolon.format()])?;

MODIFY
crates/rome_js_formatter/tests/specs/prettier/js/module-blocks/comments.js.snap
crates/rome_js_formatter/tests/specs/prettier/js/module-blocks/comments.js.snap
@@ -23,7 +23,7 @@ const m2 = module/* B1 */{
```diff
--- Prettier
+++ Rome
-@@ -1,12 +1,14 @@
+@@ -1,12 +1,13 @@
-const m = /*A1*/ module {
- /*A2*/ /*A3*/
+const m = /*A1*/ module /*A2*/;
@@ -34,8 +34,7 @@ const m2 = module/* B1 */{
export { foo }; /*A5*/
/*A6*/
-}; /*A7*/ /*A8*/
-+} /*A7*/
-+/*A8*/
++} /*A7*/ /*A8*/
-const m2 = module {
- /* B1 */
@@ -56,8 +55,7 @@ const m = /*A1*/ module /*A2*/;
export const foo = "foo";
export { foo }; /*A5*/
/*A6*/
-} /*A7*/
-/*A8*/
+} /*A7*/ /*A8*/
const m2 = module /* B1 */;
{

perf(rome_formatter): Add `Buffer.elements` (#3156)