From 43a3983d3624dae6ad4fd3e6f7a9cac031ea8865 Mon Sep 17 00:00:00 2001 From: Antoine Stevan <44101798+amtoine@users.noreply.github.com> Date: Wed, 10 May 2023 14:05:01 +0200 Subject: [PATCH] REFACTOR: move the banner from the `rust` source to the standard library (#8406) Related to: - #8311 - #8353 # Description with the new `$nu.startup-time` from #8353 and as mentionned in #8311, we are now able to fully move the `nushell` banner from the `rust` source base to the standard library. this PR - removes all the `rust` source code for the banner - rewrites a perfect clone of the banner to `std.nu`, called `std banner` - call `std banner` from `default_config.nu` # User-Facing Changes see the demo: https://asciinema.org/a/566521 - no config will show the banner (e.g. `cargo run --release -- --no-config-file`) - a custom config without the `if $env.config.show_banner` block and no call to `std banner` would never show the banner - a custom config with the block and `config.show_banner = true` will show the banner - a custom config with the block and `config.show_banner = false` will NOT show the banner # Tests + Formatting a new test line has been added to `tests.nu` to check the length of the `std banner` output. - :green_circle: `toolkit fmt` - :green_circle: `toolkit clippy` - :green_circle: `toolkit test` - :green_circle: `toolkit test stdlib` # After Submitting ``` $nothing ``` --------- Co-authored-by: Darren Schroeder <343840+fdncred@users.noreply.github.com> --- crates/nu-cli/src/repl.rs | 143 +++----------------- crates/nu-std/lib/dt.nu | 226 ++++++++++++++++++++++++++++++++ crates/nu-std/lib/mod.nu | 27 +++- crates/nu-std/src/lib.rs | 1 + crates/nu-std/tests/test_std.nu | 4 + src/run.rs | 1 + src/test_bins.rs | 5 +- 7 files changed, 281 insertions(+), 126 deletions(-) create mode 100644 crates/nu-std/lib/dt.nu diff --git a/crates/nu-cli/src/repl.rs b/crates/nu-cli/src/repl.rs index 62b60ea86..8088d51a0 100644 --- a/crates/nu-cli/src/repl.rs +++ b/crates/nu-cli/src/repl.rs @@ -11,13 +11,13 @@ use miette::{IntoDiagnostic, Result}; use nu_color_config::StyleComputer; use nu_command::hook::eval_hook; use nu_command::util::get_guaranteed_cwd; -use nu_engine::{convert_env_values, eval_block}; -use nu_parser::{lex, parse, trim_quotes_str}; +use nu_engine::convert_env_values; +use nu_parser::{lex, trim_quotes_str}; use nu_protocol::{ config::NuCursorShape, engine::{EngineState, Stack, StateWorkingSet}, - format_duration, report_error, report_error_new, HistoryFileFormat, PipelineData, ShellError, - Span, Spanned, Value, + report_error, report_error_new, HistoryFileFormat, PipelineData, ShellError, Span, Spanned, + Value, }; use nu_utils::utils::perf; use reedline::{CursorConfig, DefaultHinter, EditCommand, Emacs, SqliteBackedHistory, Vi}; @@ -43,6 +43,7 @@ pub fn evaluate_repl( stack: &mut Stack, nushell_path: &str, prerun_command: Option>, + load_std_lib: Option>, entire_start_time: Instant, ) -> Result<()> { use nu_command::hook; @@ -153,19 +154,8 @@ pub fn evaluate_repl( start_time = std::time::Instant::now(); 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 { - println!("{}", nu_utils::strip_ansi_string_likely(banner)); - } - } perf( - "get sysinfo/show banner", + "get sysinfo", start_time, file!(), line!(), @@ -185,6 +175,19 @@ pub fn evaluate_repl( engine_state.merge_env(stack, get_guaranteed_cwd(engine_state, stack))?; } + engine_state.set_startup_time(entire_start_time.elapsed().as_nanos() as i64); + + if load_std_lib.is_none() && engine_state.get_config().show_banner { + eval_source( + engine_state, + stack, + r#"use std banner; banner"#.as_bytes(), + "show_banner", + PipelineData::empty(), + false, + ); + } + loop { let loop_start_time = std::time::Instant::now(); @@ -445,16 +448,6 @@ pub fn evaluate_repl( entry_num += 1; - if entry_num == 1 { - engine_state.set_startup_time(entire_start_time.elapsed().as_nanos() as i64); - if show_banner { - println!( - "Startup Time: {}", - format_duration(engine_state.get_startup_time()) - ); - } - } - start_time = std::time::Instant::now(); let input = line_editor.read_line(prompt); let shell_integration = config.shell_integration; @@ -740,104 +733,6 @@ fn map_nucursorshape_to_cursorshape(shape: NuCursorShape) -> SetCursorStyle { } } -fn get_banner(engine_state: &mut EngineState, stack: &mut Stack) -> String { - let age = match eval_string_with_input( - engine_state, - stack, - None, - "(date now) - ('2019-05-10 09:59:12-0700' | 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 {}https://nushell.sh{} -{}Tweet{} us at {}@nu_shell{} -Learn how to remove this at: {}https://nushell.sh/book/configuration.html#remove-welcome-message{} - -It's been this long since {}Nushell{}'s first commit: -{}{} -"#, - "\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 Welcome Message - "\x1b[0m", //after Welcome Message - "\x1b[32m", //before Nushell - "\x1b[0m", //after Nushell - age, - "\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, None, source.as_bytes(), false); - - (output, working_set.render()) - }; - - engine_state.merge_delta(delta)?; - - let input_as_pipeline_data = match input { - Some(input) => PipelineData::Value(input, None), - None => PipelineData::empty(), - }; - - eval_block( - engine_state, - stack, - &block, - input_as_pipeline_data, - false, - true, - ) - .map(|x| x.into_value(Span::unknown())) -} - 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-std/lib/dt.nu b/crates/nu-std/lib/dt.nu new file mode 100644 index 000000000..c7432783a --- /dev/null +++ b/crates/nu-std/lib/dt.nu @@ -0,0 +1,226 @@ +def borrow-year [from: record, current: record] { + mut current = $current + + $current.year = $current.year - 1 + $current.month = $current.month + 12 + + $current +} + +def leap-year-days [year] { + if $year mod 400 == 0 { + 29 + } else if $year mod 4 == 0 and $year mod 100 != 0 { + 29 + } else { + 28 + } +} + +def borrow-month [from: record, current: record] { + mut current = $current + if $from.month in [1, 3, 5, 7, 8, 10, 12] { + $current.day = $current.day + 31 + $current.month = $current.month - 1 + if $current.month < 0 { + $current = (borrow-year $from $current) + } + } else if $from.month in [4, 6, 9, 11] { + $current.day = $current.day + 30 + $current.month = $current.month - 1 + if $current.month < 0 { + $current = (borrow-year $from $current) + } + } else { + # oh February + let num_days_feb = (leap-year-days $current.year) + $current.day = $current.day + $num_days_feb + $current.month = $current.month - 1 + if $current.month < 0 { + $current = (borrow-year $from $current) + } + } + + $current +} + +def borrow-day [from: record, current: record] { + mut current = $current + $current.hour = $current.hour + 24 + $current.day = $current.day - 1 + if $current.day < 0 { + $current = (borrow-month $from $current) + } + + $current +} + +def borrow-hour [from: record, current: record] { + mut current = $current + $current.minute = $current.minute + 60 + $current.hour = $current.hour - 1 + if $current.hour < 0 { + $current = (borrow-day $from $current) + } + + $current +} + +def borrow-minute [from: record, current: record] { + mut current = $current + $current.second = $current.second + 60 + $current.minute = $current.minute - 1 + if $current.minute < 0 { + $current = (borrow-hour $from $current) + } + + $current +} + +def borrow-second [from: record, current: record] { + mut current = $current + $current.millisecond = $current.millisecond + 1_000 + $current.second = $current.second - 1 + if $current.second < 0 { + $current = (borrow-minute $from $current) + } + + $current +} + +def borrow-millisecond [from: record, current: record] { + mut current = $current + $current.microsecond = $current.microsecond + 1_000_000 + $current.millisecond = $current.millisecond - 1 + if $current.millisecond < 0 { + $current = (borrow-second $from $current) + } + + $current +} + +def borrow-microsecond [from: record, current: record] { + mut current = $current + $current.nanosecond = $current.nanosecond + 1_000_000_000 + $current.microsecond = $current.microsecond - 1 + if $current.microsecond < 0 { + $current = (borrow-millisecond $from $current) + } + + $current +} + +# Subtract two datetimes and return a record with the difference +# Examples +# print (datetime-diff 2023-05-07T04:08:45+12:00 2019-05-10T09:59:12+12:00) +# print (datetime-diff (date now) 2019-05-10T09:59:12-07:00) +export def datetime-diff [from: datetime, to: datetime] { + let from_expanded = ($from | date to-timezone utc | date to-record | merge { millisecond: 0, microsecond: 0}) + let to_expanded = ($to | date to-timezone utc | date to-record | merge { millisecond: 0, microsecond: 0}) + + mut result = { year: ($from_expanded.year - $to_expanded.year), month: ($from_expanded.month - $to_expanded.month)} + + if $result.month < 0 { + $result = (borrow-year $from_expanded $result) + } + + $result.day = $from_expanded.day - $to_expanded.day + if $result.day < 0 { + $result = (borrow-month $from_expanded $result) + } + + $result.hour = $from_expanded.hour - $to_expanded.hour + if $result.hour < 0 { + $result = (borrow-day $from_expanded $result) + } + + $result.minute = $from_expanded.minute - $to_expanded.minute + if $result.minute < 0 { + $result = (borrow-hour $from_expanded $result) + } + + $result.second = $from_expanded.second - $to_expanded.second + if $result.second < 0 { + $result = (borrow-minute $from_expanded $result) + } + + $result.nanosecond = $from_expanded.nanosecond - $to_expanded.nanosecond + if $result.nanosecond < 0 { + $result = (borrow-second $from_expanded $result) + } + + $result.millisecond = ($result.nanosecond / 1_000_000 | into int) # don't want a decimal + $result.microsecond = (($result.nanosecond mod 1_000_000) / 1_000 | into int) + $result.nanosecond = ($result.nanosecond mod 1_000 | into int) + + $result +} + +export def pretty-print-duration [dur: duration] { + mut result = "" + if $dur.year > 0 { + if $dur.year > 1 { + $result = $"($dur.year)yrs " + } else { + $result = $"($dur.year)yr " + } + } + if $dur.month > 0 { + if $dur.month > 1 { + $result = $"($result)($dur.month)months " + } else { + $result = $"($result)($dur.month)month " + } + } + if $dur.day > 0 { + if $dur.day > 1 { + $result = $"($result)($dur.day)days " + } else { + $result = $"($result)($dur.day)day " + } + } + if $dur.hour > 0 { + if $dur.hour > 1 { + $result = $"($result)($dur.hour)hrs " + } else { + $result = $"($result)($dur.hour)hr " + } + } + if $dur.minute > 0 { + if $dur.minute > 1 { + $result = $"($result)($dur.minute)mins " + } else { + $result = $"($result)($dur.minute)min " + } + } + if $dur.second > 0 { + if $dur.second > 1 { + $result = $"($result)($dur.second)secs " + } else { + $result = $"($result)($dur.second)sec " + } + } + if $dur.millisecond > 0 { + if $dur.millisecond > 1 { + $result = $"($result)($dur.millisecond)ms " + } else { + $result = $"($result)($dur.millisecond)ms " + } + } + if $dur.microsecond > 0 { + if $dur.microsecond > 1 { + $result = $"($result)($dur.microsecond)µs " + } else { + $result = $"($result)($dur.microsecond)µs " + } + } + if $dur.nanosecond > 0 { + if $dur.nanosecond > 1 { + $result = $"($result)($dur.nanosecond)ns " + } else { + $result = $"($result)($dur.nanosecond)ns " + } + } + + $result +} diff --git a/crates/nu-std/lib/mod.nu b/crates/nu-std/lib/mod.nu index 2d303c669..42b036438 100644 --- a/crates/nu-std/lib/mod.nu +++ b/crates/nu-std/lib/mod.nu @@ -9,6 +9,7 @@ export use iter * export use log * export use testing * export use xml * +export use dt [datetime-diff, pretty-print-duration] # Add the given paths to the PATH. # @@ -171,7 +172,7 @@ def "from ns" [] { # > # ``` # -# > **Note** +# > **Note** # > `std bench --pretty` will return a `string`. # # # Examples @@ -224,3 +225,27 @@ export def bench [ $report } } + +# print a banner for nushell, with information about the project +# +# Example: +# an example can be found in [this asciinema recording](https://asciinema.org/a/566513) +export def banner [] { +let dt = (datetime-diff (date now) 2019-05-10T09:59:12-07:00) +$"(ansi green) __ ,(ansi reset) +(ansi green) .--\(\)°'.' (ansi reset)Welcome to (ansi green)Nushell(ansi reset), +(ansi green)'|, . ,' (ansi reset)based on the (ansi green)nu(ansi reset) language, +(ansi green) !_-\(_\\ (ansi reset)where all data is structured! + +Please join our (ansi purple)Discord(ansi reset) community at (ansi purple)https://discord.gg/NtAbbGn(ansi reset) +Our (ansi green_bold)GitHub(ansi reset) repository is at (ansi green_bold)https://github.com/nushell/nushell(ansi reset) +Our (ansi green)Documentation(ansi reset) is located at (ansi green)https://nushell.sh(ansi reset) +(ansi cyan)Tweet(ansi reset) us at (ansi cyan_bold)@nu_shell(ansi reset) +Learn how to remove this at: (ansi green)https://nushell.sh/book/configuration.html#remove-welcome-message(ansi reset) + +It's been this long since (ansi green)Nushell(ansi reset)'s first commit: +(pretty-print-duration $dt) + +Startup Time: ($nu.startup-time) +" +} diff --git a/crates/nu-std/src/lib.rs b/crates/nu-std/src/lib.rs index 863e67232..d81054873 100644 --- a/crates/nu-std/src/lib.rs +++ b/crates/nu-std/src/lib.rs @@ -78,6 +78,7 @@ pub fn load_standard_library( ("help", include_str!("../lib/help.nu")), ("testing", include_str!("../lib/testing.nu")), ("xml", include_str!("../lib/xml.nu")), + ("dt", include_str!("../lib/dt.nu")), ]; // Define commands to be preloaded into the default (top level, unprefixed) namespace. diff --git a/crates/nu-std/tests/test_std.nu b/crates/nu-std/tests/test_std.nu index 405b3e41a..fe5b8e733 100644 --- a/crates/nu-std/tests/test_std.nu +++ b/crates/nu-std/tests/test_std.nu @@ -22,3 +22,7 @@ export def test_path_add [] { assert equal $env.PATH ["fooooo", "foo", "bar", "baz"] } } + +export def test_banner [] { + std assert ((std banner | lines | length) == 15) +} diff --git a/src/run.rs b/src/run.rs index cc4a6d701..3a22c0bc9 100644 --- a/src/run.rs +++ b/src/run.rs @@ -234,6 +234,7 @@ pub(crate) fn run_repl( &mut stack, config_files::NUSHELL_FOLDER, parsed_nu_cli_args.execute, + parsed_nu_cli_args.no_std_lib, entire_start_time, ); perf( diff --git a/src/test_bins.rs b/src/test_bins.rs index 536465034..3f14519ca 100644 --- a/src/test_bins.rs +++ b/src/test_bins.rs @@ -6,6 +6,7 @@ use nu_engine::eval_block; use nu_parser::parse; use nu_protocol::engine::{EngineState, Stack, StateWorkingSet}; use nu_protocol::{CliError, PipelineData, Value}; +use nu_std::load_standard_library; // use nu_test_support::fs::in_directory; /// Echo's value of env keys from args @@ -175,10 +176,12 @@ pub fn nu_repl() { let mut engine_state = nu_cli::add_cli_context(create_default_context()); let mut stack = Stack::new(); - stack.add_env_var("PWD".to_string(), Value::test_string(cwd.to_string_lossy())); + engine_state.add_env_var("PWD".into(), Value::test_string(cwd.to_string_lossy())); let mut last_output = String::new(); + load_standard_library(&mut engine_state).expect("Could not load the standard library."); + for (i, line) in source_lines.iter().enumerate() { let cwd = nu_engine::env::current_dir(&engine_state, &stack) .unwrap_or_else(|err| outcome_err(&engine_state, &err));