enable ls_colors for the ls command (#340)

* enable ls_colors for the `ls` command

* added wrapping with ansi-cut so the ansi sequences don't bleed over

* clippy
This commit is contained in:
Darren Schroeder
2021-11-15 14:09:17 -06:00
committed by GitHub
parent 42367ddf6d
commit ab22619f4a
6 changed files with 210 additions and 47 deletions

View File

@ -611,15 +611,15 @@ impl Table {
}
#[derive(Debug)]
pub struct ProcessedTable<'a> {
pub headers: Vec<ProcessedCell<'a>>,
pub data: Vec<Vec<ProcessedCell<'a>>>,
pub struct ProcessedTable {
pub headers: Vec<ProcessedCell>,
pub data: Vec<Vec<ProcessedCell>>,
pub theme: Theme,
}
#[derive(Debug)]
pub struct ProcessedCell<'a> {
pub contents: Vec<Vec<Subline<'a>>>,
pub struct ProcessedCell {
pub contents: Vec<Vec<Subline>>,
pub style: TextStyle,
}
@ -995,7 +995,7 @@ pub fn maybe_truncate_columns(termwidth: usize, processed_table: &mut ProcessedT
processed_table.headers.push(ProcessedCell {
contents: vec![vec![Subline {
subline: "...",
subline: "...".to_string(),
width: 3,
}]],
style: TextStyle::basic_center(),
@ -1004,7 +1004,7 @@ pub fn maybe_truncate_columns(termwidth: usize, processed_table: &mut ProcessedT
for entry in processed_table.data.iter_mut() {
entry.push(ProcessedCell {
contents: vec![vec![Subline {
subline: "...",
subline: "...".to_string(),
width: 3,
}]],
style: TextStyle::basic_center(),

View File

@ -1,8 +1,9 @@
use crate::table::TextStyle;
use ansi_cut::AnsiCut;
use nu_ansi_term::Style;
use std::collections::HashMap;
use std::{fmt::Display, iter::Iterator};
use unicode_width::UnicodeWidthStr;
use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};
#[derive(Debug, Clone, Copy)]
pub enum Alignment {
@ -12,14 +13,14 @@ pub enum Alignment {
}
#[derive(Debug)]
pub struct Subline<'a> {
pub subline: &'a str,
pub struct Subline {
pub subline: String,
pub width: usize,
}
#[derive(Debug)]
pub struct Line<'a> {
pub sublines: Vec<Subline<'a>>,
pub struct Line {
pub sublines: Vec<Subline>,
pub width: usize,
}
@ -37,7 +38,7 @@ pub struct WrappedCell {
pub style: TextStyle,
}
impl<'a> Display for Line<'a> {
impl Display for Line {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut first = true;
for subline in &self.sublines {
@ -52,21 +53,42 @@ impl<'a> Display for Line<'a> {
}
}
fn strip_ansi(astring: &str) -> String {
if let Ok(bytes) = strip_ansi_escapes::strip(astring) {
String::from_utf8_lossy(&bytes).to_string()
} else {
astring.to_string()
}
}
fn unicode_width_strip_ansi(astring: &str) -> usize {
let stripped_string: String = {
if let Ok(bytes) = strip_ansi_escapes::strip(astring) {
String::from_utf8_lossy(&bytes).to_string()
} else {
astring.to_string()
}
};
UnicodeWidthStr::width(&stripped_string[..])
}
pub fn split_sublines(input: &str) -> Vec<Vec<Subline>> {
input
.split_terminator('\n')
.map(|line| {
line.split_terminator(' ')
.map(|x| Subline {
subline: x,
subline: x.to_string(),
width: {
// We've tried UnicodeWidthStr::width(x), UnicodeSegmentation::graphemes(x, true).count()
// and x.chars().count() with all types of combinations. Currently, it appears that
// getting the max of char count and Unicode width seems to produce the best layout.
// However, it's not perfect.
let c = x.chars().count();
let u = UnicodeWidthStr::width(x);
std::cmp::max(c, u)
// let c = x.chars().count();
// let u = UnicodeWidthStr::width(x);
// std::cmp::min(c, u)
unicode_width_strip_ansi(x)
},
})
.collect::<Vec<_>>()
@ -101,19 +123,18 @@ pub fn column_width(input: &[Vec<Subline>]) -> usize {
}
fn split_word(cell_width: usize, word: &str) -> Vec<Subline> {
use unicode_width::UnicodeWidthChar;
let mut output = vec![];
let mut current_width = 0;
let mut start_index = 0;
let mut end_index;
for c in word.char_indices() {
let word_no_ansi = strip_ansi(word);
for c in word_no_ansi.char_indices() {
if let Some(width) = c.1.width() {
end_index = c.0;
if current_width + width > cell_width {
output.push(Subline {
subline: &word[start_index..end_index],
subline: word.cut(start_index..end_index),
width: current_width,
});
@ -127,7 +148,7 @@ fn split_word(cell_width: usize, word: &str) -> Vec<Subline> {
if start_index != word.len() {
output.push(Subline {
subline: &word[start_index..],
subline: word.cut(start_index..),
width: current_width,
});
}
@ -135,9 +156,9 @@ fn split_word(cell_width: usize, word: &str) -> Vec<Subline> {
output
}
pub fn wrap<'a>(
pub fn wrap(
cell_width: usize,
mut input: impl Iterator<Item = Subline<'a>>,
mut input: impl Iterator<Item = Subline>,
color_hm: &HashMap<String, Style>,
re_leading: &regex::Regex,
re_trailing: &regex::Regex,
@ -165,7 +186,7 @@ pub fn wrap<'a>(
// If this is a really long single word, we need to split the word
if current_line.len() == 1 && current_width > cell_width {
max_width = cell_width;
let sublines = split_word(cell_width, current_line[0].subline);
let sublines = split_word(cell_width, &current_line[0].subline);
for subline in sublines {
let width = subline.width;
lines.push(Line {
@ -200,7 +221,7 @@ pub fn wrap<'a>(
None => {
if current_width > cell_width {
// We need to break up the last word
let sublines = split_word(cell_width, current_line[0].subline);
let sublines = split_word(cell_width, &current_line[0].subline);
for subline in sublines {
let width = subline.width;
lines.push(Line {
@ -235,7 +256,7 @@ pub fn wrap<'a>(
first = false;
current_line_width = subline.width;
}
current_line.push_str(subline.subline);
current_line.push_str(&subline.subline);
}
if current_line_width > current_max {