diff --git a/Cargo.lock b/Cargo.lock index a00377e639..d1f35a730e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2845,7 +2845,7 @@ dependencies = [ [[package]] name = "reedline" version = "0.2.0" -source = "git+https://github.com/nushell/reedline?branch=main#56025adb65f1c27078d64e5d6220827a6f0ebdb3" +source = "git+https://github.com/nushell/reedline?branch=main#4c3c23c9594ab2d58015fc75beb9fd884b763614" dependencies = [ "chrono", "crossterm", diff --git a/crates/nu-color-config/src/color_config.rs b/crates/nu-color-config/src/color_config.rs index be74e98291..459095cadf 100644 --- a/crates/nu-color-config/src/color_config.rs +++ b/crates/nu-color-config/src/color_config.rs @@ -4,9 +4,9 @@ use nu_protocol::Config; use nu_table::{Alignment, TextStyle}; use std::collections::HashMap; -pub fn lookup_ansi_color_style(s: String) -> Style { +pub fn lookup_ansi_color_style(s: &str) -> Style { if s.starts_with('#') { - match color_from_hex(&s) { + match color_from_hex(s) { Ok(c) => match c { Some(c) => c.normal(), None => Style::default(), @@ -14,9 +14,9 @@ pub fn lookup_ansi_color_style(s: String) -> Style { Err(_) => Style::default(), } } else if s.starts_with('{') { - color_string_to_nustyle(s) + color_string_to_nustyle(s.to_string()) } else { - match s.as_str() { + match s { "g" | "green" => Color::Green.normal(), "gb" | "green_bold" => Color::Green.bold(), "gu" | "green_underline" => Color::Green.underline(), @@ -168,7 +168,7 @@ pub fn lookup_ansi_color_style(s: String) -> Style { fn update_hashmap(key: &str, val: &str, hm: &mut HashMap) { // eprintln!("key: {}, val: {}", &key, &val); - let color = lookup_ansi_color_style(val.to_string()); + let color = lookup_ansi_color_style(val); if let Some(v) = hm.get_mut(key) { *v = color; } else { @@ -210,7 +210,10 @@ pub fn get_color_config(config: &Config) -> HashMap { hm.insert("hints".to_string(), Color::DarkGray.normal()); for (key, value) in &config.color_config { - update_hashmap(key, value, &mut hm); + let value = value + .as_string() + .expect("the only values for config color must be strings"); + update_hashmap(key, &value, &mut hm); // eprintln!( // "config: {}:{}\t\t\thashmap: {}:{:?}", diff --git a/crates/nu-color-config/src/shape_color.rs b/crates/nu-color-config/src/shape_color.rs index 190475dcba..a856a0ed16 100644 --- a/crates/nu-color-config/src/shape_color.rs +++ b/crates/nu-color-config/src/shape_color.rs @@ -4,7 +4,10 @@ use nu_protocol::Config; pub fn get_shape_color(shape: String, conf: &Config) -> Style { match conf.color_config.get(shape.as_str()) { - Some(int_color) => lookup_ansi_color_style(int_color.to_string()), + Some(int_color) => match int_color.as_string() { + Ok(int_color) => lookup_ansi_color_style(&int_color), + Err(_) => Style::default(), + }, None => match shape.as_ref() { "flatshape_garbage" => Style::new().fg(Color::White).on(Color::Red).bold(), "flatshape_bool" => Style::new().fg(Color::LightCyan), diff --git a/crates/nu-protocol/src/config.rs b/crates/nu-protocol/src/config.rs index 9b7e51833e..8fe530e544 100644 --- a/crates/nu-protocol/src/config.rs +++ b/crates/nu-protocol/src/config.rs @@ -43,7 +43,7 @@ pub struct Config { pub filesize_metric: bool, pub table_mode: String, pub use_ls_colors: bool, - pub color_config: HashMap, + pub color_config: HashMap, pub use_grid_icons: bool, pub footer_mode: FooterMode, pub animate_prompt: bool, @@ -54,6 +54,7 @@ pub struct Config { pub edit_mode: String, pub max_history_size: i64, pub log_level: String, + pub menu_config: HashMap, } impl Default for Config { @@ -73,6 +74,7 @@ impl Default for Config { edit_mode: "emacs".into(), max_history_size: 1000, log_level: String::new(), + menu_config: HashMap::new(), } } } @@ -107,42 +109,7 @@ impl Value { config.use_ls_colors = value.as_bool()?; } "color_config" => { - let (cols, inner_vals) = value.as_record()?; - let mut hm = HashMap::new(); - for (k, v) in cols.iter().zip(inner_vals) { - match &v { - Value::Record { - cols: inner_cols, - vals: inner_vals, - span: _, - } => { - // make a string from our config.color_config section that - // looks like this: { fg: "#rrggbb" bg: "#rrggbb" attr: "abc", } - // the real key here was to have quotes around the values but not - // require them around the keys. - - // maybe there's a better way to generate this but i'm not sure - // what it is. - let key = k.to_string(); - let mut val: String = inner_cols - .iter() - .zip(inner_vals) - .map(|(x, y)| { - let clony = y.clone(); - format!("{}: \"{}\" ", x, clony.into_string(", ", &config)) - }) - .collect(); - // now insert the braces at the front and the back to fake the json string - val.insert(0, '{'); - val.push('}'); - hm.insert(key, val); - } - _ => { - hm.insert(k.to_string(), v.as_string()?); - } - } - } - config.color_config = hm; + config.color_config = create_map(value, &config)?; } "use_grid_icons" => { config.use_grid_icons = value.as_bool()?; @@ -191,6 +158,9 @@ impl Value { "log_level" => { config.log_level = value.as_string()?; } + "menu_config" => { + config.menu_config = create_map(value, &config)?; + } _ => {} } } @@ -198,3 +168,48 @@ impl Value { Ok(config) } } + +fn create_map(value: &Value, config: &Config) -> Result, ShellError> { + let (cols, inner_vals) = value.as_record()?; + let mut hm: HashMap = HashMap::new(); + + for (k, v) in cols.iter().zip(inner_vals) { + match &v { + Value::Record { + cols: inner_cols, + vals: inner_vals, + span, + } => { + // make a string from our config.color_config section that + // looks like this: { fg: "#rrggbb" bg: "#rrggbb" attr: "abc", } + // the real key here was to have quotes around the values but not + // require them around the keys. + + // maybe there's a better way to generate this but i'm not sure + // what it is. + let key = k.to_string(); + let val: String = inner_cols + .iter() + .zip(inner_vals) + .map(|(x, y)| { + let clony = y.clone(); + format!("{}: \"{}\" ", x, clony.into_string(", ", config)) + }) + .collect(); + + // now insert the braces at the front and the back to fake the json string + let val = Value::String { + val: format!("{{{}}}", val), + span: *span, + }; + + hm.insert(key, val); + } + _ => { + hm.insert(k.to_string(), v.clone()); + } + } + } + + Ok(hm) +} diff --git a/src/main.rs b/src/main.rs index 6a6576fd83..0dc6e62227 100644 --- a/src/main.rs +++ b/src/main.rs @@ -9,7 +9,7 @@ use dialoguer::{ use log::trace; use miette::{IntoDiagnostic, Result}; use nu_cli::{CliError, NuCompleter, NuHighlighter, NuValidator, NushellPrompt}; -use nu_color_config::get_color_config; +use nu_color_config::{get_color_config, lookup_ansi_color_style}; use nu_command::create_default_context; use nu_engine::{convert_env_values, eval_block}; use nu_parser::{lex, parse, trim_quotes, Token, TokenContents}; @@ -19,8 +19,8 @@ use nu_protocol::{ Config, PipelineData, ShellError, Span, Value, CONFIG_VARIABLE_ID, }; use reedline::{ - default_emacs_keybindings, Completer, CompletionActionHandler, DefaultHinter, EditCommand, - Emacs, LineBuffer, Prompt, ReedlineEvent, Vi, + default_emacs_keybindings, Completer, CompletionActionHandler, ContextMenuInput, DefaultHinter, + EditCommand, Emacs, LineBuffer, Prompt, ReedlineEvent, Vi, }; use std::{ io::Write, @@ -435,7 +435,11 @@ fn main() -> Result<()> { })) .with_edit_mode(edit_mode) .with_ansi_colors(config.use_ansi_coloring) - .with_menu_completer(Box::new(NuCompleter::new(engine_state.clone()))); + .with_menu_completer( + Box::new(NuCompleter::new(engine_state.clone())), + create_menu_input(&config), + ); + //FIXME: if config.use_ansi_coloring is false then we should // turn off the hinter but I don't see any way to do that yet. @@ -587,6 +591,58 @@ fn main() -> Result<()> { } } +// This creates an input object for the context menu based on the dictionary +// stored in the config variable +fn create_menu_input(config: &Config) -> ContextMenuInput { + let mut input = ContextMenuInput::default(); + + input = match config + .menu_config + .get("columns") + .and_then(|value| value.as_integer().ok()) + { + Some(value) => input.with_columns(value as u16), + None => input, + }; + + input = input.with_col_width( + config + .menu_config + .get("col_width") + .and_then(|value| value.as_integer().ok()) + .map(|value| value as usize), + ); + + input = match config + .menu_config + .get("col_padding") + .and_then(|value| value.as_integer().ok()) + { + Some(value) => input.with_col_padding(value as usize), + None => input, + }; + + input = match config + .menu_config + .get("text_style") + .and_then(|value| value.as_string().ok()) + { + Some(value) => input.with_text_style(lookup_ansi_color_style(&value)), + None => input, + }; + + input = match config + .menu_config + .get("selected_text_style") + .and_then(|value| value.as_string().ok()) + { + Some(value) => input.with_selected_text_style(lookup_ansi_color_style(&value)), + None => input, + }; + + input +} + // This fill collect environment variables from std::env and adds them to a stack. // // In order to ensure the values have spans, it first creates a dummy file, writes the collected