@@ -9,6 +9,7 @@ use biome_rowan::TokenText;
99#[ cfg( target_pointer_width = "64" ) ]
1010use biome_rowan:: static_assert;
1111use std:: hash:: { Hash , Hasher } ;
12+ use std:: iter:: FusedIterator ;
1213use std:: ops:: Deref ;
1314use std:: rc:: Rc ;
1415
@@ -58,7 +59,7 @@ pub enum FormatElement {
5859
5960 /// A list of different variants representing the same content. The printer picks the best fitting content.
6061 /// Line breaks inside of a best fitting don't propagate to parent groups.
61- BestFitting ( BestFittingElement ) ,
62+ BestFitting ( BestFittingVariants ) ,
6263
6364 /// A [Tag] that marks the start/end of some content to which some special formatting is applied.
6465 Tag ( Tag ) ,
@@ -229,6 +230,14 @@ impl FormatElement {
229230 pub const fn is_line ( & self ) -> bool {
230231 matches ! ( self , Self :: Line ( _) )
231232 }
233+
234+ pub fn tag_kind ( & self ) -> Option < TagKind > {
235+ if let Self :: Tag ( tag) = self {
236+ Some ( tag. kind ( ) )
237+ } else {
238+ None
239+ }
240+ }
232241}
233242
234243impl FormatElements for FormatElement {
@@ -279,65 +288,145 @@ impl FormatElements for FormatElement {
279288/// Provides the printer with different representations for the same element so that the printer
280289/// can pick the best fitting variant.
281290///
282- /// Best fitting is defined as the variant that takes the most horizontal space but fits on the line.
283- # [ derive ( Clone , Eq , PartialEq ) ]
284- pub struct BestFittingElement {
285- /// The different variants for this element.
286- /// The first element is the one that takes up the most space horizontally (the most flat),
287- /// The last element takes up the least space horizontally (but most horizontal space ).
288- variants : Box < [ Box < [ FormatElement ] > ] > ,
289- }
291+ /// The best fitting is defined as the variant
292+ /// that takes the most horizontal space but fits on the line.
293+ /// The different variants for this element.
294+ ///
295+ /// The first element is the one that takes up the most space horizontally (the most flat),
296+ /// The last element takes up the least horizontal ( most vertical ).
297+ # [ derive ( Clone , Eq , PartialEq , Debug ) ]
298+ pub struct BestFittingVariants ( Box < [ FormatElement ] > ) ;
290299
291- impl BestFittingElement {
300+ impl BestFittingVariants {
292301 /// Creates a new best fitting IR with the given variants. The method itself isn't unsafe
293302 /// but it is to discourage people from using it because the printer will panic if
294303 /// the slice doesn't contain at least the least and most expanded variants.
295304 ///
296305 /// You're looking for a way to create a `BestFitting` object, use the `best_fitting![least_expanded, most_expanded]` macro.
297306 ///
298307 /// ## Safety
299- /// The slice must contain at least two variants.
308+ /// The slice must contain at least two variants (each delimited by StartEntry/EndEntry tags) .
300309 #[ doc( hidden) ]
301- pub unsafe fn from_vec_unchecked ( variants : Vec < Box < [ FormatElement ] > > ) -> Self {
310+ pub unsafe fn from_vec_unchecked ( variants : Vec < FormatElement > ) -> Self {
302311 debug_assert ! (
303- variants. len( ) >= 2 ,
312+ variants
313+ . iter( )
314+ . filter( |element| matches!( element, FormatElement :: Tag ( Tag :: StartBestFittingEntry ) ) )
315+ . count( )
316+ >= 2 ,
304317 "Requires at least the least expanded and most expanded variants"
305318 ) ;
306-
307- Self {
308- variants : variants. into_boxed_slice ( ) ,
309- }
319+ Self ( variants. into_boxed_slice ( ) )
310320 }
311321
312322 /// Returns the most expanded variant
313323 pub fn most_expanded ( & self ) -> & [ FormatElement ] {
314- self . variants . last ( ) . expect (
324+ debug_assert ! (
325+ self . as_slice( )
326+ . iter( )
327+ . filter( |element| matches!( element, FormatElement :: Tag ( Tag :: StartBestFittingEntry ) ) )
328+ . count( )
329+ >= 2 ,
330+ "Requires at least the least expanded and most expanded variants"
331+ ) ;
332+
333+ self . into_iter ( ) . last ( ) . expect (
315334 "Most contain at least two elements, as guaranteed by the best fitting builder." ,
316335 )
317336 }
318337
319- pub fn variants ( & self ) -> & [ Box < [ FormatElement ] > ] {
320- & self . variants
338+ pub fn as_slice ( & self ) -> & [ FormatElement ] {
339+ & self . 0
321340 }
322341
323- pub ( crate ) fn variants_mut ( & mut self ) -> & mut [ Box < [ FormatElement ] > ] {
324- & mut self . variants
342+ pub ( crate ) fn as_slice_mut ( & mut self ) -> & mut [ FormatElement ] {
343+ & mut self . 0
325344 }
326345
327346 /// Returns the least expanded variant
347+ ///
348+ /// # Panics
349+ ///
350+ /// When the number of variants is less than two.
328351 pub fn most_flat ( & self ) -> & [ FormatElement ] {
329- self . variants . first ( ) . expect (
330- "Most contain at least two elements, as guaranteed by the best fitting builder." ,
331- )
352+ debug_assert ! (
353+ self . as_slice( )
354+ . iter( )
355+ . filter( |element| matches!( element, FormatElement :: Tag ( Tag :: StartBestFittingEntry ) ) )
356+ . count( )
357+ >= 2 ,
358+ "Requires at least the least expanded and most expanded variants"
359+ ) ;
360+ self . into_iter ( ) . next ( ) . unwrap ( )
332361 }
333362}
334363
335- impl std:: fmt:: Debug for BestFittingElement {
336- fn fmt ( & self , f : & mut std:: fmt:: Formatter < ' _ > ) -> std:: fmt:: Result {
337- f. debug_list ( ) . entries ( & * self . variants ) . finish ( )
364+ impl Deref for BestFittingVariants {
365+ type Target = [ FormatElement ] ;
366+
367+ fn deref ( & self ) -> & Self :: Target {
368+ self . as_slice ( )
338369 }
339370}
340371
372+ pub struct BestFittingVariantsIter < ' a > {
373+ elements : & ' a [ FormatElement ] ,
374+ }
375+
376+ impl < ' a > IntoIterator for & ' a BestFittingVariants {
377+ type Item = & ' a [ FormatElement ] ;
378+ type IntoIter = BestFittingVariantsIter < ' a > ;
379+
380+ fn into_iter ( self ) -> Self :: IntoIter {
381+ BestFittingVariantsIter { elements : & self . 0 }
382+ }
383+ }
384+
385+ impl < ' a > Iterator for BestFittingVariantsIter < ' a > {
386+ type Item = & ' a [ FormatElement ] ;
387+
388+ fn next ( & mut self ) -> Option < Self :: Item > {
389+ match self . elements . first ( ) ? {
390+ FormatElement :: Tag ( Tag :: StartBestFittingEntry ) => {
391+ let end = self
392+ . elements
393+ . iter ( )
394+ . position ( |element| {
395+ matches ! ( element, FormatElement :: Tag ( Tag :: EndBestFittingEntry ) )
396+ } )
397+ . map_or ( self . elements . len ( ) , |position| position + 1 ) ;
398+
399+ let ( variant, rest) = self . elements . split_at ( end) ;
400+ self . elements = rest;
401+
402+ Some ( variant)
403+ }
404+ _ => None ,
405+ }
406+ }
407+
408+ fn last ( mut self ) -> Option < Self :: Item >
409+ where
410+ Self : Sized ,
411+ {
412+ self . next_back ( )
413+ }
414+ }
415+
416+ impl < ' a > DoubleEndedIterator for BestFittingVariantsIter < ' a > {
417+ fn next_back ( & mut self ) -> Option < Self :: Item > {
418+ let start_position = self . elements . iter ( ) . rposition ( |element| {
419+ matches ! ( element, FormatElement :: Tag ( Tag :: StartBestFittingEntry ) )
420+ } ) ?;
421+
422+ let ( rest, variant) = self . elements . split_at ( start_position) ;
423+ self . elements = rest;
424+ Some ( variant)
425+ }
426+ }
427+
428+ impl FusedIterator for BestFittingVariantsIter < ' _ > { }
429+
341430pub trait FormatElements {
342431 /// Returns true if this [FormatElement] is guaranteed to break across multiple lines by the printer.
343432 /// This is the case if this format element recursively contains a:
0 commit comments