mirror of
https://github.com/nushell/nushell
synced 2024-11-10 15:14:14 +00:00
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:
parent
42367ddf6d
commit
ab22619f4a
6 changed files with 210 additions and 47 deletions
117
Cargo.lock
generated
117
Cargo.lock
generated
|
@ -36,6 +36,26 @@ dependencies = [
|
||||||
"memchr",
|
"memchr",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ansi-cut"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "116f67a17a90942bc30b3fe78b800941f8276234386844b3e0dee3f9d2782839"
|
||||||
|
dependencies = [
|
||||||
|
"ansi-parser",
|
||||||
|
"strip-ansi-escapes",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "ansi-parser"
|
||||||
|
version = "0.8.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "bcb2392079bf27198570d6af79ecbd9ec7d8f16d3ec6b60933922fdb66287127"
|
||||||
|
dependencies = [
|
||||||
|
"heapless",
|
||||||
|
"nom 4.2.3",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ansi_term"
|
name = "ansi_term"
|
||||||
version = "0.12.1"
|
version = "0.12.1"
|
||||||
|
@ -51,6 +71,18 @@ version = "0.5.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
|
checksum = "23b62fc65de8e4e7f52534fb52b0f3ed04746ae267519eef2a83941e8085068b"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "as-slice"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "45403b49e3954a4b8428a0ac21a4b7afadccf92bfd96273f1a58cd4812496ae0"
|
||||||
|
dependencies = [
|
||||||
|
"generic-array 0.12.4",
|
||||||
|
"generic-array 0.13.3",
|
||||||
|
"generic-array 0.14.4",
|
||||||
|
"stable_deref_trait",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "assert_cmd"
|
name = "assert_cmd"
|
||||||
version = "1.0.8"
|
version = "1.0.8"
|
||||||
|
@ -133,6 +165,12 @@ dependencies = [
|
||||||
"utf8-width",
|
"utf8-width",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "byteorder"
|
||||||
|
version = "1.4.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "bytesize"
|
name = "bytesize"
|
||||||
version = "1.1.0"
|
version = "1.1.0"
|
||||||
|
@ -493,6 +531,34 @@ dependencies = [
|
||||||
"percent-encoding",
|
"percent-encoding",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generic-array"
|
||||||
|
version = "0.12.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ffdf9f34f1447443d37393cc6c2b8313aebddcd96906caf34e54c68d8e57d7bd"
|
||||||
|
dependencies = [
|
||||||
|
"typenum",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generic-array"
|
||||||
|
version = "0.13.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f797e67af32588215eaaab8327027ee8e71b9dd0b2b26996aedf20c030fce309"
|
||||||
|
dependencies = [
|
||||||
|
"typenum",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "generic-array"
|
||||||
|
version = "0.14.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "501466ecc8a30d1d3b7fc9229b122b2ce8ed6e9d9223f1138d4babb253e51817"
|
||||||
|
dependencies = [
|
||||||
|
"typenum",
|
||||||
|
"version_check 0.9.3",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "getrandom"
|
name = "getrandom"
|
||||||
version = "0.2.3"
|
version = "0.2.3"
|
||||||
|
@ -516,12 +582,33 @@ version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hash32"
|
||||||
|
version = "0.1.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d4041af86e63ac4298ce40e5cca669066e75b6f1aa3390fe2561ffa5e1d9f4cc"
|
||||||
|
dependencies = [
|
||||||
|
"byteorder",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hashbrown"
|
name = "hashbrown"
|
||||||
version = "0.11.2"
|
version = "0.11.2"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
|
checksum = "ab5ef0d4909ef3724cc8cce6ccc8572c5c817592e9285f5464f8e86f8bd3726e"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heapless"
|
||||||
|
version = "0.5.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "74911a68a1658cfcfb61bc0ccfbd536e3b6e906f8c2f7883ee50157e3e2184f1"
|
||||||
|
dependencies = [
|
||||||
|
"as-slice",
|
||||||
|
"generic-array 0.13.3",
|
||||||
|
"hash32",
|
||||||
|
"stable_deref_trait",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "hermit-abi"
|
name = "hermit-abi"
|
||||||
version = "0.1.19"
|
version = "0.1.19"
|
||||||
|
@ -542,7 +629,7 @@ dependencies = [
|
||||||
"rand_xoshiro",
|
"rand_xoshiro",
|
||||||
"sized-chunks",
|
"sized-chunks",
|
||||||
"typenum",
|
"typenum",
|
||||||
"version_check",
|
"version_check 0.9.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -664,7 +751,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f79496a5651c8d57cd033c5add8ca7ee4e3d5f7587a4777484640d9cb60392d9"
|
checksum = "f79496a5651c8d57cd033c5add8ca7ee4e3d5f7587a4777484640d9cb60392d9"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"fnv",
|
"fnv",
|
||||||
"nom",
|
"nom 1.2.4",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -748,6 +835,16 @@ version = "1.2.4"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "a5b8c256fd9471521bcb84c3cdba98921497f1a331cbc15b8030fc63b82050ce"
|
checksum = "a5b8c256fd9471521bcb84c3cdba98921497f1a331cbc15b8030fc63b82050ce"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "nom"
|
||||||
|
version = "4.2.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2ad2a91a8e869eeb30b9cb3119ae87773a8f4ae617f41b1eb9c154b2905f7bd6"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
"version_check 0.1.5",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "ntapi"
|
name = "ntapi"
|
||||||
version = "0.3.6"
|
version = "0.3.6"
|
||||||
|
@ -887,8 +984,10 @@ dependencies = [
|
||||||
name = "nu-table"
|
name = "nu-table"
|
||||||
version = "0.36.0"
|
version = "0.36.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
|
"ansi-cut",
|
||||||
"nu-ansi-term",
|
"nu-ansi-term",
|
||||||
"regex",
|
"regex",
|
||||||
|
"strip-ansi-escapes",
|
||||||
"unicode-width",
|
"unicode-width",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -1428,6 +1527,12 @@ version = "0.3.1"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043"
|
checksum = "f67ad224767faa3c7d8b6d91985b78e70a1324408abcb1cfcc2be4c06bc06043"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "stable_deref_trait"
|
||||||
|
version = "1.2.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a8f112729512f8e442d81f95a8a7ddf2b7c6b8a1a6f509a95864142b30cab2d3"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "strip-ansi-escapes"
|
name = "strip-ansi-escapes"
|
||||||
version = "0.1.1"
|
version = "0.1.1"
|
||||||
|
@ -1610,7 +1715,7 @@ version = "0.9.6"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "5baeed7327e25054889b9bd4f975f32e5f4c5d434042d59ab6cd4142c0a76ed0"
|
checksum = "5baeed7327e25054889b9bd4f975f32e5f4c5d434042d59ab6cd4142c0a76ed0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"version_check",
|
"version_check 0.9.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -1652,6 +1757,12 @@ version = "0.2.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372"
|
checksum = "936e4b492acfd135421d8dca4b1aa80a7bfc26e702ef3af710e0752684df5372"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "version_check"
|
||||||
|
version = "0.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "914b1a6776c4c929a602fafd8bc742e06365d4bcbe48c30f9cca5824f70dc9dd"
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "version_check"
|
name = "version_check"
|
||||||
version = "0.9.3"
|
version = "0.9.3"
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
use chrono::{DateTime, Utc};
|
use chrono::{DateTime, Utc};
|
||||||
|
use lscolors::{LsColors, Style};
|
||||||
use nu_engine::eval_expression;
|
use nu_engine::eval_expression;
|
||||||
use nu_protocol::ast::Call;
|
use nu_protocol::ast::Call;
|
||||||
use nu_protocol::engine::{Command, EngineState, Stack};
|
use nu_protocol::engine::{Command, EngineState, Stack};
|
||||||
|
@ -32,6 +33,7 @@ impl Command for Ls {
|
||||||
call: &Call,
|
call: &Call,
|
||||||
_input: PipelineData,
|
_input: PipelineData,
|
||||||
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
) -> Result<nu_protocol::PipelineData, nu_protocol::ShellError> {
|
||||||
|
let config = stack.get_config()?;
|
||||||
let pattern = if let Some(expr) = call.positional.get(0) {
|
let pattern = if let Some(expr) = call.positional.get(0) {
|
||||||
let result = eval_expression(engine_state, stack, expr)?;
|
let result = eval_expression(engine_state, stack, expr)?;
|
||||||
let mut result = result.as_string()?;
|
let mut result = result.as_string()?;
|
||||||
|
@ -51,6 +53,7 @@ impl Command for Ls {
|
||||||
|
|
||||||
let call_span = call.head;
|
let call_span = call.head;
|
||||||
let glob = glob::glob(&pattern).unwrap();
|
let glob = glob::glob(&pattern).unwrap();
|
||||||
|
let ls_colors = LsColors::from_env().unwrap_or_default();
|
||||||
|
|
||||||
Ok(glob
|
Ok(glob
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -60,13 +63,22 @@ impl Command for Ls {
|
||||||
let is_file = metadata.is_file();
|
let is_file = metadata.is_file();
|
||||||
let is_dir = metadata.is_dir();
|
let is_dir = metadata.is_dir();
|
||||||
let filesize = metadata.len();
|
let filesize = metadata.len();
|
||||||
|
|
||||||
let mut cols = vec!["name".into(), "type".into(), "size".into()];
|
let mut cols = vec!["name".into(), "type".into(), "size".into()];
|
||||||
|
let style = ls_colors.style_for_path(path.clone());
|
||||||
|
let ansi_style = style.map(Style::to_crossterm_style).unwrap_or_default();
|
||||||
|
let use_ls_colors = config.use_ls_colors;
|
||||||
|
|
||||||
let mut vals = vec![
|
let mut vals = vec![
|
||||||
|
if use_ls_colors {
|
||||||
|
Value::String {
|
||||||
|
val: ansi_style.apply(path.to_string_lossy()).to_string(),
|
||||||
|
span: call_span,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
Value::String {
|
Value::String {
|
||||||
val: path.to_string_lossy().to_string(),
|
val: path.to_string_lossy().to_string(),
|
||||||
span: call_span,
|
span: call_span,
|
||||||
|
}
|
||||||
},
|
},
|
||||||
if is_file {
|
if is_file {
|
||||||
Value::string("file", call_span)
|
Value::string("file", call_span)
|
||||||
|
@ -97,18 +109,31 @@ impl Command for Ls {
|
||||||
span: call_span,
|
span: call_span,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Err(_) => Value::Record {
|
Err(_) => {
|
||||||
|
let style = ls_colors.style_for_path(path.clone());
|
||||||
|
let ansi_style = style.map(Style::to_crossterm_style).unwrap_or_default();
|
||||||
|
let use_ls_colors = config.use_ls_colors;
|
||||||
|
|
||||||
|
Value::Record {
|
||||||
cols: vec!["name".into(), "type".into(), "size".into()],
|
cols: vec!["name".into(), "type".into(), "size".into()],
|
||||||
vals: vec![
|
vals: vec![
|
||||||
|
if use_ls_colors {
|
||||||
|
Value::String {
|
||||||
|
val: ansi_style.apply(path.to_string_lossy()).to_string(),
|
||||||
|
span: call_span,
|
||||||
|
}
|
||||||
|
} else {
|
||||||
Value::String {
|
Value::String {
|
||||||
val: path.to_string_lossy().to_string(),
|
val: path.to_string_lossy().to_string(),
|
||||||
span: call_span,
|
span: call_span,
|
||||||
|
}
|
||||||
},
|
},
|
||||||
Value::Nothing { span: call_span },
|
Value::Nothing { span: call_span },
|
||||||
Value::Nothing { span: call_span },
|
Value::Nothing { span: call_span },
|
||||||
],
|
],
|
||||||
span: call_span,
|
span: call_span,
|
||||||
},
|
}
|
||||||
|
}
|
||||||
},
|
},
|
||||||
_ => Value::Nothing { span: call_span },
|
_ => Value::Nothing { span: call_span },
|
||||||
})
|
})
|
||||||
|
|
|
@ -6,6 +6,7 @@ use crate::{ShellError, Value};
|
||||||
pub struct Config {
|
pub struct Config {
|
||||||
pub filesize_metric: bool,
|
pub filesize_metric: bool,
|
||||||
pub table_mode: String,
|
pub table_mode: String,
|
||||||
|
pub use_ls_colors: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Config {
|
impl Default for Config {
|
||||||
|
@ -13,6 +14,7 @@ impl Default for Config {
|
||||||
Config {
|
Config {
|
||||||
filesize_metric: false,
|
filesize_metric: false,
|
||||||
table_mode: "rounded".into(),
|
table_mode: "rounded".into(),
|
||||||
|
use_ls_colors: true,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,6 +33,9 @@ impl Value {
|
||||||
"table_mode" => {
|
"table_mode" => {
|
||||||
config.table_mode = value.as_string()?;
|
config.table_mode = value.as_string()?;
|
||||||
}
|
}
|
||||||
|
"use_ls_colors" => {
|
||||||
|
config.use_ls_colors = value.as_bool()?;
|
||||||
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,7 @@ path = "src/main.rs"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
nu-ansi-term = "0.39.0"
|
nu-ansi-term = "0.39.0"
|
||||||
|
|
||||||
regex = "1.4"
|
regex = "1.4"
|
||||||
unicode-width = "0.1.8"
|
unicode-width = "0.1.8"
|
||||||
|
strip-ansi-escapes = "0.1.1"
|
||||||
|
ansi-cut = "0.1.1"
|
|
@ -611,15 +611,15 @@ impl Table {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ProcessedTable<'a> {
|
pub struct ProcessedTable {
|
||||||
pub headers: Vec<ProcessedCell<'a>>,
|
pub headers: Vec<ProcessedCell>,
|
||||||
pub data: Vec<Vec<ProcessedCell<'a>>>,
|
pub data: Vec<Vec<ProcessedCell>>,
|
||||||
pub theme: Theme,
|
pub theme: Theme,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct ProcessedCell<'a> {
|
pub struct ProcessedCell {
|
||||||
pub contents: Vec<Vec<Subline<'a>>>,
|
pub contents: Vec<Vec<Subline>>,
|
||||||
pub style: TextStyle,
|
pub style: TextStyle,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -995,7 +995,7 @@ pub fn maybe_truncate_columns(termwidth: usize, processed_table: &mut ProcessedT
|
||||||
|
|
||||||
processed_table.headers.push(ProcessedCell {
|
processed_table.headers.push(ProcessedCell {
|
||||||
contents: vec![vec![Subline {
|
contents: vec![vec![Subline {
|
||||||
subline: "...",
|
subline: "...".to_string(),
|
||||||
width: 3,
|
width: 3,
|
||||||
}]],
|
}]],
|
||||||
style: TextStyle::basic_center(),
|
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() {
|
for entry in processed_table.data.iter_mut() {
|
||||||
entry.push(ProcessedCell {
|
entry.push(ProcessedCell {
|
||||||
contents: vec![vec![Subline {
|
contents: vec![vec![Subline {
|
||||||
subline: "...",
|
subline: "...".to_string(),
|
||||||
width: 3,
|
width: 3,
|
||||||
}]],
|
}]],
|
||||||
style: TextStyle::basic_center(),
|
style: TextStyle::basic_center(),
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
use crate::table::TextStyle;
|
use crate::table::TextStyle;
|
||||||
|
use ansi_cut::AnsiCut;
|
||||||
use nu_ansi_term::Style;
|
use nu_ansi_term::Style;
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
use std::{fmt::Display, iter::Iterator};
|
use std::{fmt::Display, iter::Iterator};
|
||||||
use unicode_width::UnicodeWidthStr;
|
use unicode_width::{UnicodeWidthChar, UnicodeWidthStr};
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub enum Alignment {
|
pub enum Alignment {
|
||||||
|
@ -12,14 +13,14 @@ pub enum Alignment {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Subline<'a> {
|
pub struct Subline {
|
||||||
pub subline: &'a str,
|
pub subline: String,
|
||||||
pub width: usize,
|
pub width: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct Line<'a> {
|
pub struct Line {
|
||||||
pub sublines: Vec<Subline<'a>>,
|
pub sublines: Vec<Subline>,
|
||||||
pub width: usize,
|
pub width: usize,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -37,7 +38,7 @@ pub struct WrappedCell {
|
||||||
pub style: TextStyle,
|
pub style: TextStyle,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> Display for Line<'a> {
|
impl Display for Line {
|
||||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
let mut first = true;
|
let mut first = true;
|
||||||
for subline in &self.sublines {
|
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>> {
|
pub fn split_sublines(input: &str) -> Vec<Vec<Subline>> {
|
||||||
input
|
input
|
||||||
.split_terminator('\n')
|
.split_terminator('\n')
|
||||||
.map(|line| {
|
.map(|line| {
|
||||||
line.split_terminator(' ')
|
line.split_terminator(' ')
|
||||||
.map(|x| Subline {
|
.map(|x| Subline {
|
||||||
subline: x,
|
subline: x.to_string(),
|
||||||
width: {
|
width: {
|
||||||
// We've tried UnicodeWidthStr::width(x), UnicodeSegmentation::graphemes(x, true).count()
|
// We've tried UnicodeWidthStr::width(x), UnicodeSegmentation::graphemes(x, true).count()
|
||||||
// and x.chars().count() with all types of combinations. Currently, it appears that
|
// 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.
|
// getting the max of char count and Unicode width seems to produce the best layout.
|
||||||
// However, it's not perfect.
|
// However, it's not perfect.
|
||||||
let c = x.chars().count();
|
// let c = x.chars().count();
|
||||||
let u = UnicodeWidthStr::width(x);
|
// let u = UnicodeWidthStr::width(x);
|
||||||
std::cmp::max(c, u)
|
// std::cmp::min(c, u)
|
||||||
|
unicode_width_strip_ansi(x)
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
|
@ -101,19 +123,18 @@ pub fn column_width(input: &[Vec<Subline>]) -> usize {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn split_word(cell_width: usize, word: &str) -> Vec<Subline> {
|
fn split_word(cell_width: usize, word: &str) -> Vec<Subline> {
|
||||||
use unicode_width::UnicodeWidthChar;
|
|
||||||
|
|
||||||
let mut output = vec![];
|
let mut output = vec![];
|
||||||
let mut current_width = 0;
|
let mut current_width = 0;
|
||||||
let mut start_index = 0;
|
let mut start_index = 0;
|
||||||
let mut end_index;
|
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() {
|
if let Some(width) = c.1.width() {
|
||||||
end_index = c.0;
|
end_index = c.0;
|
||||||
if current_width + width > cell_width {
|
if current_width + width > cell_width {
|
||||||
output.push(Subline {
|
output.push(Subline {
|
||||||
subline: &word[start_index..end_index],
|
subline: word.cut(start_index..end_index),
|
||||||
width: current_width,
|
width: current_width,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -127,7 +148,7 @@ fn split_word(cell_width: usize, word: &str) -> Vec<Subline> {
|
||||||
|
|
||||||
if start_index != word.len() {
|
if start_index != word.len() {
|
||||||
output.push(Subline {
|
output.push(Subline {
|
||||||
subline: &word[start_index..],
|
subline: word.cut(start_index..),
|
||||||
width: current_width,
|
width: current_width,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -135,9 +156,9 @@ fn split_word(cell_width: usize, word: &str) -> Vec<Subline> {
|
||||||
output
|
output
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn wrap<'a>(
|
pub fn wrap(
|
||||||
cell_width: usize,
|
cell_width: usize,
|
||||||
mut input: impl Iterator<Item = Subline<'a>>,
|
mut input: impl Iterator<Item = Subline>,
|
||||||
color_hm: &HashMap<String, Style>,
|
color_hm: &HashMap<String, Style>,
|
||||||
re_leading: ®ex::Regex,
|
re_leading: ®ex::Regex,
|
||||||
re_trailing: ®ex::Regex,
|
re_trailing: ®ex::Regex,
|
||||||
|
@ -165,7 +186,7 @@ pub fn wrap<'a>(
|
||||||
// If this is a really long single word, we need to split the word
|
// If this is a really long single word, we need to split the word
|
||||||
if current_line.len() == 1 && current_width > cell_width {
|
if current_line.len() == 1 && current_width > cell_width {
|
||||||
max_width = cell_width;
|
max_width = cell_width;
|
||||||
let sublines = split_word(cell_width, current_line[0].subline);
|
let sublines = split_word(cell_width, ¤t_line[0].subline);
|
||||||
for subline in sublines {
|
for subline in sublines {
|
||||||
let width = subline.width;
|
let width = subline.width;
|
||||||
lines.push(Line {
|
lines.push(Line {
|
||||||
|
@ -200,7 +221,7 @@ pub fn wrap<'a>(
|
||||||
None => {
|
None => {
|
||||||
if current_width > cell_width {
|
if current_width > cell_width {
|
||||||
// We need to break up the last word
|
// We need to break up the last word
|
||||||
let sublines = split_word(cell_width, current_line[0].subline);
|
let sublines = split_word(cell_width, ¤t_line[0].subline);
|
||||||
for subline in sublines {
|
for subline in sublines {
|
||||||
let width = subline.width;
|
let width = subline.width;
|
||||||
lines.push(Line {
|
lines.push(Line {
|
||||||
|
@ -235,7 +256,7 @@ pub fn wrap<'a>(
|
||||||
first = false;
|
first = false;
|
||||||
current_line_width = subline.width;
|
current_line_width = subline.width;
|
||||||
}
|
}
|
||||||
current_line.push_str(subline.subline);
|
current_line.push_str(&subline.subline);
|
||||||
}
|
}
|
||||||
|
|
||||||
if current_line_width > current_max {
|
if current_line_width > current_max {
|
||||||
|
|
Loading…
Reference in a new issue