11use crate :: html:: lists:: element_list:: { FormatHtmlElementListOptions , HtmlChildListLayout } ;
2- use crate :: utils:: css_display:: { CssDisplay , get_css_display , get_css_display_from_tag} ;
2+ use crate :: utils:: css_display:: { CssDisplay , get_css_display_from_tag} ;
33use crate :: utils:: metadata:: { get_css_whitespace, get_element_css_display} ;
44use crate :: verbatim:: { format_html_leading_comments, format_html_leading_comments_for_block} ;
55use crate :: { html:: lists:: element_list:: FormatHtmlElementList , prelude:: * } ;
66use biome_formatter:: { CstFormatContext , FormatRefWithRule , FormatRuleWithOptions , write} ;
77use biome_html_syntax:: {
88 AnyHtmlContent , AnyHtmlElement , AnyHtmlTagName , HtmlElement , HtmlElementFields ,
99 HtmlElementList , HtmlRoot , HtmlSelfClosingElement ,
10- HtmlSyntaxKind :: { self , AUDIO_KW , OBJECT_KW , TEMPLATE_KW , VIDEO_KW } ,
10+ HtmlSyntaxKind :: {
11+ self , AUDIO_KW , BODY_KW , HEAD_KW , HTML_KW , OBJECT_KW , OL_KW , SCRIPT_KW , SELECT_KW ,
12+ STYLE_KW , TEMPLATE_KW , UL_KW , VIDEO_KW ,
13+ } ,
1114 HtmlSyntaxToken ,
1215} ;
1316use biome_parser:: { TokenSet , token_set} ;
14- use biome_string_case:: StrLikeExtension ;
1517
1618use super :: {
1719 closing_element:: { FormatHtmlClosingElement , FormatHtmlClosingElementOptions } ,
@@ -25,15 +27,17 @@ use super::{
2527/// browser user-agent CSS.
2628///
2729/// See also: <https://developer.mozilla.org/en-US/docs/Web/CSS/Reference/Properties/white-space>
28- fn is_verbatim_tag ( tag_name : & str ) -> bool {
29- tag_name. eq_ignore_ascii_case ( "script" )
30- || tag_name. eq_ignore_ascii_case ( "style" )
30+ fn is_verbatim_tag ( tag_name : & AnyHtmlTagName ) -> bool {
31+ matches ! ( tag_name. tag_name_kind( ) , Some ( SCRIPT_KW | STYLE_KW ) )
3132 || get_css_whitespace ( tag_name) . preserves_content ( )
3233}
3334
3435const STRUCTURAL_FALLBACK_ELEMENTS : TokenSet < HtmlSyntaxKind > =
3536 token_set ! ( AUDIO_KW , OBJECT_KW , VIDEO_KW ) ;
3637
38+ const FORCE_BREAK_CHILDREN_ELEMENTS : TokenSet < HtmlSyntaxKind > =
39+ token_set ! ( HTML_KW , HEAD_KW , UL_KW , OL_KW , SELECT_KW ) ;
40+
3741#[ derive( Debug , Clone , Default ) ]
3842pub ( crate ) struct FormatHtmlElement {
3943 /// Whether the closing `>` should be borrowed by an adjacent sibling element.
@@ -94,8 +98,9 @@ impl FormatNodeRule<HtmlElement> for FormatHtmlElement {
9498 // Instead of:
9599 // <!-- comment --> <div>...</div>
96100 let css_display = node
97- . tag_name ( )
98- . map_or ( CssDisplay :: Block , |tag| get_css_display ( & tag) ) ;
101+ . name ( )
102+ . as_ref ( )
103+ . map_or ( CssDisplay :: Block , get_css_display_from_tag) ;
99104
100105 if css_display. is_block_like ( ) {
101106 format_html_leading_comments_for_block ( node. syntax ( ) ) . fmt ( f)
@@ -142,12 +147,12 @@ impl FormatHtmlElement {
142147 closing_element,
143148 } = node. as_fields ( ) ;
144149
145- let should_force_break_content = should_force_break_content ( node) ;
146-
147150 let closing_element = closing_element?;
148151 let opening_element = opening_element?;
149152 let tag_name = opening_element. name ( ) ?;
150153 let css_display = get_css_display_from_tag ( & tag_name) ;
154+ let should_force_break_content =
155+ should_force_break_content ( node, tag_name. tag_name_kind ( ) , css_display) ;
151156 let is_element_internally_whitespace_sensitive =
152157 css_display. is_internally_whitespace_sensitive ( f) ;
153158 let is_root_element_list = node
@@ -179,14 +184,7 @@ impl FormatHtmlElement {
179184 AnyHtmlElement :: AnyHtmlContent ( AnyHtmlContent :: HtmlEmbeddedContent ( _) )
180185 )
181186 } ) ;
182- let should_be_verbatim = has_embedded_content
183- || match tag_name {
184- AnyHtmlTagName :: HtmlComponentName ( _) | AnyHtmlTagName :: HtmlMemberName ( _) => false ,
185- AnyHtmlTagName :: HtmlTagName ( tag_name) => tag_name
186- . value_token ( )
187- . as_ref ( )
188- . is_ok_and ( |tag_name_token| is_verbatim_tag ( tag_name_token. text_trimmed ( ) ) ) ,
189- } ;
187+ let should_be_verbatim = has_embedded_content || is_verbatim_tag ( & tag_name) ;
190188
191189 let should_format_embedded_nodes = if f. context ( ) . should_delegate_fmt_embedded_nodes ( ) {
192190 // Only delegate for supported <script> or <style> content
@@ -345,33 +343,33 @@ impl FormatHtmlElement {
345343/// This is equivalent to Prettier's `function forceBreakChildren()`.
346344///
347345/// Prettier source: src/language-html/utilities/index.js:271-278
348- fn should_force_break_children ( tag_name : & str ) -> bool {
349- let tag_lower = tag_name . to_ascii_lowercase_cow ( ) ;
350-
351- // These elements always break children
352- if matches ! ( tag_lower . as_ref ( ) , "html" | "head" | "ul" | "ol" | "select" ) {
346+ fn should_force_break_children (
347+ tag_name_kind : Option < HtmlSyntaxKind > ,
348+ css_display : CssDisplay ,
349+ ) -> bool {
350+ if tag_name_kind . is_some_and ( |kind| FORCE_BREAK_CHILDREN_ELEMENTS . contains ( kind ) ) {
353351 return true ;
354352 }
355353
356354 // Table-related elements (except table-cell) break children
357- let display = get_css_display ( & tag_lower) ;
358- display. is_table_like ( ) && !matches ! ( display, CssDisplay :: TableCell )
355+ css_display. is_table_like ( ) && !matches ! ( css_display, CssDisplay :: TableCell )
359356}
360357
361358/// Determines if the content of an element should be forcefully broken into multiple lines.
362359///
363360/// This is equivalent to Prettier's `function forceBreakContent()`.
364- fn should_force_break_content ( node : & HtmlElement ) -> bool {
365- let Some ( tag_name) = node. tag_name ( ) else {
366- return false ;
367- } ;
368- if should_force_break_children ( & tag_name) {
361+ fn should_force_break_content (
362+ node : & HtmlElement ,
363+ tag_name_kind : Option < HtmlSyntaxKind > ,
364+ css_display : CssDisplay ,
365+ ) -> bool {
366+ if should_force_break_children ( tag_name_kind, css_display) {
369367 return true ;
370368 }
371369
372370 // prettier also considers `<script>` and `<style>` here, but we handle those elsewhere.
373371 // if its a `<body>` or if the grandchildren contain non-text nodes
374- if !node. children ( ) . is_empty ( ) && tag_name . eq_ignore_ascii_case ( "body" )
372+ if !node. children ( ) . is_empty ( ) && tag_name_kind == Some ( BODY_KW )
375373 || node. children ( ) . iter ( ) . any ( |child| {
376374 if let Some ( element) = child. as_html_element ( ) {
377375 has_non_text_child ( & element. children ( ) )
0 commit comments