@ -1,7 +1,7 @@
//! Apply colours and styling to strings.
//! Apply colours and styling to strings.
use crate ::{
use crate ::{
lines ::{ codepoint_len, LineNumber } ,
lines ::{ byte_len, codepoint_len, LineNumber } ,
positions ::SingleLineSpan ,
positions ::SingleLineSpan ,
syntax ::{ AtomKind , MatchKind , MatchedPos , TokenKind } ,
syntax ::{ AtomKind , MatchKind , MatchedPos , TokenKind } ,
} ;
} ;
@ -44,13 +44,17 @@ fn substring_by_codepoint(s: &str, start: usize, end: usize) -> &str {
}
}
}
}
fn substring_by_byte ( s : & str , start : usize , end : usize ) -> & str {
& s [ start .. end ]
}
/// Split a string into equal length parts, padding the last part if
/// Split a string into equal length parts, padding the last part if
/// necessary.
/// necessary.
///
///
/// ```
/// ```
/// split_string ("fooba", 3) // vec!["foo", "ba "]
/// split_string _by_codepoint ("fooba", 3) // vec!["foo", "ba "]
/// ```
/// ```
fn split_string ( s : & str , max_len : usize ) -> Vec < String > {
fn split_string _by_codepoint ( s : & str , max_len : usize ) -> Vec < String > {
let mut res = vec! [ ] ;
let mut res = vec! [ ] ;
let mut s = s ;
let mut s = s ;
@ -81,7 +85,7 @@ pub fn split_and_apply(
) -> Vec < String > {
) -> Vec < String > {
if styles . is_empty ( ) & & ! line . is_empty ( ) {
if styles . is_empty ( ) & & ! line . is_empty ( ) {
// Missing styles is a bug, so highlight in purple to make this obvious.
// Missing styles is a bug, so highlight in purple to make this obvious.
return split_string ( line , max_len )
return split_string _by_codepoint ( line , max_len )
. into_iter ( )
. into_iter ( )
. map ( | part | {
. map ( | part | {
if use_color {
if use_color {
@ -96,12 +100,12 @@ pub fn split_and_apply(
let mut styled_parts = vec! [ ] ;
let mut styled_parts = vec! [ ] ;
let mut part_start = 0 ;
let mut part_start = 0 ;
for part in split_string ( line , max_len ) {
for part in split_string _by_codepoint ( line , max_len ) {
let mut res = String ::with_capacity ( part . len ( ) ) ;
let mut res = String ::with_capacity ( part . len ( ) ) ;
let mut prev_style_end = 0 ;
let mut prev_style_end = 0 ;
for ( span , style ) in styles {
for ( span , style ) in styles {
// The remaining spans are beyond the end of this part.
// The remaining spans are beyond the end of this part.
if span . start_col > = part_start + codepoint _len( & part ) {
if span . start_col > = part_start + byte _len( & part ) {
break ;
break ;
}
}
@ -109,7 +113,7 @@ pub fn split_and_apply(
if span . start_col > part_start & & prev_style_end < span . start_col {
if span . start_col > part_start & & prev_style_end < span . start_col {
// Then append that text without styling.
// Then append that text without styling.
let unstyled_start = max ( prev_style_end , part_start ) ;
let unstyled_start = max ( prev_style_end , part_start ) ;
res . push_str ( substring_by_ codepoint (
res . push_str ( substring_by_ byte (
& part ,
& part ,
unstyled_start - part_start ,
unstyled_start - part_start ,
span . start_col - part_start ,
span . start_col - part_start ,
@ -118,10 +122,10 @@ pub fn split_and_apply(
// Apply style to the substring in this span.
// Apply style to the substring in this span.
if span . end_col > part_start {
if span . end_col > part_start {
let span_s = substring_by_ codepoint (
let span_s = substring_by_ byte (
& part ,
& part ,
max ( 0 , span . start_col as isize - part_start as isize ) as usize ,
max ( 0 , span . start_col as isize - part_start as isize ) as usize ,
min ( codepoint _len( & part ) , span . end_col - part_start ) ,
min ( byte _len( & part ) , span . end_col - part_start ) ,
) ;
) ;
res . push_str ( & span_s . style ( * style ) . to_string ( ) ) ;
res . push_str ( & span_s . style ( * style ) . to_string ( ) ) ;
}
}
@ -136,13 +140,12 @@ pub fn split_and_apply(
// Unstyled text after the last span.
// Unstyled text after the last span.
if prev_style_end < part_start + codepoint_len ( & part ) {
if prev_style_end < part_start + codepoint_len ( & part ) {
let span_s =
let span_s = substring_by_byte ( & part , prev_style_end - part_start , byte_len ( & part ) ) ;
substring_by_codepoint ( & part , prev_style_end - part_start , codepoint_len ( & part ) ) ;
res . push_str ( span_s ) ;
res . push_str ( span_s ) ;
}
}
styled_parts . push ( res ) ;
styled_parts . push ( res ) ;
part_start + = codepoint _len( & part )
part_start + = byte _len( & part )
}
}
styled_parts
styled_parts
@ -155,31 +158,30 @@ fn apply_line(line: &str, styles: &[(SingleLineSpan, Style)]) -> String {
return highlight_missing_style_bug ( line ) ;
return highlight_missing_style_bug ( line ) ;
}
}
let line_ codepoints = codepoint _len( line ) ;
let line_ bytes = byte _len( line ) ;
let mut res = String ::with_capacity ( line . len ( ) ) ;
let mut res = String ::with_capacity ( line . len ( ) ) ;
let mut i = 0 ;
let mut i = 0 ;
for ( span , style ) in styles {
for ( span , style ) in styles {
// The remaining spans are beyond the end of this line. This
// The remaining spans are beyond the end of this line. This
// occurs when we truncate the line to fit on the display.
// occurs when we truncate the line to fit on the display.
if span . start_col > = line_ codepoint s {
if span . start_col > = line_ byte s {
break ;
break ;
}
}
// Unstyled text before the next span.
// Unstyled text before the next span.
if i < span . start_col {
if i < span . start_col {
res . push_str ( substring_by_ codepoint ( line , i , span . start_col ) ) ;
res . push_str ( substring_by_ byte ( line , i , span . start_col ) ) ;
}
}
// Apply style to the substring in this span.
// Apply style to the substring in this span.
let span_s =
let span_s = substring_by_byte ( line , span . start_col , min ( line_bytes , span . end_col ) ) ;
substring_by_codepoint ( line , span . start_col , min ( line_codepoints , span . end_col ) ) ;
res . push_str ( & span_s . style ( * style ) . to_string ( ) ) ;
res . push_str ( & span_s . style ( * style ) . to_string ( ) ) ;
i = span . end_col ;
i = span . end_col ;
}
}
// Unstyled text after the last span.
// Unstyled text after the last span.
if i < line_ codepoint s {
if i < line_ byte s {
let span_s = substring_by_ codepoint( line , i , line_codepoint s) ;
let span_s = substring_by_ byte( line , i , line_byte s) ;
res . push_str ( span_s ) ;
res . push_str ( span_s ) ;
}
}
res
res
@ -349,12 +351,12 @@ mod tests {
#[ test ]
#[ test ]
fn split_string_simple ( ) {
fn split_string_simple ( ) {
assert_eq! ( split_string ( " fooba " , 3 ) , vec! [ " foo " , " ba " ] ) ;
assert_eq! ( split_string _by_codepoint ( " fooba " , 3 ) , vec! [ " foo " , " ba " ] ) ;
}
}
#[ test ]
#[ test ]
fn split_string_unicode ( ) {
fn split_string_unicode ( ) {
assert_eq! ( split_string ( " ab📦def " , 3 ) , vec! [ " ab📦 " , " def " ] ) ;
assert_eq! ( split_string _by_codepoint ( " ab📦def " , 3 ) , vec! [ " ab📦 " , " def " ] ) ;
}
}
#[ test ]
#[ test ]