From 7a820b1304b45f1bbf2d2f13e44af4659acbaeb4 Mon Sep 17 00:00:00 2001 From: Darren Schroeder <343840+fdncred@users.noreply.github.com> Date: Fri, 29 Jul 2022 12:50:12 -0500 Subject: [PATCH] add a new welcome banner to nushell (#6163) * add a new welcome banner to nushell * remove top line * tweaked colors and wording * changed to dimmed white * removed a comment * make config nu stand out a little * fix type-o --- Cargo.lock | 1 + crates/nu-cli/Cargo.toml | 1 + crates/nu-cli/src/repl.rs | 138 ++++++++++++++++++++++++++- crates/nu-protocol/src/config.rs | 9 ++ docs/sample_config/default_config.nu | 2 +- 5 files changed, 149 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d3c029281..eb8d458fe 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2537,6 +2537,7 @@ dependencies = [ "reedline", "regex", "rstest", + "strip-ansi-escapes", "sysinfo", "thiserror", ] diff --git a/crates/nu-cli/Cargo.toml b/crates/nu-cli/Cargo.toml index 0f741f30d..7751fcc15 100644 --- a/crates/nu-cli/Cargo.toml +++ b/crates/nu-cli/Cargo.toml @@ -30,6 +30,7 @@ is_executable = "1.0.1" lazy_static = "1.4.0" log = "0.4" regex = "1.5.4" +strip-ansi-escapes = "0.1.1" sysinfo = "0.24.1" [features] diff --git a/crates/nu-cli/src/repl.rs b/crates/nu-cli/src/repl.rs index 5f5ffb28b..ab36d3261 100644 --- a/crates/nu-cli/src/repl.rs +++ b/crates/nu-cli/src/repl.rs @@ -14,12 +14,14 @@ use nu_parser::{lex, parse}; use nu_protocol::{ ast::PathMember, engine::{EngineState, Stack, StateWorkingSet}, - BlockId, HistoryFileFormat, PipelineData, PositionalArg, ShellError, Span, Type, Value, VarId, + format_duration, BlockId, HistoryFileFormat, PipelineData, PositionalArg, ShellError, Span, + Type, Value, VarId, }; use reedline::{DefaultHinter, Emacs, SqliteBackedHistory, Vi}; use regex::Regex; use std::io::{self, Write}; use std::{sync::atomic::Ordering, time::Instant}; +use strip_ansi_escapes::strip; use sysinfo::SystemExt; // According to Daniel Imms @Tyriar, we need to do these this way: @@ -119,6 +121,25 @@ pub fn evaluate_repl( let sys = sysinfo::System::new(); + let show_banner = config.show_banner; + let use_ansi = config.use_ansi_coloring; + if show_banner { + let banner = get_banner(engine_state, stack); + if use_ansi { + println!("{}", banner); + } else { + let stripped_string = { + if let Ok(bytes) = strip(&banner) { + String::from_utf8_lossy(&bytes).to_string() + } else { + banner + } + }; + + println!("{}", stripped_string); + } + } + loop { if is_perf_true { info!( @@ -460,6 +481,121 @@ pub fn evaluate_repl( Ok(()) } +fn get_banner(engine_state: &mut EngineState, stack: &mut Stack) -> String { + let age = match eval_string_with_input( + engine_state, + stack, + None, + "(date now) - ('05/10/2019' | into datetime)", + ) { + Ok(Value::Duration { val, .. }) => format_duration(val), + _ => "".to_string(), + }; + + let banner = format!( + r#"{} __ , +{} .--()°'.' {}Welcome to {}Nushell{}, +{}'|, . ,' {}based on the {}nu{} language, +{} !_-(_\ {}where all data is structured! + +Please join our {}Discord{} community at {}https://discord.gg/NtAbbGn{} +Our {}GitHub{} repository is at {}https://github.com/nushell/nushell{} +Our {}Documentation{} is located at {}http://nushell.sh{} +{}Tweet{} us at {}@nu_shell{} + +{}Nushell{} has been around for: +{} + +{}You can disable this banner using the {}config nu{}{} command +to modify the config.nu file and setting show_banner to false. + +let-env config {{ + show_banner: false + ... +}}{} +"#, + "\x1b[32m", //start line 1 green + "\x1b[32m", //start line 2 + "\x1b[0m", //before welcome + "\x1b[32m", //before nushell + "\x1b[0m", //after nushell + "\x1b[32m", //start line 3 + "\x1b[0m", //before based + "\x1b[32m", //before nu + "\x1b[0m", //after nu + "\x1b[32m", //start line 4 + "\x1b[0m", //before where + "\x1b[35m", //before Discord purple + "\x1b[0m", //after Discord + "\x1b[35m", //before Discord URL + "\x1b[0m", //after Discord URL + "\x1b[1;32m", //before GitHub green_bold + "\x1b[0m", //after GitHub + "\x1b[1;32m", //before GitHub URL + "\x1b[0m", //after GitHub URL + "\x1b[32m", //before Documentation + "\x1b[0m", //after Documentation + "\x1b[32m", //before Documentation URL + "\x1b[0m", //after Documentation URL + "\x1b[36m", //before Tweet blue + "\x1b[0m", //after Tweet + "\x1b[1;36m", //before @nu_shell cyan_bold + "\x1b[0m", //after @nu_shell + "\x1b[32m", //before Nushell + "\x1b[0m", //after Nushell + age, + "\x1b[2;37m", //before banner disable dim white + "\x1b[2;36m", //before config nu dim cyan + "\x1b[0m", //after config nu + "\x1b[2;37m", //after config nu dim white + "\x1b[0m", //after banner disable + ); + + banner +} + +// Taken from Nana's simple_eval +/// Evaluate a block of Nu code, optionally with input. +/// For example, source="$in * 2" will multiply the value in input by 2. +pub fn eval_string_with_input( + engine_state: &mut EngineState, + stack: &mut Stack, + input: Option, + source: &str, +) -> Result { + let (block, delta) = { + let mut working_set = StateWorkingSet::new(engine_state); + let (output, _) = parse( + &mut working_set, + Some("nana"), + source.as_bytes(), + false, + &[], + ); + + (output, working_set.render()) + }; + + if let Err(err) = engine_state.merge_delta(delta) { + return Err(err); + } + + let input_as_pipeline_data = match input { + Some(input) => PipelineData::Value(input, None), + None => PipelineData::new(Span::test_data()), + }; + + eval_block( + engine_state, + stack, + &block, + input_as_pipeline_data, + false, + true, + ) + .map(|x| x.into_value(Span::test_data())) +} + pub fn get_command_finished_marker(stack: &Stack, engine_state: &EngineState) -> String { let exit_code = stack .get_env_var(engine_state, "LAST_EXIT_CODE") diff --git a/crates/nu-protocol/src/config.rs b/crates/nu-protocol/src/config.rs index 03f104142..8c3946004 100644 --- a/crates/nu-protocol/src/config.rs +++ b/crates/nu-protocol/src/config.rs @@ -81,6 +81,7 @@ pub struct Config { pub case_sensitive_completions: bool, pub enable_external_completion: bool, pub trim_strategy: TrimStrategy, + pub show_banner: bool, } impl Default for Config { @@ -115,6 +116,7 @@ impl Default for Config { case_sensitive_completions: false, enable_external_completion: true, trim_strategy: TRIM_STRATEGY_DEFAULT, + show_banner: true, } } } @@ -388,6 +390,13 @@ impl Value { } } "table_trim" => config.trim_strategy = try_parse_trim_strategy(value, &config)?, + "show_banner" => { + if let Ok(b) = value.as_bool() { + config.show_banner = b; + } else { + eprintln!("$config.show_banner is not a bool") + } + } x => { eprintln!("$config.{} is an unknown config setting", x) } diff --git a/docs/sample_config/default_config.nu b/docs/sample_config/default_config.nu index e156b10f0..a225c8a9f 100644 --- a/docs/sample_config/default_config.nu +++ b/docs/sample_config/default_config.nu @@ -257,7 +257,6 @@ let-env config = { case_sensitive_completions: false # set to true to enable case-sensitive completions enable_external_completion: true # set to false to prevent nushell looking into $env.PATH to find more suggestions, `false` recommended for WSL users as this look up my be very slow max_external_completion_results: 100 # setting it lower can improve completion performance at the cost of omitting some options - # A strategy of managing table view in case of limited space. table_trim: { methodology: wrapping, # truncating @@ -266,6 +265,7 @@ let-env config = { # A suffix which will be used with 'truncating' methodology # truncating_suffix: "..." } + show_banner: true # true or false to enable or disable the banner hooks: { pre_prompt: [{