Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
6 changes: 6 additions & 0 deletions .changeset/real-tires-move.md
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This isn't really a user facing change, so this doesn't need a changeset.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could if we manage to increase the performance of the formatter

Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@biomejs/biome": patch
---

Refactored `FormatElement::Text` and `FormatElement::LocatedTokenText` to remove source_position and use the new
`FormatElement::SourcePosition` for source tracking, improving memory layout.
7 changes: 5 additions & 2 deletions crates/biome_css_formatter/src/comments.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ impl FormatRule<SourceComment<CssLanguage>> for FormatCssLeadingComment {

// SAFETY: Safe, `is_doc_comment` only returns `true` for multiline comments
let first_line = lines.next().unwrap();
write!(f, [text(first_line.trim_end(), source_offset)])?;
write!(f, [text(first_line.trim_end(), Some(source_offset))])?;

source_offset += first_line.text_len();

Expand All @@ -44,7 +44,10 @@ impl FormatRule<SourceComment<CssLanguage>> for FormatCssLeadingComment {
1,
&format_once(|f| {
for line in lines {
write!(f, [hard_line_break(), text(line.trim(), source_offset)])?;
write!(
f,
[hard_line_break(), text(line.trim(), Some(source_offset))]
)?;

source_offset += line.text_len();
}
Expand Down
5 changes: 4 additions & 1 deletion crates/biome_css_formatter/src/css/value/url_value_raw.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ impl FormatNodeRule<CssUrlValueRaw> for FormatCssUrlValueRaw {
f,
[format_replaced(
&value_token,
&text(token_text.trim(), value_token.text_trimmed_range().start())
&text(
token_text.trim(),
Some(value_token.text_trimmed_range().start())
)
)]
)
}
Expand Down
5 changes: 4 additions & 1 deletion crates/biome_css_formatter/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -317,7 +317,10 @@ impl FormatRule<CssSyntaxToken> for FormatCssSyntaxToken {
match original.to_ascii_lowercase_cow() {
Cow::Borrowed(_) => self.format_trimmed_token_trivia(token, f),
Cow::Owned(lowercase) => {
write!(f, [text(&lowercase, token.text_trimmed_range().start())])
write!(
f,
[text(&lowercase, Some(token.text_trimmed_range().start()))]
)
}
}
} else {
Expand Down
2 changes: 1 addition & 1 deletion crates/biome_css_formatter/src/utils/string_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ impl Format<CssFormatContext> for FormatTokenAsLowercase {
f,
[format_replaced(
&self.token,
&text(&lowercase, self.token.text_trimmed_range().start()),
&text(&lowercase, Some(self.token.text_trimmed_range().start())),
)]
),
}
Expand Down
2 changes: 1 addition & 1 deletion crates/biome_css_formatter/src/verbatim.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ impl Format<CssFormatContext> for FormatCssVerbatimNode<'_> {

text(
&normalize_newlines(&original_source, LINE_TERMINATORS),
self.node.text_trimmed_range().start(),
Some(self.node.text_trimmed_range().start()),
)
.fmt(f)?;

Expand Down
15 changes: 7 additions & 8 deletions crates/biome_css_semantic/src/format_semantic_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use biome_formatter::{
FormatContext, FormatOptions, IndentStyle, IndentWidth, LineEnding, LineWidth,
TransformSourceMap,
};
use biome_rowan::{AstNode, TextSize};
use biome_rowan::AstNode;

#[derive(Debug, Default)]
struct FormatSemanticModelOptions;
Expand Down Expand Up @@ -92,7 +92,7 @@ impl Format<FormatSemanticModelContext> for Rule {
[
text(
self.node().syntax().text_trimmed().into_text().text(),
self.node().syntax().text_trimmed_range().start()
Some(self.node().syntax().text_trimmed_range().start())
),
token(":"),
space(),
Expand All @@ -108,13 +108,13 @@ impl Format<FormatSemanticModelContext> for Selector {
write!(
f,
[
text(self.text().into_text().text(), self.range().start()),
text(self.text().into_text().text(), Some(self.range().start())),
token(":"),
space(),
&self.specificity(),
space(),
token(" @ "),
text(range.as_str(), TextSize::default()),
text(range.as_str(), None),
]
)
}
Expand All @@ -126,13 +126,13 @@ impl Format<FormatSemanticModelContext> for Specificity {
f,
[
token("("),
text(self.0.to_string().as_str(), TextSize::default()),
text(self.0.to_string().as_str(), None),
token(","),
space(),
text(self.1.to_string().as_str(), TextSize::default()),
text(self.1.to_string().as_str(), None),
token(","),
space(),
text(self.2.to_string().as_str(), TextSize::default()),
text(self.2.to_string().as_str(), None),
token(")")
]
)
Expand All @@ -141,7 +141,6 @@ impl Format<FormatSemanticModelContext> for Specificity {

#[cfg(test)]
mod tests {

use crate::semantic_model;
use biome_css_parser::{CssParserOptions, parse_css};

Expand Down
76 changes: 66 additions & 10 deletions crates/biome_formatter/src/builders.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,8 +275,63 @@ impl std::fmt::Debug for Token {
}
}

/// Creates a source map entry from the passed source `position`
/// to the position in the formatted output.
///
/// ## Examples
///
/// ```
/// use biome_formatter::format;
/// use biome_formatter::prelude::*;
///
/// # fn main() -> FormatResult<()> {
/// use biome_rowan::TextSize;
/// use biome_formatter::SourceMarker;
///
///
/// let elements = format!(SimpleFormatContext::default(), [
/// source_position(TextSize::from(0)),
/// token("Hello "),
/// source_position(TextSize::from(7)),
/// token("'Biome'"),
/// ])?;
///
/// let printed = elements.print()?;
///
/// assert_eq!(printed.as_code(), r#"Hello 'Biome'"#);
/// assert_eq!(printed.sourcemap(), [
/// SourceMarker { source: TextSize::from(0), dest: TextSize::from(0) },
/// SourceMarker { source: TextSize::from(6), dest: TextSize::from(6) },
/// SourceMarker { source: TextSize::from(7), dest: TextSize::from(6) },
/// SourceMarker { source: TextSize::from(14), dest: TextSize::from(13) },
/// ]);
///
/// # Ok(())
/// # }
/// ```
pub const fn source_position(position: TextSize) -> SourcePosition {
SourcePosition(position)
}

#[derive(Eq, PartialEq, Copy, Clone, Debug)]
pub struct SourcePosition(TextSize);

impl<Context> Format<Context> for SourcePosition {
fn fmt(&self, f: &mut Formatter<Context>) -> FormatResult<()> {
if let Some(FormatElement::SourcePosition(last_position)) = f.buffer.elements().last()
&& *last_position == self.0
{
return Ok(());
}

f.write_element(FormatElement::SourcePosition(self.0))?;

Ok(())
}
}

/// Creates a text from a dynamic string and a range of the input source
pub fn text(text: &str, position: TextSize) -> Text<'_> {
pub fn text(text: &str, position: Option<TextSize>) -> Text<'_> {
debug_assert_no_newlines(text);

Text { text, position }
Expand All @@ -285,14 +340,17 @@ pub fn text(text: &str, position: TextSize) -> Text<'_> {
#[derive(Eq, PartialEq)]
pub struct Text<'a> {
text: &'a str,
position: TextSize,
position: Option<TextSize>,
}

impl<Context> Format<Context> for Text<'_> {
fn fmt(&self, f: &mut Formatter<Context>) -> FormatResult<()> {
if let Some(position) = self.position {
source_position(position).fmt(f)?;
}

f.write_element(FormatElement::Text {
text: self.text.to_string().into_boxed_str(),
source_position: self.position,
})
}
}
Expand Down Expand Up @@ -323,6 +381,8 @@ pub struct SyntaxTokenCowSlice<'a, L: Language> {

impl<L: Language, Context> Format<Context> for SyntaxTokenCowSlice<'_, L> {
fn fmt(&self, f: &mut Formatter<Context>) -> FormatResult<()> {
source_position(self.start).fmt(f)?;

match &self.text {
Cow::Borrowed(text) => {
let range = TextRange::at(self.start, text.text_len());
Expand All @@ -335,14 +395,10 @@ impl<L: Language, Context> Format<Context> for SyntaxTokenCowSlice<'_, L> {
let relative_range = range - self.token.text_range().start();
let slice = self.token.token_text().slice(relative_range);

f.write_element(FormatElement::LocatedTokenText {
slice,
source_position: self.start,
})
f.write_element(FormatElement::LocatedTokenText { slice })
}
Cow::Owned(text) => f.write_element(FormatElement::Text {
text: text.clone().into_boxed_str(),
source_position: self.start,
}),
}
}
Expand Down Expand Up @@ -377,9 +433,9 @@ pub struct LocatedTokenText {

impl<Context> Format<Context> for LocatedTokenText {
fn fmt(&self, f: &mut Formatter<Context>) -> FormatResult<()> {
f.write_element(FormatElement::SourcePosition(self.source_position))?;
f.write_element(FormatElement::LocatedTokenText {
slice: self.text.clone(),
source_position: self.source_position,
})
}
}
Expand Down Expand Up @@ -2285,7 +2341,7 @@ impl<Context, T> std::fmt::Debug for FormatWith<Context, T> {
/// let mut join = f.join_with(&separator);
///
/// for item in &self.items {
/// join.entry(&format_with(|f| write!(f, [text(item, TextSize::default())])));
/// join.entry(&format_with(|f| write!(f, [text(item, None)])));
/// }
/// join.finish()
/// })),
Expand Down
16 changes: 10 additions & 6 deletions crates/biome_formatter/src/format_element.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ pub enum FormatElement {
/// Forces the parent group to print in expanded mode.
ExpandParent,

/// Indicates the position of the elements coming after this element in the source document.
/// The printer will create a source map entry from this position in the source document to the
/// formatted position.
SourcePosition(TextSize),

/// A ASCII only Token that contains no line breaks or tab characters.
Token {
text: &'static str,
Expand All @@ -35,15 +40,11 @@ pub enum FormatElement {
Text {
/// There's no need for the text to be mutable, using `Box<str>` safes 8 bytes over `String`.
text: Box<str>,
/// The start position of the text in the unformatted source code
source_position: TextSize,
},

/// A token for a text that is taken as is from the source code (input text and formatted representation are identical).
/// Implementing by taking a slice from a `SyntaxToken` to avoid allocating a new string.
LocatedTokenText {
/// The start position of the token in the unformatted source code
source_position: TextSize,
/// The token text
slice: TokenText,
},
Expand All @@ -70,6 +71,9 @@ impl std::fmt::Debug for FormatElement {
Self::Space | Self::HardSpace => write!(fmt, "Space"),
Self::Line(mode) => fmt.debug_tuple("Line").field(mode).finish(),
Self::ExpandParent => write!(fmt, "ExpandParent"),
Self::SourcePosition(position) => {
fmt.debug_tuple("SourcePosition").field(position).finish()
}
Self::Token { text } => fmt.debug_tuple("Token").field(text).finish(),
Self::Text { text, .. } => fmt.debug_tuple("Text").field(text).finish(),
Self::LocatedTokenText { slice, .. } => {
Expand Down Expand Up @@ -248,7 +252,8 @@ impl FormatElements for FormatElement {
| Self::Space
| Self::Tag(_)
| Self::HardSpace
| Self::Token { .. } => false,
| Self::Token { .. }
| Self::SourcePosition(_) => false,
}
}

Expand Down Expand Up @@ -372,7 +377,6 @@ pub trait FormatElements {

#[cfg(test)]
mod tests {

use crate::format_element::{LINE_TERMINATORS, normalize_newlines};

#[test]
Expand Down
Loading
Loading