menu options (#748)

This commit is contained in:
Fernando Herrera 2022-01-15 17:01:44 +00:00 committed by GitHub
parent f9c0d223c1
commit 89d99db94f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 126 additions and 49 deletions

2
Cargo.lock generated
View File

@ -2845,7 +2845,7 @@ dependencies = [
[[package]] [[package]]
name = "reedline" name = "reedline"
version = "0.2.0" 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 = [ dependencies = [
"chrono", "chrono",
"crossterm", "crossterm",

View File

@ -4,9 +4,9 @@ use nu_protocol::Config;
use nu_table::{Alignment, TextStyle}; use nu_table::{Alignment, TextStyle};
use std::collections::HashMap; 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('#') { if s.starts_with('#') {
match color_from_hex(&s) { match color_from_hex(s) {
Ok(c) => match c { Ok(c) => match c {
Some(c) => c.normal(), Some(c) => c.normal(),
None => Style::default(), None => Style::default(),
@ -14,9 +14,9 @@ pub fn lookup_ansi_color_style(s: String) -> Style {
Err(_) => Style::default(), Err(_) => Style::default(),
} }
} else if s.starts_with('{') { } else if s.starts_with('{') {
color_string_to_nustyle(s) color_string_to_nustyle(s.to_string())
} else { } else {
match s.as_str() { match s {
"g" | "green" => Color::Green.normal(), "g" | "green" => Color::Green.normal(),
"gb" | "green_bold" => Color::Green.bold(), "gb" | "green_bold" => Color::Green.bold(),
"gu" | "green_underline" => Color::Green.underline(), "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<String, Style>) { fn update_hashmap(key: &str, val: &str, hm: &mut HashMap<String, Style>) {
// eprintln!("key: {}, val: {}", &key, &val); // 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) { if let Some(v) = hm.get_mut(key) {
*v = color; *v = color;
} else { } else {
@ -210,7 +210,10 @@ pub fn get_color_config(config: &Config) -> HashMap<String, Style> {
hm.insert("hints".to_string(), Color::DarkGray.normal()); hm.insert("hints".to_string(), Color::DarkGray.normal());
for (key, value) in &config.color_config { 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!( // eprintln!(
// "config: {}:{}\t\t\thashmap: {}:{:?}", // "config: {}:{}\t\t\thashmap: {}:{:?}",

View File

@ -4,7 +4,10 @@ use nu_protocol::Config;
pub fn get_shape_color(shape: String, conf: &Config) -> Style { pub fn get_shape_color(shape: String, conf: &Config) -> Style {
match conf.color_config.get(shape.as_str()) { 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() { None => match shape.as_ref() {
"flatshape_garbage" => Style::new().fg(Color::White).on(Color::Red).bold(), "flatshape_garbage" => Style::new().fg(Color::White).on(Color::Red).bold(),
"flatshape_bool" => Style::new().fg(Color::LightCyan), "flatshape_bool" => Style::new().fg(Color::LightCyan),

View File

@ -43,7 +43,7 @@ pub struct Config {
pub filesize_metric: bool, pub filesize_metric: bool,
pub table_mode: String, pub table_mode: String,
pub use_ls_colors: bool, pub use_ls_colors: bool,
pub color_config: HashMap<String, String>, pub color_config: HashMap<String, Value>,
pub use_grid_icons: bool, pub use_grid_icons: bool,
pub footer_mode: FooterMode, pub footer_mode: FooterMode,
pub animate_prompt: bool, pub animate_prompt: bool,
@ -54,6 +54,7 @@ pub struct Config {
pub edit_mode: String, pub edit_mode: String,
pub max_history_size: i64, pub max_history_size: i64,
pub log_level: String, pub log_level: String,
pub menu_config: HashMap<String, Value>,
} }
impl Default for Config { impl Default for Config {
@ -73,6 +74,7 @@ impl Default for Config {
edit_mode: "emacs".into(), edit_mode: "emacs".into(),
max_history_size: 1000, max_history_size: 1000,
log_level: String::new(), log_level: String::new(),
menu_config: HashMap::new(),
} }
} }
} }
@ -107,42 +109,7 @@ impl Value {
config.use_ls_colors = value.as_bool()?; config.use_ls_colors = value.as_bool()?;
} }
"color_config" => { "color_config" => {
let (cols, inner_vals) = value.as_record()?; config.color_config = create_map(value, &config)?;
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;
} }
"use_grid_icons" => { "use_grid_icons" => {
config.use_grid_icons = value.as_bool()?; config.use_grid_icons = value.as_bool()?;
@ -191,6 +158,9 @@ impl Value {
"log_level" => { "log_level" => {
config.log_level = value.as_string()?; config.log_level = value.as_string()?;
} }
"menu_config" => {
config.menu_config = create_map(value, &config)?;
}
_ => {} _ => {}
} }
} }
@ -198,3 +168,48 @@ impl Value {
Ok(config) Ok(config)
} }
} }
fn create_map(value: &Value, config: &Config) -> Result<HashMap<String, Value>, ShellError> {
let (cols, inner_vals) = value.as_record()?;
let mut hm: HashMap<String, Value> = 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)
}

View File

@ -9,7 +9,7 @@ use dialoguer::{
use log::trace; use log::trace;
use miette::{IntoDiagnostic, Result}; use miette::{IntoDiagnostic, Result};
use nu_cli::{CliError, NuCompleter, NuHighlighter, NuValidator, NushellPrompt}; 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_command::create_default_context;
use nu_engine::{convert_env_values, eval_block}; use nu_engine::{convert_env_values, eval_block};
use nu_parser::{lex, parse, trim_quotes, Token, TokenContents}; use nu_parser::{lex, parse, trim_quotes, Token, TokenContents};
@ -19,8 +19,8 @@ use nu_protocol::{
Config, PipelineData, ShellError, Span, Value, CONFIG_VARIABLE_ID, Config, PipelineData, ShellError, Span, Value, CONFIG_VARIABLE_ID,
}; };
use reedline::{ use reedline::{
default_emacs_keybindings, Completer, CompletionActionHandler, DefaultHinter, EditCommand, default_emacs_keybindings, Completer, CompletionActionHandler, ContextMenuInput, DefaultHinter,
Emacs, LineBuffer, Prompt, ReedlineEvent, Vi, EditCommand, Emacs, LineBuffer, Prompt, ReedlineEvent, Vi,
}; };
use std::{ use std::{
io::Write, io::Write,
@ -435,7 +435,11 @@ fn main() -> Result<()> {
})) }))
.with_edit_mode(edit_mode) .with_edit_mode(edit_mode)
.with_ansi_colors(config.use_ansi_coloring) .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 //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. // 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. // 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 // In order to ensure the values have spans, it first creates a dummy file, writes the collected