Thanks to visit codestin.com
Credit goes to github.com

Skip to content
Merged
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
5 changes: 5 additions & 0 deletions .changeset/blue-teeth-obey.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@biomejs/biome": patch
---

Reduced the internal memory used by the Biome formatter.
4 changes: 2 additions & 2 deletions crates/biome_analyze/src/diagnostics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ impl From<RuleDiagnostic> for AnalyzerDiagnostic {
}

#[derive(Debug)]
enum DiagnosticKind {
pub enum DiagnosticKind {
/// It holds various info related to diagnostics emitted by the rules
Rule(Box<RuleDiagnostic>),
/// We have raw information to create a basic [Diagnostic]
Expand Down Expand Up @@ -135,7 +135,7 @@ impl AnalyzerDiagnostic {
}

/// The location of the diagnostic is shifted using this offset.
/// This is only applied when the [Self::kind] is [DiagnosticKind::Rule]
/// This is only applied when the `Self::kind` is [DiagnosticKind::Rule]
pub fn add_diagnostic_offset(&mut self, offset: TextSize) {
if let DiagnosticKind::Rule(rule_diagnostic) = &mut self.kind {
let diagnostic = rule_diagnostic.as_mut();
Expand Down
8 changes: 4 additions & 4 deletions crates/biome_configuration/src/analyzer/assist/actions.rs

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

16 changes: 8 additions & 8 deletions crates/biome_configuration/src/analyzer/linter/rules.rs

Large diffs are not rendered by default.

17 changes: 6 additions & 11 deletions crates/biome_formatter/src/builders.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use crate::format_element::tag::{Condition, Tag};
use crate::prelude::tag::{DedentMode, GroupMode, LabelId};
use crate::prelude::*;
use crate::{Argument, Arguments, GroupId, TextRange, TextSize, format_element, write};
use crate::{Argument, Arguments, GroupId, TextRange, TextSize, write};
use crate::{Buffer, VecBuffer};
use Tag::*;
use biome_rowan::{Language, SyntaxNode, SyntaxToken, TextLen, TokenText};
Expand Down Expand Up @@ -2669,25 +2669,20 @@ impl<'a, Context> BestFitting<'a, Context> {

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

let mut formatted_variants = Vec::with_capacity(variants.len());
// Each variant is wrapped in Start/End tags
let mut buffer = VecBuffer::with_capacity(variants.len() * 3, f.state_mut());

for variant in variants {
buffer.write_element(FormatElement::Tag(StartEntry))?;
buffer.write_element(FormatElement::Tag(StartBestFittingEntry))?;
buffer.write_fmt(Arguments::from(variant))?;
buffer.write_element(FormatElement::Tag(EndEntry))?;

formatted_variants.push(buffer.take_vec().into_boxed_slice());
buffer.write_element(FormatElement::Tag(EndBestFittingEntry))?;
}

// SAFETY: The constructor guarantees that there are always at least two variants. It's, therefore,
// safe to call into the unsafe `from_vec_unchecked` function
let element = unsafe {
FormatElement::BestFitting(format_element::BestFittingElement::from_vec_unchecked(
formatted_variants,
))
FormatElement::BestFitting(BestFittingVariants::from_vec_unchecked(buffer.into_vec()))
};

f.write_element(element)
Expand Down
145 changes: 117 additions & 28 deletions crates/biome_formatter/src/format_element.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ use biome_rowan::TokenText;
#[cfg(target_pointer_width = "64")]
use biome_rowan::static_assert;
use std::hash::{Hash, Hasher};
use std::iter::FusedIterator;
use std::ops::Deref;
use std::rc::Rc;

Expand Down Expand Up @@ -58,7 +59,7 @@ 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(BestFittingElement),
BestFitting(BestFittingVariants),

/// A [Tag] that marks the start/end of some content to which some special formatting is applied.
Tag(Tag),
Expand Down Expand Up @@ -229,6 +230,14 @@ impl FormatElement {
pub const fn is_line(&self) -> bool {
matches!(self, Self::Line(_))
}

pub fn tag_kind(&self) -> Option<TagKind> {
if let Self::Tag(tag) = self {
Some(tag.kind())
} else {
None
}
}
}

impl FormatElements for FormatElement {
Expand Down Expand Up @@ -279,65 +288,145 @@ impl FormatElements for FormatElement {
/// Provides the printer with different representations for the same element so that the printer
/// can pick the best fitting variant.
///
/// Best fitting is defined as the variant that takes the most horizontal space but fits on the line.
#[derive(Clone, Eq, PartialEq)]
pub struct BestFittingElement {
/// The different variants for this element.
/// The first element is the one that takes up the most space horizontally (the most flat),
/// The last element takes up the least space horizontally (but most horizontal space).
variants: Box<[Box<[FormatElement]>]>,
}
/// The best fitting is defined as the variant
/// that takes the most horizontal space but fits on the line.
/// The different variants for this element.
///
/// The first element is the one that takes up the most space horizontally (the most flat),
/// The last element takes up the least horizontal (most vertical).
#[derive(Clone, Eq, PartialEq, Debug)]
pub struct BestFittingVariants(Box<[FormatElement]>);

impl BestFittingElement {
impl BestFittingVariants {
/// Creates a new best fitting IR with the given variants. The method itself isn't unsafe
/// but it is to discourage people from using it because the printer will panic if
/// the slice doesn't contain at least the least and most expanded variants.
///
/// You're looking for a way to create a `BestFitting` object, use the `best_fitting![least_expanded, most_expanded]` macro.
///
/// ## Safety
/// The slice must contain at least two variants.
/// The slice must contain at least two variants (each delimited by StartEntry/EndEntry tags).
#[doc(hidden)]
pub unsafe fn from_vec_unchecked(variants: Vec<Box<[FormatElement]>>) -> Self {
pub unsafe fn from_vec_unchecked(variants: Vec<FormatElement>) -> Self {
debug_assert!(
variants.len() >= 2,
variants
.iter()
.filter(|element| matches!(element, FormatElement::Tag(Tag::StartBestFittingEntry)))
.count()
>= 2,
"Requires at least the least expanded and most expanded variants"
);

Self {
variants: variants.into_boxed_slice(),
}
Self(variants.into_boxed_slice())
}

/// Returns the most expanded variant
pub fn most_expanded(&self) -> &[FormatElement] {
self.variants.last().expect(
debug_assert!(
self.as_slice()
.iter()
.filter(|element| matches!(element, FormatElement::Tag(Tag::StartBestFittingEntry)))
.count()
>= 2,
"Requires at least the least expanded and most expanded variants"
);

self.into_iter().last().expect(
"Most contain at least two elements, as guaranteed by the best fitting builder.",
)
}

pub fn variants(&self) -> &[Box<[FormatElement]>] {
&self.variants
pub fn as_slice(&self) -> &[FormatElement] {
&self.0
}

pub(crate) fn variants_mut(&mut self) -> &mut [Box<[FormatElement]>] {
&mut self.variants
pub(crate) fn as_slice_mut(&mut self) -> &mut [FormatElement] {
&mut self.0
}

/// Returns the least expanded variant
///
/// # Panics
///
/// When the number of variants is less than two.
pub fn most_flat(&self) -> &[FormatElement] {
self.variants.first().expect(
"Most contain at least two elements, as guaranteed by the best fitting builder.",
)
debug_assert!(
self.as_slice()
.iter()
.filter(|element| matches!(element, FormatElement::Tag(Tag::StartBestFittingEntry)))
.count()
>= 2,
"Requires at least the least expanded and most expanded variants"
);
self.into_iter().next().unwrap()
}
}

impl std::fmt::Debug for BestFittingElement {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_list().entries(&*self.variants).finish()
impl Deref for BestFittingVariants {
type Target = [FormatElement];

fn deref(&self) -> &Self::Target {
self.as_slice()
}
}

pub struct BestFittingVariantsIter<'a> {
elements: &'a [FormatElement],
}

impl<'a> IntoIterator for &'a BestFittingVariants {
type Item = &'a [FormatElement];
type IntoIter = BestFittingVariantsIter<'a>;

fn into_iter(self) -> Self::IntoIter {
BestFittingVariantsIter { elements: &self.0 }
}
}

impl<'a> Iterator for BestFittingVariantsIter<'a> {
type Item = &'a [FormatElement];

fn next(&mut self) -> Option<Self::Item> {
match self.elements.first()? {
FormatElement::Tag(Tag::StartBestFittingEntry) => {
let end = self
.elements
.iter()
.position(|element| {
matches!(element, FormatElement::Tag(Tag::EndBestFittingEntry))
})
.map_or(self.elements.len(), |position| position + 1);

let (variant, rest) = self.elements.split_at(end);
self.elements = rest;

Some(variant)
}
_ => None,
}
}

fn last(mut self) -> Option<Self::Item>
where
Self: Sized,
{
self.next_back()
}
}

impl<'a> DoubleEndedIterator for BestFittingVariantsIter<'a> {
fn next_back(&mut self) -> Option<Self::Item> {
let start_position = self.elements.iter().rposition(|element| {
matches!(element, FormatElement::Tag(Tag::StartBestFittingEntry))
})?;

let (rest, variant) = self.elements.split_at(start_position);
self.elements = rest;
Some(variant)
}
}

impl FusedIterator for BestFittingVariantsIter<'_> {}

pub trait FormatElements {
/// Returns true if this [FormatElement] is guaranteed to break across multiple lines by the printer.
/// This is the case if this format element recursively contains a:
Expand Down
33 changes: 16 additions & 17 deletions crates/biome_formatter/src/format_element/document.rs
Original file line number Diff line number Diff line change
Expand Up @@ -71,13 +71,10 @@ impl Document {
interned_expands
}
},
FormatElement::BestFitting(best_fitting) => {
FormatElement::BestFitting(variants) => {
enclosing.push(Enclosing::BestFitting);

for variant in best_fitting.variants() {
propagate_expands(variant, enclosing, checked_interned);
}

propagate_expands(variants, enclosing, checked_interned);
enclosing.pop();
// BestFitting acts as a boundary, meaning there is no need to continue
// processing this element and we can move onto the next. However, we
Expand Down Expand Up @@ -181,9 +178,7 @@ fn transform_elements(
*element = FormatElement::Interned(Interned::new(nested_elements));
}
FormatElement::BestFitting(best_fitting) => {
for variant in best_fitting.variants_mut() {
transform_elements(variant, visitor);
}
transform_elements(best_fitting.as_slice_mut(), visitor);
}
_ => {
if let Some(replacement) = visitor(element) {
Expand Down Expand Up @@ -375,8 +370,8 @@ impl Format<IrFormatContext> for &[FormatElement] {
FormatElement::Line(LineMode::Hard),
])?;

for variant in best_fitting.variants() {
write!(f, [variant.deref(), hard_line_break()])?;
for variant in best_fitting {
write!(f, [variant, hard_line_break()])?;
}

f.write_elements([
Expand Down Expand Up @@ -584,10 +579,10 @@ impl Format<IrFormatContext> for &[FormatElement] {
StartEmbedded(_) => {
write!(f, [token("embedded(")])?;
}
StartEntry => {
StartEntry | StartBestFittingEntry => {
// handled after the match for all start tags
}
EndEntry => write!(f, [ContentArrayEnd])?,
EndEntry | EndBestFittingEntry => write!(f, [ContentArrayEnd])?,

EndFill
| EndLabelled
Expand Down Expand Up @@ -884,9 +879,13 @@ mod tests {
// best_fitting(...)
// ]
let interned = Interned::new(vec![FormatElement::ExpandParent, unsafe {
FormatElement::BestFitting(BestFittingElement::from_vec_unchecked(vec![
Box::new([FormatElement::Token { text: "a" }]),
Box::new([FormatElement::Token { text: "b" }]),
FormatElement::BestFitting(BestFittingVariants::from_vec_unchecked(vec![
FormatElement::Tag(StartBestFittingEntry),
FormatElement::Token { text: "a" },
FormatElement::Tag(EndBestFittingEntry),
FormatElement::Tag(StartBestFittingEntry),
FormatElement::Token { text: "b" },
FormatElement::Tag(EndBestFittingEntry),
]))
}]);

Expand All @@ -911,8 +910,8 @@ mod tests {
<interned 0> [
expand_parent,
best_fitting([
["a"]
["b"]
[["a"]]
[["b"]]
])
]
]),
Expand Down
Loading
Loading