Patch explore 4 (#7517)

ref #7339 - This PR updates explore to take some of the colors from
nushell, namely the line colors and the ls_colors.

note: Not sure why this regression appeared maybe it's a feature or it's
no longer supposed to be supported?

Signed-off-by: Maxim Zhiburt <zhiburt@gmail.com>
Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com>
This commit is contained in:
Maxim Zhiburt 2022-12-18 17:43:15 +03:00 committed by GitHub
parent 183be911d0
commit 28123841ba
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
14 changed files with 276 additions and 80 deletions

3
Cargo.lock generated
View file

@ -2566,6 +2566,7 @@ version = "0.72.2"
dependencies = [
"nu-ansi-term",
"nu-engine",
"nu-json",
"nu-path",
"nu-protocol",
"nu-test-support",
@ -2691,6 +2692,7 @@ version = "0.72.2"
dependencies = [
"ansi-str 0.7.2",
"crossterm 0.24.0",
"lscolors",
"nu-ansi-term",
"nu-color-config",
"nu-engine",
@ -2698,6 +2700,7 @@ dependencies = [
"nu-parser",
"nu-protocol",
"nu-table",
"nu-utils",
"strip-ansi-escapes",
"terminal_size 0.2.1",
"tui",

View file

@ -18,4 +18,5 @@ nu-utils = { path = "../nu-utils", version = "0.72.2" }
nu-engine = { path = "../nu-engine", version = "0.72.2" }
nu-test-support = { path="../nu-test-support", version = "0.72.2" }
# nu-path is used only for test support
nu-path = { path="../nu-path", version = "0.72.2" }
nu-path = { path="../nu-path", version = "0.72.2" }
nu-json = { path="../nu-json", version = "0.72.2" }

View file

@ -1,4 +1,7 @@
use crate::nu_style::{color_from_hex, lookup_style};
use crate::{
nu_style::{color_from_hex, lookup_style},
parse_nustyle, NuStyle,
};
use nu_ansi_term::Style;
use nu_protocol::Value;
use std::collections::HashMap;
@ -9,6 +12,8 @@ pub fn lookup_ansi_color_style(s: &str) -> Style {
.ok()
.and_then(|c| c.map(|c| c.normal()))
.unwrap_or_default()
} else if s.starts_with('{') {
color_string_to_nustyle(s.to_string())
} else {
lookup_style(s)
}
@ -36,3 +41,17 @@ pub fn get_color_map(colors: &HashMap<String, Value>) -> HashMap<String, Style>
hm
}
fn color_string_to_nustyle(color_string: String) -> Style {
// eprintln!("color_string: {}", &color_string);
if color_string.is_empty() {
return Style::default();
}
let nu_style = match nu_json::from_str::<NuStyle>(&color_string) {
Ok(s) => s,
Err(_) => return Style::default(),
};
parse_nustyle(nu_style)
}

View file

@ -106,6 +106,7 @@ pub fn color_record_to_nustyle(value: &Value) -> Style {
}
}
}
parse_nustyle(NuStyle { fg, bg, attr })
}

View file

@ -74,13 +74,16 @@ impl Command for Explore {
let mut config = nu_config.explore.clone();
prepare_default_config(&mut config);
update_config(&mut config, show_index, show_head);
include_nu_config(&mut config, &style_computer);
let show_banner = is_need_banner(&config).unwrap_or(true);
let exit_esc = is_need_esc_exit(&config).unwrap_or(true);
let style = style_from_config(&config);
let mut config = PagerConfig::new(nu_config, &style_computer, config);
let lscolors = nu_explore::util::create_lscolors(engine_state, stack);
let mut config = PagerConfig::new(nu_config, &style_computer, &lscolors, config);
config.style = style;
config.reverse = is_reverse;
config.peek_value = peek_value;
@ -246,6 +249,7 @@ fn prepare_default_config(config: &mut HashMap<String, Value>) {
config.insert(String::from("status"), map_into_value(hm));
}
{
let mut hm = config
.get("table")
@ -322,8 +326,7 @@ fn insert_style(map: &mut HashMap<String, Value>, key: &str, value: Style) {
}
let value = nu_color_config::NuStyle::from(value);
if let Ok(val) = nu_json::to_string(&value) {
if let Ok(val) = nu_json::to_string_raw(&value) {
map.insert(String::from(key), Value::string(val, Span::unknown()));
}
}
@ -368,3 +371,39 @@ fn convert_json_value_into_value(value: nu_json::Value) -> Value {
}
}
}
fn include_nu_config(config: &mut HashMap<String, Value>, style_computer: &StyleComputer) {
let line_color = lookup_color(style_computer, "separator");
if line_color != nu_ansi_term::Style::default() {
{
let mut map = config
.get("table")
.and_then(parse_hash_map)
.unwrap_or_default();
insert_style(&mut map, "split_line", line_color);
config.insert(String::from("table"), map_into_value(map));
}
{
let mut map = config
.get("try")
.and_then(parse_hash_map)
.unwrap_or_default();
insert_style(&mut map, "border_color", line_color);
config.insert(String::from("try"), map_into_value(map));
}
{
let mut map = config
.get("config")
.and_then(parse_hash_map)
.unwrap_or_default();
insert_style(&mut map, "border_color", line_color);
config.insert(String::from("config"), map_into_value(map));
}
}
}
fn lookup_color(style_computer: &StyleComputer, key: &str) -> nu_ansi_term::Style {
style_computer.compute(key, &Value::nothing(Span::unknown()))
}

View file

@ -14,10 +14,12 @@ nu-parser = { path = "../nu-parser", version = "0.72.2" }
nu-color-config = { path = "../nu-color-config", version = "0.72.2" }
nu-engine = { path = "../nu-engine", version = "0.72.2" }
nu-table = { path = "../nu-table", version = "0.72.2" }
nu-json = { path="../nu-json", version = "0.72.2" }
nu-json = { path = "../nu-json", version = "0.72.2" }
nu-utils = { path = "../nu-utils", version = "0.72.2" }
terminal_size = "0.2.1"
strip-ansi-escapes = "0.1.1"
crossterm = "0.24.0"
tui = "0.19.0"
ansi-str = "0.7.2"
lscolors = { version = "0.12.0", features = ["crossterm"], default-features = false }

View file

@ -23,7 +23,7 @@ use views::{InformationView, Orientation, Preview, RecordView};
pub use pager::{PagerConfig, StyleConfig};
pub mod util {
pub use super::nu_common::{create_map, map_into_value};
pub use super::nu_common::{create_lscolors, create_map, map_into_value};
}
pub fn run_pager(

View file

@ -0,0 +1,115 @@
use std::fs::symlink_metadata;
use lscolors::LsColors;
use nu_ansi_term::{Color, Style};
use nu_engine::env_to_string;
use nu_protocol::engine::{EngineState, Stack};
use nu_utils::get_ls_colors;
use super::NuText;
pub fn create_lscolors(engine_state: &EngineState, stack: &Stack) -> LsColors {
let colors = stack
.get_env_var(engine_state, "LS_COLORS")
.and_then(|v| env_to_string("LS_COLORS", &v, engine_state, stack).ok());
get_ls_colors(colors)
}
pub fn lscolorize(header: &[String], data: &mut [Vec<NuText>], lscolors: &LsColors) {
for (col, col_name) in header.iter().enumerate() {
if col_name != "name" {
continue;
}
for row in data.iter_mut() {
let (path, text_style) = &mut row[col];
let style = get_path_style(path, lscolors);
if let Some(style) = style {
*text_style = text_style.style(style);
}
}
}
}
fn get_path_style(path: &str, ls_colors: &LsColors) -> Option<Style> {
let stripped_path = nu_utils::strip_ansi_unlikely(path);
let style = match symlink_metadata(stripped_path.as_ref()) {
Ok(metadata) => {
ls_colors.style_for_path_with_metadata(stripped_path.as_ref(), Some(&metadata))
}
Err(_) => ls_colors.style_for_path(stripped_path.as_ref()),
};
style.map(lsstyle_to_nu_style)
}
fn lsstyle_to_nu_style(s: &lscolors::Style) -> Style {
let mut out = Style::default();
if let Some(clr) = &s.background {
out.background = lscolor_to_nu_color(clr);
}
if let Some(clr) = &s.foreground {
out.foreground = lscolor_to_nu_color(clr);
}
if s.font_style.slow_blink | s.font_style.rapid_blink {
out.is_blink = true;
}
if s.font_style.bold {
out.is_bold = true;
}
if s.font_style.dimmed {
out.is_dimmed = true;
}
if s.font_style.hidden {
out.is_hidden = true;
}
if s.font_style.reverse {
out.is_reverse = true;
}
if s.font_style.italic {
out.is_italic = true;
}
if s.font_style.underline {
out.is_underline = true;
}
out
}
fn lscolor_to_nu_color(clr: &lscolors::Color) -> Option<Color> {
use lscolors::Color::*;
let clr = match clr {
Black => Color::Black,
BrightBlack => Color::DarkGray,
Red => Color::Red,
BrightRed => Color::LightRed,
Green => Color::Green,
BrightGreen => Color::LightGreen,
Yellow => Color::Yellow,
BrightYellow => Color::LightYellow,
Blue => Color::Blue,
BrightBlue => Color::LightBlue,
Magenta => Color::Magenta,
BrightMagenta => Color::LightMagenta,
Cyan => Color::Cyan,
BrightCyan => Color::LightCyan,
White => Color::White,
BrightWhite => Color::LightGray,
&Fixed(i) => Color::Fixed(i),
&RGB(r, g, b) => Color::Rgb(r, g, b),
};
Some(clr)
}

View file

@ -1,4 +1,5 @@
mod command;
mod lscolor;
mod string;
mod table;
mod value;
@ -15,6 +16,7 @@ pub type NuText = (String, TextStyle);
pub type CtrlC = Option<Arc<AtomicBool>>;
pub use command::{is_ignored_command, run_command_with_value, run_nu_command};
pub use lscolor::{create_lscolors, lscolorize};
pub use string::truncate_str;
pub use table::try_build_table;
pub use value::{collect_input, collect_pipeline, create_map, map_into_value, nu_str};

View file

@ -1,7 +1,7 @@
use std::collections::HashMap;
use nu_engine::get_columns;
use nu_protocol::{ast::PathMember, PipelineData, Value};
use nu_protocol::{ast::PathMember, ListStream, PipelineData, PipelineMetadata, RawStream, Value};
use super::NuSpan;
@ -9,25 +9,7 @@ pub fn collect_pipeline(input: PipelineData) -> (Vec<String>, Vec<Vec<Value>>) {
match input {
PipelineData::Empty => (vec![], vec![]),
PipelineData::Value(value, ..) => collect_input(value),
PipelineData::ListStream(mut stream, ..) => {
let mut records = vec![];
for item in stream.by_ref() {
records.push(item);
}
let mut cols = get_columns(&records);
let data = convert_records_to_dataset(&cols, records);
// trying to deal with 'not standart input'
if cols.is_empty() && !data.is_empty() {
let min_column_length = data.iter().map(|row| row.len()).min().unwrap_or(0);
if min_column_length > 0 {
cols = (0..min_column_length).map(|i| i.to_string()).collect();
}
}
(cols, data)
}
PipelineData::ListStream(stream, ..) => collect_list_stream(stream),
PipelineData::ExternalStream {
stdout,
stderr,
@ -35,55 +17,78 @@ pub fn collect_pipeline(input: PipelineData) -> (Vec<String>, Vec<Vec<Value>>) {
metadata,
span,
..
} => {
let mut columns = vec![];
let mut data = vec![];
} => collect_external_stream(stdout, stderr, exit_code, metadata, span),
}
}
if let Some(stdout) = stdout {
let value = stdout.into_string().map_or_else(
|error| Value::Error { error },
|string| Value::string(string.item, span),
);
fn collect_list_stream(mut stream: ListStream) -> (Vec<String>, Vec<Vec<Value>>) {
let mut records = vec![];
for item in stream.by_ref() {
records.push(item);
}
columns.push(String::from("stdout"));
data.push(value);
}
let mut cols = get_columns(&records);
let data = convert_records_to_dataset(&cols, records);
if let Some(stderr) = stderr {
let value = stderr.into_string().map_or_else(
|error| Value::Error { error },
|string| Value::string(string.item, span),
);
columns.push(String::from("stderr"));
data.push(value);
}
if let Some(exit_code) = exit_code {
let list = exit_code.collect::<Vec<_>>();
let val = Value::List { vals: list, span };
columns.push(String::from("exit_code"));
data.push(val);
}
if metadata.is_some() {
let val = Value::Record {
cols: vec![String::from("data_source")],
vals: vec![Value::String {
val: String::from("ls"),
span,
}],
span,
};
columns.push(String::from("metadata"));
data.push(val);
}
(columns, vec![data])
// trying to deal with 'non-standard input'
if cols.is_empty() && !data.is_empty() {
let min_column_length = data.iter().map(|row| row.len()).min().unwrap_or(0);
if min_column_length > 0 {
cols = (0..min_column_length).map(|i| i.to_string()).collect();
}
}
(cols, data)
}
fn collect_external_stream(
stdout: Option<RawStream>,
stderr: Option<RawStream>,
exit_code: Option<ListStream>,
metadata: Option<PipelineMetadata>,
span: NuSpan,
) -> (Vec<String>, Vec<Vec<Value>>) {
let mut columns = vec![];
let mut data = vec![];
if let Some(stdout) = stdout {
let value = stdout.into_string().map_or_else(
|error| Value::Error { error },
|string| Value::string(string.item, span),
);
columns.push(String::from("stdout"));
data.push(value);
}
if let Some(stderr) = stderr {
let value = stderr.into_string().map_or_else(
|error| Value::Error { error },
|string| Value::string(string.item, span),
);
columns.push(String::from("stderr"));
data.push(value);
}
if let Some(exit_code) = exit_code {
let list = exit_code.collect::<Vec<_>>();
let val = Value::List { vals: list, span };
columns.push(String::from("exit_code"));
data.push(val);
}
if metadata.is_some() {
let val = Value::Record {
cols: vec![String::from("data_source")],
vals: vec![Value::String {
val: String::from("ls"),
span,
}],
span,
};
columns.push(String::from("metadata"));
data.push(val);
}
(columns, vec![data])
}
/// Try to build column names and a table grid.

View file

@ -18,6 +18,7 @@ use crossterm::{
LeaveAlternateScreen,
},
};
use lscolors::LsColors;
use nu_color_config::{lookup_ansi_color_style, StyleComputer};
use nu_protocol::{
engine::{EngineState, Stack},
@ -142,6 +143,7 @@ impl<'a> Pager<'a> {
self.config.nu_config,
self.config.style_computer,
&self.config.config,
self.config.lscolors,
))
}
@ -161,6 +163,7 @@ pub enum Transition {
pub struct PagerConfig<'a> {
pub nu_config: &'a NuConfig,
pub style_computer: &'a StyleComputer<'a>,
pub lscolors: &'a LsColors,
pub config: ConfigMap,
pub style: StyleConfig,
pub peek_value: bool,
@ -173,12 +176,14 @@ impl<'a> PagerConfig<'a> {
pub fn new(
nu_config: &'a NuConfig,
style_computer: &'a StyleComputer,
lscolors: &'a LsColors,
config: ConfigMap,
) -> Self {
Self {
nu_config,
style_computer,
config,
lscolors,
peek_value: false,
exit_esc: true,
reverse: false,
@ -267,6 +272,7 @@ fn render_ui(
pager.config.nu_config,
pager.config.style_computer,
&pager.config.config,
pager.config.lscolors,
);
page.view.draw(f, available_area, cfg, &mut layout);
@ -422,6 +428,7 @@ fn run_command(
pager.config.nu_config,
pager.config.style_computer,
&pager.config.config,
pager.config.lscolors,
));
}
@ -430,6 +437,7 @@ fn run_command(
pager.config.nu_config,
pager.config.style_computer,
&pager.config.config,
pager.config.lscolors,
));
}
}
@ -458,6 +466,7 @@ fn run_command(
pager.config.nu_config,
pager.config.style_computer,
&pager.config.config,
pager.config.lscolors,
));
*view = Some(Page::raw(new_view, is_light));

View file

@ -7,6 +7,7 @@ mod record;
pub mod util;
use crossterm::event::KeyEvent;
use lscolors::LsColors;
use nu_color_config::StyleComputer;
use nu_protocol::{
engine::{EngineState, Stack},
@ -61,6 +62,7 @@ pub struct ViewConfig<'a> {
pub nu_config: &'a NuConfig,
pub style_computer: &'a StyleComputer<'a>,
pub config: &'a ConfigMap,
pub lscolors: &'a LsColors,
}
impl<'a> ViewConfig<'a> {
@ -68,11 +70,13 @@ impl<'a> ViewConfig<'a> {
nu_config: &'a NuConfig,
style_computer: &'a StyleComputer<'a>,
config: &'a ConfigMap,
lscolors: &'a LsColors,
) -> Self {
Self {
nu_config,
style_computer,
config,
lscolors,
}
}
}

View file

@ -11,7 +11,7 @@ use nu_protocol::{
use tui::{layout::Rect, widgets::Block};
use crate::{
nu_common::{collect_input, NuConfig, NuSpan, NuStyle, NuText},
nu_common::{collect_input, lscolorize, NuConfig, NuSpan, NuStyle, NuText},
pager::{
report::{Report, Severity},
ConfigMap, Frame, Transition, ViewInfo,
@ -208,7 +208,9 @@ impl<'a> RecordView<'a> {
fn create_tablew(&'a self, cfg: ViewConfig<'a>) -> TableW<'a> {
let layer = self.get_layer_last();
let data = convert_records_to_string(&layer.records, cfg.nu_config, cfg.style_computer);
let mut data = convert_records_to_string(&layer.records, cfg.nu_config, cfg.style_computer);
lscolorize(&layer.columns, &mut data, cfg.lscolors);
let headers = layer.columns.as_ref();
let style_computer = cfg.style_computer;

View file

@ -107,13 +107,7 @@ pub fn nu_ansi_color_to_tui_color(clr: NuColor) -> Option<tui::style::Color> {
pub fn text_style_to_tui_style(style: TextStyle) -> tui::style::Style {
let mut out = tui::style::Style::default();
if let Some(style) = style.color_style {
if let Some(clr) = style.background {
out.bg = nu_ansi_color_to_tui_color(clr);
}
if let Some(clr) = style.foreground {
out.fg = nu_ansi_color_to_tui_color(clr);
}
out = nu_style_to_tui(style);
}
out