1
0
mirror of https://github.com/Llewellynvdm/starship.git synced 2024-11-05 04:47:58 +00:00

fix(explain): align table correctly (#1482)

* fix(explain): align table correctly

* iterate over lines directly

* calculate desc_width with the actual space available

* custom unicode-aware textwrapping

* fix clippy error

* better width estimination

* explain +6

* move padding width into a constant
This commit is contained in:
David Knaack 2020-08-05 19:16:59 +02:00 committed by GitHub
parent 0be9ffc0a1
commit 8b0f589486
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 62 additions and 37 deletions

12
Cargo.lock generated
View File

@ -195,7 +195,7 @@ dependencies = [
"atty", "atty",
"bitflags", "bitflags",
"strsim", "strsim",
"textwrap 0.11.0", "textwrap",
"unicode-width", "unicode-width",
"vec_map", "vec_map",
] ]
@ -1110,7 +1110,6 @@ dependencies = [
"sysinfo", "sysinfo",
"tempfile", "tempfile",
"term_size", "term_size",
"textwrap 0.12.1",
"toml", "toml",
"unicode-segmentation", "unicode-segmentation",
"unicode-width", "unicode-width",
@ -1202,15 +1201,6 @@ dependencies = [
"unicode-width", "unicode-width",
] ]
[[package]]
name = "textwrap"
version = "0.12.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "203008d98caf094106cfaba70acfed15e18ed3ddb7d94e49baec153a2b462789"
dependencies = [
"unicode-width",
]
[[package]] [[package]]
name = "thread_local" name = "thread_local"
version = "1.0.1" version = "1.0.1"

View File

@ -56,7 +56,6 @@ os_info = "2.0.7"
urlencoding = "1.1.1" urlencoding = "1.1.1"
open = "1.4.0" open = "1.4.0"
unicode-width = "0.1.8" unicode-width = "0.1.8"
textwrap = "0.12.1"
term_size = "0.3.2" term_size = "0.3.2"
quick-xml = "0.18.1" quick-xml = "0.18.1"

View File

@ -4,6 +4,7 @@ use rayon::prelude::*;
use std::collections::BTreeSet; use std::collections::BTreeSet;
use std::fmt::{self, Debug, Write as FmtWrite}; use std::fmt::{self, Debug, Write as FmtWrite};
use std::io::{self, Write}; use std::io::{self, Write};
use unicode_segmentation::UnicodeSegmentation;
use unicode_width::UnicodeWidthChar; use unicode_width::UnicodeWidthChar;
use crate::configs::PROMPT_ORDER; use crate::configs::PROMPT_ORDER;
@ -104,50 +105,72 @@ 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: value.chars().count() + count_wide_chars(&value), value_len: better_width(value.as_str()),
desc: module.get_description().to_owned(), desc: module.get_description().to_owned(),
} }
}) })
.collect::<Vec<ModuleInfo>>(); .collect::<Vec<ModuleInfo>>();
let mut max_ansi_module_width = 0; let max_module_width = modules.iter().map(|i| i.value_len).max().unwrap_or(0);
let mut max_module_width = 0;
for info in &modules { // In addition to the module width itself there are also 6 padding characters in each line.
max_ansi_module_width = std::cmp::max( // Overall a line looks like this: " {module name} - {description}".
max_ansi_module_width, const PADDING_WIDTH: usize = 6;
info.value.chars().count() + count_wide_chars(&info.value),
);
max_module_width = std::cmp::max(max_module_width, info.value_len);
}
let desc_width = term_size::dimensions() let desc_width = term_size::dimensions()
.map(|(w, _)| w) .map(|(w, _)| w)
.map(|width| width - std::cmp::min(width, max_ansi_module_width)); // Add padding length to module length to avoid text overflow. This line also assures desc_width >= 0.
.map(|width| width - std::cmp::min(width, max_module_width + PADDING_WIDTH));
println!("\n Here's a breakdown of your prompt:"); println!("\n Here's a breakdown of your prompt:");
for info in modules { for info in modules {
let wide_chars = count_wide_chars(&info.value);
if let Some(desc_width) = desc_width { if let Some(desc_width) = desc_width {
let wrapped = textwrap::fill(&info.desc, desc_width); // Custom Textwrapping!
let mut lines = wrapped.split('\n'); let mut current_pos = 0;
println!( let mut escaping = false;
" {:width$} - {}", // Print info
print!(
" {}{} - ",
info.value, info.value,
lines.next().unwrap(), " ".repeat(max_module_width - info.value_len)
width = max_ansi_module_width - wide_chars
); );
for g in info.desc.graphemes(true) {
for line in lines { // Handle ANSI escape sequnces
println!("{}{}", " ".repeat(max_module_width + 6), line.trim()); if g == "\x1B" {
escaping = true;
} }
if escaping {
print!("{}", g);
escaping = !("a" <= g && "z" >= g || "A" <= g && "Z" >= g);
continue;
}
// Handle normal wrapping
current_pos += grapheme_width(g);
// Wrap when hitting max width or newline
if g == "\n" || current_pos > desc_width {
// trim spaces on linebreak
if g == " " && desc_width > 1 {
continue;
}
print!("\n{}", " ".repeat(max_module_width + PADDING_WIDTH));
if g == "\n" {
current_pos = 0;
continue;
}
current_pos = 1;
}
print!("{}", g);
}
println!();
} else { } else {
println!( println!(
" {:width$} - {}", " {}{} - {}",
info.value, info.value,
" ".repeat(max_module_width - info.value_len),
info.desc, info.desc,
width = max_ansi_module_width - wide_chars
); );
}; };
} }
@ -266,6 +289,19 @@ fn should_add_implicit_custom_module(
.unwrap_or(false) .unwrap_or(false)
} }
fn count_wide_chars(value: &str) -> usize { fn better_width(s: &str) -> usize {
value.chars().filter(|c| c.width().unwrap_or(0) > 1).count() s.graphemes(true).map(grapheme_width).sum()
}
// 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"));
} }