refactor(print): introduce `UnicodeWidthGraphemes` trait (#2670)

This commit is contained in:
Dario Vladović 2021-05-02 09:55:26 +02:00 committed by GitHub
parent cb681cd0ae
commit ee65297183
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 42 additions and 22 deletions

View File

@ -16,6 +16,43 @@ use crate::module::ALL_MODULES;
use crate::modules; use crate::modules;
use crate::segment::Segment; use crate::segment::Segment;
pub struct Grapheme<'a>(&'a str);
impl<'a> Grapheme<'a> {
pub fn width(&self) -> usize {
self.0
.chars()
.filter_map(UnicodeWidthChar::width)
.max()
.unwrap_or(0)
}
}
pub trait UnicodeWidthGraphemes {
fn width_graphemes(&self) -> usize;
}
impl<T> UnicodeWidthGraphemes for T
where
T: AsRef<str>,
{
fn width_graphemes(&self) -> usize {
self.as_ref()
.graphemes(true)
.map(Grapheme)
.map(|g| g.width())
.sum()
}
}
#[test]
fn test_grapheme_aware_width() {
// UnicodeWidthStr::width would return 8
assert_eq!(2, "👩‍👩‍👦‍👦".width_graphemes());
assert_eq!(1, "".width_graphemes());
assert_eq!(11, "normal text".width_graphemes());
}
pub fn prompt(args: ArgMatches) { pub fn prompt(args: ArgMatches) {
let context = Context::new(args); let context = Context::new(args);
let stdout = io::stdout(); let stdout = io::stdout();
@ -123,12 +160,12 @@ pub fn timings(args: ArgMatches) {
.filter(|module| !module.is_empty() || module.duration.as_millis() > 0) .filter(|module| !module.is_empty() || module.duration.as_millis() > 0)
.map(|module| ModuleTiming { .map(|module| ModuleTiming {
name: String::from(module.get_name().as_str()), name: String::from(module.get_name().as_str()),
name_len: better_width(module.get_name().as_str()), name_len: module.get_name().width_graphemes(),
value: ansi_term::ANSIStrings(&module.ansi_strings()) value: ansi_term::ANSIStrings(&module.ansi_strings())
.to_string() .to_string()
.replace('\n', "\\n"), .replace('\n', "\\n"),
duration: module.duration, duration: module.duration,
duration_len: better_width(format_duration(&module.duration).as_str()), duration_len: format_duration(&module.duration).width_graphemes(),
}) })
.collect::<Vec<ModuleTiming>>(); .collect::<Vec<ModuleTiming>>();
@ -174,8 +211,8 @@ pub fn explain(args: ArgMatches) {
let value = module.get_segments().join(""); let value = module.get_segments().join("");
ModuleInfo { ModuleInfo {
value: ansi_term::ANSIStrings(&module.ansi_strings()).to_string(), value: ansi_term::ANSIStrings(&module.ansi_strings()).to_string(),
value_len: better_width(value.as_str()) value_len: value.width_graphemes()
+ better_width(format_duration(&module.duration).as_str()), + format_duration(&module.duration).width_graphemes(),
desc: module.get_description().to_owned(), desc: module.get_description().to_owned(),
duration: format_duration(&module.duration), duration: format_duration(&module.duration),
} }
@ -218,7 +255,7 @@ pub fn explain(args: ArgMatches) {
} }
// Handle normal wrapping // Handle normal wrapping
current_pos += grapheme_width(g); current_pos += Grapheme(g).width();
// Wrap when hitting max width or newline // Wrap when hitting max width or newline
if g == "\n" || current_pos > desc_width { if g == "\n" || current_pos > desc_width {
// trim spaces on linebreak // trim spaces on linebreak
@ -358,10 +395,6 @@ fn should_add_implicit_custom_module(
.unwrap_or(false) .unwrap_or(false)
} }
fn better_width(s: &str) -> usize {
s.graphemes(true).map(grapheme_width).sum()
}
pub fn format_duration(duration: &Duration) -> String { pub fn format_duration(duration: &Duration) -> String {
let milis = duration.as_millis(); let milis = duration.as_millis();
if milis == 0 { if milis == 0 {
@ -370,16 +403,3 @@ pub fn format_duration(duration: &Duration) -> String {
format!("{:?}ms", &milis) format!("{:?}ms", &milis)
} }
} }
// Assume that graphemes have width of the first character in the grapheme
fn grapheme_width(g: &str) -> usize {
g.chars().next().and_then(|i| i.width()).unwrap_or(0)
}
#[test]
fn test_grapheme_aware_better_width() {
// UnicodeWidthStr::width would return 8
assert_eq!(2, better_width("👩‍👩‍👦‍👦"));
assert_eq!(1, better_width(""));
assert_eq!(11, better_width("normal text"));
}