@@ -93,12 +93,12 @@ impl<'a> Printer<'a> {
9393 }
9494 }
9595
96- FormatElement :: Token { text } => self . print_text ( text) ,
96+ FormatElement :: Token { text } => self . print_text ( Text :: Token ( text) ) ,
9797 FormatElement :: Text { text } => {
98- self . print_text ( text) ;
98+ self . print_text ( Text :: Text ( text) ) ;
9999 }
100100 FormatElement :: LocatedTokenText { slice, source_position } => {
101- self . print_text ( slice) ;
101+ self . print_text ( Text :: Text ( slice) ) ;
102102 }
103103 FormatElement :: Line ( line_mode) => {
104104 if args. mode ( ) . is_flat ( ) {
@@ -122,12 +122,13 @@ impl<'a> Printer<'a> {
122122
123123 // Only print a newline if the current line isn't already empty
124124 if self . state . line_width > 0 {
125- self . print_str ( "\n " ) ;
125+ self . print_char ( '\n' ) ;
126+ self . state . has_empty_line = false ;
126127 }
127128
128129 // Print a second line break if this is an empty line
129130 if line_mode == & LineMode :: Empty && !self . state . has_empty_line {
130- self . print_str ( " \n " ) ;
131+ self . print_char ( '\n' ) ;
131132 self . state . has_empty_line = true ;
132133 }
133134
@@ -300,38 +301,6 @@ impl<'a> Printer<'a> {
300301 result
301302 }
302303
303- fn print_text ( & mut self , text : & str ) {
304- if !self . state . pending_indent . is_empty ( ) {
305- let ( indent_char, repeat_count) = match self . options . indent_style ( ) {
306- IndentStyle :: Tab => ( '\t' , 1 ) ,
307- IndentStyle :: Space => ( ' ' , self . options . indent_width ( ) . value ( ) ) ,
308- } ;
309-
310- let indent = std:: mem:: take ( & mut self . state . pending_indent ) ;
311- let total_indent_char_count = indent. level ( ) as usize * repeat_count as usize ;
312-
313- self . state
314- . buffer
315- . reserve ( total_indent_char_count + indent. align ( ) as usize + text. len ( ) ) ;
316-
317- for _ in 0 ..total_indent_char_count {
318- self . print_char ( indent_char) ;
319- }
320-
321- for _ in 0 ..indent. align ( ) {
322- self . print_char ( ' ' ) ;
323- }
324- }
325-
326- // Print pending spaces
327- if self . state . pending_space {
328- self . print_str ( " " ) ;
329- self . state . pending_space = false ;
330- }
331-
332- self . print_str ( text) ;
333- }
334-
335304 fn flush_line_suffixes (
336305 & mut self ,
337306 queue : & mut PrintQueue < ' a > ,
@@ -633,12 +602,49 @@ impl<'a> Printer<'a> {
633602 invalid_end_tag ( TagKind :: Entry , stack. top_kind ( ) )
634603 }
635604
636- fn print_str ( & mut self , content : & str ) {
637- for char in content. chars ( ) {
638- self . print_char ( char) ;
605+ fn print_text ( & mut self , text : Text ) {
606+ if !self . state . pending_indent . is_empty ( ) {
607+ let ( indent_char, repeat_count) = match self . options . indent_style ( ) {
608+ IndentStyle :: Tab => ( '\t' , 1 ) ,
609+ IndentStyle :: Space => ( ' ' , self . options . indent_width ( ) . value ( ) ) ,
610+ } ;
611+
612+ let indent = std:: mem:: take ( & mut self . state . pending_indent ) ;
613+ let total_indent_char_count = indent. level ( ) as usize * repeat_count as usize ;
614+
615+ self . state
616+ . buffer
617+ . reserve ( total_indent_char_count + indent. align ( ) as usize + text. len ( ) ) ;
618+
619+ for _ in 0 ..total_indent_char_count {
620+ self . print_char ( indent_char) ;
621+ }
639622
640- self . state . has_empty_line = false ;
623+ for _ in 0 ..indent. align ( ) {
624+ self . print_char ( ' ' ) ;
625+ }
626+ }
627+
628+ // Print pending spaces
629+ if self . state . pending_space {
630+ self . state . buffer . push ( ' ' ) ;
631+ self . state . pending_space = false ;
632+ self . state . line_width += 1 ;
633+ }
634+
635+ match text {
636+ Text :: Token ( text) => {
637+ self . state . buffer . push_str ( text) ;
638+ self . state . line_width += text. len ( ) ;
639+ }
640+ Text :: Text ( text) => {
641+ for char in text. chars ( ) {
642+ self . print_char ( char) ;
643+ }
644+ }
641645 }
646+
647+ self . state . has_empty_line = false ;
642648 }
643649
644650 fn print_char ( & mut self , char : char ) {
@@ -1015,10 +1021,15 @@ impl<'a, 'print> FitsMeasurer<'a, 'print> {
10151021 }
10161022 }
10171023
1018- FormatElement :: Token { text } | FormatElement :: Text { text, .. } => {
1019- return Ok ( self . fits_text ( text) ) ;
1024+ FormatElement :: Token { text } => {
1025+ return Ok ( self . fits_text ( Text :: Token ( text) ) ) ;
1026+ }
1027+ FormatElement :: Text { text, .. } => {
1028+ return Ok ( self . fits_text ( Text :: Text ( text) ) ) ;
1029+ }
1030+ FormatElement :: LocatedTokenText { slice, .. } => {
1031+ return Ok ( self . fits_text ( Text :: Text ( slice) ) ) ;
10201032 }
1021- FormatElement :: LocatedTokenText { slice, .. } => return Ok ( self . fits_text ( slice) ) ,
10221033
10231034 FormatElement :: LineSuffixBoundary => {
10241035 if self . state . has_line_suffix {
@@ -1157,7 +1168,7 @@ impl<'a, 'print> FitsMeasurer<'a, 'print> {
11571168 Ok ( Fits :: Maybe )
11581169 }
11591170
1160- fn fits_text ( & mut self , text : & str ) -> Fits {
1171+ fn fits_text ( & mut self , text : Text ) -> Fits {
11611172 let indent = std:: mem:: take ( & mut self . state . pending_indent ) ;
11621173 self . state . line_width += indent. level ( ) as usize
11631174 * self . options ( ) . indent_width ( ) . value ( ) as usize
@@ -1167,21 +1178,28 @@ impl<'a, 'print> FitsMeasurer<'a, 'print> {
11671178 self . state . line_width += 1 ;
11681179 }
11691180
1170- for c in text. chars ( ) {
1171- let char_width = match c {
1172- '\t' => self . options ( ) . indent_width . value ( ) as usize ,
1173- '\n' => {
1174- return if self . must_be_flat
1175- || self . state . line_width > usize:: from ( self . options ( ) . print_width )
1176- {
1177- Fits :: No
1178- } else {
1179- Fits :: Yes
1181+ match text {
1182+ Text :: Token ( text) => {
1183+ self . state . line_width += text. len ( ) ;
1184+ }
1185+ Text :: Text ( text) => {
1186+ for c in text. chars ( ) {
1187+ let char_width = match c {
1188+ '\t' => self . options ( ) . indent_width . value ( ) as usize ,
1189+ '\n' => {
1190+ return if self . must_be_flat
1191+ || self . state . line_width > usize:: from ( self . options ( ) . print_width )
1192+ {
1193+ Fits :: No
1194+ } else {
1195+ Fits :: Yes
1196+ } ;
1197+ }
1198+ c => c. width ( ) . unwrap_or ( 0 ) ,
11801199 } ;
1200+ self . state . line_width += char_width;
11811201 }
1182- c => c. width ( ) . unwrap_or ( 0 ) ,
1183- } ;
1184- self . state . line_width += char_width;
1202+ }
11851203 }
11861204
11871205 if self . state . line_width > usize:: from ( self . options ( ) . print_width ) {
@@ -1271,6 +1289,22 @@ struct FitsState {
12711289 line_width : usize ,
12721290}
12731291
1292+ #[ derive( Copy , Clone , Debug ) ]
1293+ enum Text < ' a > {
1294+ /// ASCII only text that contains no line breaks or tab characters.
1295+ Token ( & ' a str ) ,
1296+ /// Arbitrary text. May contain `\n` line breaks, tab characters, or unicode characters.
1297+ Text ( & ' a str ) ,
1298+ }
1299+
1300+ impl Text < ' _ > {
1301+ fn len ( & self ) -> usize {
1302+ match self {
1303+ Text :: Token ( text) | Text :: Text ( text) => text. len ( ) ,
1304+ }
1305+ }
1306+ }
1307+
12741308#[ cfg( test) ]
12751309mod tests {
12761310 use oxc_allocator:: Allocator ;
0 commit comments