11use std:: {
22 borrow:: Cow ,
3+ cmp:: Ordering ,
34 collections:: { HashMap , HashSet } ,
45 mem,
56 ops:: Deref ,
@@ -307,6 +308,34 @@ impl AtomCollection {
307308 predicates,
308309 ) ;
309310 }
311+ "prepend_begin_measuring_scope" => {
312+ self . prepend (
313+ Atom :: MeasuringScopeBegin ( scope_information_prepend ( ) ?) ,
314+ node,
315+ predicates,
316+ ) ;
317+ }
318+ "append_begin_measuring_scope" => {
319+ self . append (
320+ Atom :: MeasuringScopeBegin ( scope_information_append ( ) ?) ,
321+ node,
322+ predicates,
323+ ) ;
324+ }
325+ "prepend_end_measuring_scope" => {
326+ self . prepend (
327+ Atom :: MeasuringScopeEnd ( scope_information_prepend ( ) ?) ,
328+ node,
329+ predicates,
330+ ) ;
331+ }
332+ "append_end_measuring_scope" => {
333+ self . append (
334+ Atom :: MeasuringScopeEnd ( scope_information_append ( ) ?) ,
335+ node,
336+ predicates,
337+ ) ;
338+ }
310339 // Scoped softlines
311340 "append_empty_scoped_softline" => {
312341 let id = self . next_id ( ) ;
@@ -404,10 +433,29 @@ impl AtomCollection {
404433 pub fn apply_prepends_and_appends ( & mut self ) {
405434 let mut expanded: Vec < Atom > = Vec :: new ( ) ;
406435
436+ // We sort the prepends/appends so that:
437+ // * BeginScope(s) will always be the first element(s)
438+ // * EndScope(s) will always be the last element(s)
439+ // This permits proper processing of measuring scopes and scoped atoms
440+ // that are added at the same place as Begin/EndScopes.
441+ fn compare_atoms ( a1 : & Atom , a2 : & Atom ) -> Ordering {
442+ match ( a1, a2) {
443+ ( Atom :: ScopeBegin ( _) , Atom :: ScopeBegin ( _) ) => { Ordering :: Equal }
444+ ( Atom :: ScopeBegin ( _) , _) => { Ordering :: Less }
445+ ( _, Atom :: ScopeBegin ( _) ) => { Ordering :: Greater }
446+ ( Atom :: ScopeEnd ( _) , Atom :: ScopeEnd ( _) ) => { Ordering :: Equal }
447+ ( Atom :: ScopeEnd ( _) , _) => { Ordering :: Greater }
448+ ( _, Atom :: ScopeEnd ( _) ) => { Ordering :: Less }
449+ ( _, _) => { Ordering :: Equal }
450+ }
451+ }
452+
407453 for atom in & mut self . atoms {
408454 if let Atom :: Leaf { id, .. } = atom {
409455 let prepends = self . prepend . entry ( * id) . or_default ( ) ;
456+ prepends. sort_by ( compare_atoms) ;
410457 let appends = self . append . entry ( * id) . or_default ( ) ;
458+ appends. sort_by ( compare_atoms) ;
411459
412460 // Rather than cloning the atom from the old vector, we
413461 // simply take it. This will leave a default (empty) atom
@@ -605,10 +653,15 @@ impl AtomCollection {
605653 type ScopeId = String ;
606654 type LineIndex = u32 ;
607655 type ScopedNodeId = usize ;
608- // `opened_scopes` maintains stacks of opened scopes,
609- // the line at which they started,
610- // and the list of `ScopedSoftline` they contain.
611- let mut opened_scopes: HashMap < & ScopeId , Vec < ( LineIndex , Vec < & Atom > ) > > = HashMap :: new ( ) ;
656+ // `opened_scopes` maintains stacks of opened scopes.
657+ // For each scope, we record:
658+ // * the line at which they started (LineIndex),
659+ // * the list of `ScopedSoftline` and `ScopedConditional` they contain (Vec<&Atom>),
660+ // * if they contain a measuring scope, whether it is multi-line (Option<bool>).
661+ let mut opened_scopes: HashMap < & ScopeId , Vec < ( LineIndex , Vec < & Atom > , Option < bool > ) > > = HashMap :: new ( ) ;
662+ // `opened_measuring_scopes` maintains stacks of opened measuring scopes,
663+ // and the line at which they started.
664+ let mut opened_measuring_scopes: HashMap < & ScopeId , Vec < LineIndex > > = HashMap :: new ( ) ;
612665 // We can't process `ScopedSoftline` in-place as we encounter them in the list of
613666 // atoms: we need to know when their encompassing scope ends to decide what to
614667 // replace them with. Instead of in-place modifications, we associate a replacement
@@ -631,16 +684,20 @@ impl AtomCollection {
631684 opened_scopes
632685 . entry ( scope_id)
633686 . or_default ( )
634- . push ( ( * line_start, Vec :: new ( ) ) ) ;
687+ . push ( ( * line_start, Vec :: new ( ) , None ) ) ;
635688 } else if let Atom :: ScopeEnd ( ScopeInformation {
636689 line_number : line_end,
637690 scope_id,
638691 } ) = atom
639692 {
640- if let Some ( ( line_start, atoms) ) =
693+ if let Some ( ( line_start, atoms, measuring_scope ) ) =
641694 opened_scopes. get_mut ( scope_id) . and_then ( Vec :: pop)
642695 {
643- let multiline = line_start != * line_end;
696+ let multiline = if let Some ( mult) = measuring_scope {
697+ mult
698+ } else {
699+ line_start != * line_end
700+ } ;
644701 for atom in atoms {
645702 if let Atom :: ScopedSoftline { id, spaced, .. } = atom {
646703 let new_atom = if multiline {
@@ -671,9 +728,54 @@ impl AtomCollection {
671728 log:: warn!( "Closing unopened scope {scope_id:?}" ) ;
672729 force_apply_modifications = true ;
673730 }
731+ // Open measuring scope
732+ } else if let Atom :: MeasuringScopeBegin ( ScopeInformation {
733+ line_number : line_start,
734+ scope_id,
735+ } ) = atom
736+ {
737+ if opened_scopes. entry ( scope_id) . or_default ( ) . is_empty ( ) {
738+ log:: warn!( "Opening measuring scope with no associated regular scope {scope_id:?}" ) ;
739+ force_apply_modifications = true ;
740+ } else {
741+ opened_measuring_scopes
742+ . entry ( scope_id)
743+ . or_default ( )
744+ . push ( * line_start)
745+ }
746+ // Close measuring scope and register multi-line-ness in the appropriate regular scope
747+ } else if let Atom :: MeasuringScopeEnd ( ScopeInformation {
748+ line_number : line_end,
749+ scope_id,
750+ } ) = atom
751+ {
752+ if let Some ( line_start) =
753+ opened_measuring_scopes. get_mut ( scope_id) . and_then ( Vec :: pop)
754+ {
755+ let multi_line = line_start != * line_end;
756+ if let Some ( ( regular_line_start, vec, measuring_scope) ) =
757+ opened_scopes. get_mut ( scope_id) . and_then ( Vec :: pop)
758+ {
759+ if measuring_scope == None {
760+ opened_scopes
761+ . entry ( scope_id)
762+ . or_default ( )
763+ . push ( ( regular_line_start, vec, Some ( multi_line) ) ) ;
764+ } else {
765+ log:: warn!( "Found several measuring scopes in a single regular scope {scope_id:?}" ) ;
766+ force_apply_modifications = true ;
767+ }
768+ } else {
769+ log:: warn!( "Found measuring scope outside of regular scope {scope_id:?}" ) ;
770+ force_apply_modifications = true ;
771+ }
772+ } else {
773+ log:: warn!( "Closing unopened measuring scope {scope_id:?}" ) ;
774+ force_apply_modifications = true ;
775+ }
674776 // Register the ScopedSoftline in the correct scope
675777 } else if let Atom :: ScopedSoftline { scope_id, .. } = atom {
676- if let Some ( ( _, vec) ) = opened_scopes. get_mut ( & scope_id) . and_then ( |v| v. last_mut ( ) )
778+ if let Some ( ( _, vec, _ ) ) = opened_scopes. get_mut ( & scope_id) . and_then ( |v| v. last_mut ( ) )
677779 {
678780 vec. push ( atom) ;
679781 } else {
@@ -682,7 +784,7 @@ impl AtomCollection {
682784 }
683785 // Register the ScopedConditional in the correct scope
684786 } else if let Atom :: ScopedConditional { scope_id, .. } = atom {
685- if let Some ( ( _, vec) ) = opened_scopes. get_mut ( & scope_id) . and_then ( |v| v. last_mut ( ) )
787+ if let Some ( ( _, vec, _ ) ) = opened_scopes. get_mut ( & scope_id) . and_then ( |v| v. last_mut ( ) )
686788 {
687789 vec. push ( atom) ;
688790 } else {
@@ -691,21 +793,32 @@ impl AtomCollection {
691793 }
692794 }
693795 }
694- let still_opened: Vec < & String > = opened_scopes
796+ let mut still_opened: Vec < & String > = opened_scopes
695797 . into_iter ( )
696798 . filter_map ( |( scope_id, vec) | if vec. is_empty ( ) { None } else { Some ( scope_id) } )
697799 . collect ( ) ;
698800 if !still_opened. is_empty ( ) {
699801 log:: warn!( "Some scopes have been left opened: {:?}" , still_opened) ;
700802 force_apply_modifications = true ;
701803 }
804+ still_opened = opened_measuring_scopes
805+ . into_iter ( )
806+ . filter_map ( |( scope_id, vec) | if vec. is_empty ( ) { None } else { Some ( scope_id) } )
807+ . collect ( ) ;
808+ if !still_opened. is_empty ( ) {
809+ log:: warn!( "Some measuring scopes have been left opened: {:?}" , still_opened) ;
810+ force_apply_modifications = true ;
811+ }
702812
703813 // Remove scopes from the atom list
704814 for atom in & mut self . atoms {
705- match atom {
706- Atom :: ScopeBegin ( _) | Atom :: ScopeEnd ( _) => {
707- * atom = Atom :: Empty ;
708- }
815+ match atom
816+ {
817+ | Atom :: ScopeBegin ( _)
818+ | Atom :: ScopeEnd ( _)
819+ | Atom :: MeasuringScopeBegin ( _)
820+ | Atom :: MeasuringScopeEnd ( _) =>
821+ { * atom = Atom :: Empty }
709822 _ => { }
710823 }
711824 }
@@ -954,7 +1067,7 @@ fn collapse_spaces_before_antispace(v: &mut [Atom]) {
9541067 antispace_mode = true ;
9551068 } else if * a == Atom :: Space && antispace_mode {
9561069 * a = Atom :: Empty ;
957- } else {
1070+ } else if * a != Atom :: Empty { // Don't change mode when encountering Empty
9581071 antispace_mode = false ;
9591072 }
9601073 }
0 commit comments