@@ -400,19 +400,24 @@ impl<'s> DocGen<'s> for Element<'s> {
400400 . split_once ( ':' )
401401 . and_then ( |( namespace, name) | namespace. eq_ignore_ascii_case ( "html" ) . then_some ( name) )
402402 . unwrap_or ( self . tag_name ) ;
403+ let formatted_tag_name = if matches ! (
404+ ctx. language,
405+ Language :: Html | Language :: Jinja | Language :: Vento
406+ ) && css_dataset:: tags:: STANDARD_HTML_TAGS
407+ . iter ( )
408+ . any ( |tag| tag. eq_ignore_ascii_case ( self . tag_name ) )
409+ {
410+ Cow :: from ( self . tag_name . to_ascii_lowercase ( ) )
411+ } else {
412+ Cow :: from ( self . tag_name )
413+ } ;
403414 let is_root = state. is_root ;
404415 let mut state = State {
405416 current_tag_name : Some ( tag_name) ,
406417 is_root : false ,
407418 in_svg : tag_name. eq_ignore_ascii_case ( "svg" ) ,
408419 indent_level : state. indent_level ,
409420 } ;
410- let should_lower_cased = matches ! (
411- ctx. language,
412- Language :: Html | Language :: Jinja | Language :: Vento
413- ) && css_dataset:: tags:: STANDARD_HTML_TAGS
414- . iter ( )
415- . any ( |tag| tag. eq_ignore_ascii_case ( self . tag_name ) ) ;
416421
417422 let self_closing = if helpers:: is_void_element ( tag_name, ctx. language ) {
418423 ctx. options
@@ -447,115 +452,128 @@ impl<'s> DocGen<'s> for Element<'s> {
447452 let mut docs = Vec :: with_capacity ( 5 ) ;
448453
449454 docs. push ( Doc :: text ( "<" ) ) ;
450- docs. push ( Doc :: text ( if should_lower_cased {
451- Cow :: from ( self . tag_name . to_ascii_lowercase ( ) )
452- } else {
453- Cow :: from ( self . tag_name )
454- } ) ) ;
455-
456- let attrs_sep = if !self . first_attr_same_line
457- && !ctx. options . prefer_attrs_single_line
458- && self . attrs . len ( ) > 1
459- && !ctx
460- . options
461- . max_attrs_per_line
462- . map ( |value| value. get ( ) > 1 )
463- . unwrap_or_default ( )
464- {
465- Doc :: hard_line ( )
466- } else {
467- Doc :: line_or_space ( )
468- } ;
469- let attrs = if let Some ( max) = ctx. options . max_attrs_per_line {
470- // fix #2
471- if self . attrs . is_empty ( ) {
472- Doc :: line_or_nil ( )
473- } else {
474- Doc :: line_or_space ( )
455+ docs. push ( Doc :: text ( formatted_tag_name. clone ( ) ) ) ;
456+
457+ match self . attrs . as_slice ( ) {
458+ [ attr] if !is_whitespace_sensitive && !is_multi_line_attr ( attr) => {
459+ docs. push ( Doc :: space ( ) ) ;
460+ docs. push ( attr. doc ( ctx, & state) ) ;
461+ if self_closing && is_empty {
462+ docs. push ( Doc :: text ( " />" ) ) ;
463+ return Doc :: list ( docs) ;
464+ } else {
465+ docs. push ( Doc :: text ( ">" ) ) ;
466+ } ;
467+ if self . void_element {
468+ return Doc :: list ( docs) ;
469+ }
475470 }
476- . concat ( itertools:: intersperse (
477- self . attrs . chunks ( max. into ( ) ) . map ( |chunk| {
471+ _ => {
472+ let attrs_sep = if !self . first_attr_same_line
473+ && !ctx. options . prefer_attrs_single_line
474+ && self . attrs . len ( ) > 1
475+ && !ctx
476+ . options
477+ . max_attrs_per_line
478+ . map ( |value| value. get ( ) > 1 )
479+ . unwrap_or_default ( )
480+ {
481+ Doc :: hard_line ( )
482+ } else {
483+ Doc :: line_or_space ( )
484+ } ;
485+ let attrs = if let Some ( max) = ctx. options . max_attrs_per_line {
486+ // fix #2
487+ if self . attrs . is_empty ( ) {
488+ Doc :: line_or_nil ( )
489+ } else {
490+ Doc :: line_or_space ( )
491+ }
492+ . concat ( itertools:: intersperse (
493+ self . attrs . chunks ( max. into ( ) ) . map ( |chunk| {
494+ Doc :: list (
495+ itertools:: intersperse (
496+ chunk. iter ( ) . map ( |attr| attr. doc ( ctx, & state) ) ,
497+ attrs_sep. clone ( ) ,
498+ )
499+ . collect ( ) ,
500+ )
501+ . group ( )
502+ } ) ,
503+ Doc :: hard_line ( ) ,
504+ ) )
505+ . nest ( ctx. indent_width )
506+ } else {
478507 Doc :: list (
479- itertools:: intersperse (
480- chunk. iter ( ) . map ( |attr| attr. doc ( ctx, & state) ) ,
481- attrs_sep. clone ( ) ,
482- )
483- . collect ( ) ,
508+ self . attrs
509+ . iter ( )
510+ . flat_map ( |attr| [ attrs_sep. clone ( ) , attr. doc ( ctx, & state) ] . into_iter ( ) )
511+ . collect ( ) ,
484512 )
485- . group ( )
486- } ) ,
487- Doc :: hard_line ( ) ,
488- ) )
489- . nest ( ctx. indent_width )
490- } else {
491- Doc :: list (
492- self . attrs
493- . iter ( )
494- . flat_map ( |attr| [ attrs_sep. clone ( ) , attr. doc ( ctx, & state) ] . into_iter ( ) )
495- . collect ( ) ,
496- )
497- . nest ( ctx. indent_width )
498- } ;
513+ . nest ( ctx. indent_width )
514+ } ;
499515
500- if self . void_element {
501- docs. push ( attrs) ;
502- if self_closing {
503- docs. push ( Doc :: line_or_space ( ) ) ;
504- docs. push ( Doc :: text ( "/>" ) ) ;
505- } else {
506- if !ctx. options . closing_bracket_same_line {
507- docs. push ( Doc :: line_or_nil ( ) ) ;
508- }
509- docs. push ( Doc :: text ( ">" ) ) ;
510- }
511- return Doc :: list ( docs) . group ( ) ;
512- }
513- if self_closing && is_empty {
514- docs. push ( attrs) ;
515- docs. push ( Doc :: line_or_space ( ) ) ;
516- docs. push ( Doc :: text ( "/>" ) ) ;
517- return Doc :: list ( docs) . group ( ) ;
518- }
519- if ctx. options . closing_bracket_same_line {
520- docs. push ( attrs. append ( Doc :: text ( ">" ) ) . group ( ) ) ;
521- } else {
522- // for #16
523- if is_whitespace_sensitive
524- && !self . attrs . is_empty ( ) // there're no attributes, so don't insert line break
525- && self
526- . children
527- . first ( )
528- . is_some_and ( |child| {
529- if let NodeKind :: Text ( text_node) = & child. kind {
530- !text_node. raw . starts_with ( |c : char | c. is_ascii_whitespace ( ) )
531- } else {
532- false
533- }
534- } )
535- && self
536- . children
537- . last ( )
538- . is_some_and ( |child| {
539- if let NodeKind :: Text ( text_node) = & child. kind {
540- !text_node. raw . ends_with ( |c : char | c. is_ascii_whitespace ( ) )
541- } else {
542- false
516+ if self . void_element {
517+ docs. push ( attrs) ;
518+ if self_closing {
519+ docs. push ( Doc :: line_or_space ( ) ) ;
520+ docs. push ( Doc :: text ( "/>" ) ) ;
521+ } else {
522+ if !ctx. options . closing_bracket_same_line {
523+ docs. push ( Doc :: line_or_nil ( ) ) ;
543524 }
544- } )
545- {
546- docs. push (
547- attrs
548- . group ( )
549- . append ( Doc :: line_or_nil ( ) )
550- . append ( Doc :: text ( ">" ) ) ,
551- ) ;
552- } else {
553- docs. push (
554- attrs
555- . append ( Doc :: line_or_nil ( ) )
556- . append ( Doc :: text ( ">" ) )
557- . group ( ) ,
558- ) ;
525+ docs. push ( Doc :: text ( ">" ) ) ;
526+ }
527+ return Doc :: list ( docs) . group ( ) ;
528+ }
529+ if self_closing && is_empty {
530+ docs. push ( attrs) ;
531+ docs. push ( Doc :: line_or_space ( ) ) ;
532+ docs. push ( Doc :: text ( "/>" ) ) ;
533+ return Doc :: list ( docs) . group ( ) ;
534+ }
535+ if ctx. options . closing_bracket_same_line {
536+ docs. push ( attrs. append ( Doc :: text ( ">" ) ) . group ( ) ) ;
537+ } else {
538+ // for #16
539+ if is_whitespace_sensitive
540+ && !self . attrs . is_empty ( ) // there're no attributes, so don't insert line break
541+ && self
542+ . children
543+ . first ( )
544+ . is_some_and ( |child| {
545+ if let NodeKind :: Text ( text_node) = & child. kind {
546+ !text_node. raw . starts_with ( |c : char | c. is_ascii_whitespace ( ) )
547+ } else {
548+ false
549+ }
550+ } )
551+ && self
552+ . children
553+ . last ( )
554+ . is_some_and ( |child| {
555+ if let NodeKind :: Text ( text_node) = & child. kind {
556+ !text_node. raw . ends_with ( |c : char | c. is_ascii_whitespace ( ) )
557+ } else {
558+ false
559+ }
560+ } )
561+ {
562+ docs. push (
563+ attrs
564+ . group ( )
565+ . append ( Doc :: line_or_nil ( ) )
566+ . append ( Doc :: text ( ">" ) ) ,
567+ ) ;
568+ } else {
569+ docs. push (
570+ attrs
571+ . append ( Doc :: line_or_nil ( ) )
572+ . append ( Doc :: text ( ">" ) )
573+ . group ( ) ,
574+ ) ;
575+ }
576+ }
559577 }
560578 }
561579
@@ -756,11 +774,7 @@ impl<'s> DocGen<'s> for Element<'s> {
756774
757775 docs. push (
758776 Doc :: text ( "</" )
759- . append ( Doc :: text ( if should_lower_cased {
760- Cow :: from ( self . tag_name . to_ascii_lowercase ( ) )
761- } else {
762- Cow :: from ( self . tag_name )
763- } ) )
777+ . append ( Doc :: text ( formatted_tag_name) )
764778 . append ( Doc :: line_or_nil ( ) )
765779 . append ( Doc :: text ( ">" ) )
766780 . group ( ) ,
@@ -1966,6 +1980,25 @@ fn is_all_ascii_whitespace(s: &str) -> bool {
19661980 !s. is_empty ( ) && s. as_bytes ( ) . iter ( ) . all ( |byte| byte. is_ascii_whitespace ( ) )
19671981}
19681982
1983+ fn is_multi_line_attr ( attr : & Attribute ) -> bool {
1984+ match attr {
1985+ Attribute :: Native ( attr) => attr
1986+ . value
1987+ . map ( |( value, _) | value. trim ( ) . contains ( '\n' ) )
1988+ . unwrap_or ( false ) ,
1989+ Attribute :: VueDirective ( attr) => attr
1990+ . value
1991+ . map ( |( value, _) | value. contains ( '\n' ) )
1992+ . unwrap_or ( false ) ,
1993+ Attribute :: Astro ( attr) => attr. expr . 0 . contains ( '\n' ) ,
1994+ Attribute :: Svelte ( attr) => attr. expr . 0 . contains ( '\n' ) ,
1995+ Attribute :: JinjaComment ( comment) => comment. raw . contains ( '\n' ) ,
1996+ Attribute :: JinjaTag ( tag) => tag. content . contains ( '\n' ) ,
1997+ // Templating blocks usually span across multiple lines so let's just assume true.
1998+ Attribute :: JinjaBlock ( ..) | Attribute :: VentoTagOrBlock ( ..) => true ,
1999+ }
2000+ }
2001+
19692002fn should_ignore_node < ' s , E , F > ( index : usize , nodes : & [ Node ] , ctx : & Ctx < ' s , E , F > ) -> bool
19702003where
19712004 F : for < ' a > FnMut ( & ' a str , Hints ) -> Result < Cow < ' a , str > , E > ,
0 commit comments