From 7e949595bdd31f197894580daf8ea84cd2ce51ca Mon Sep 17 00:00:00 2001 From: StevenDoesStuffs Date: Wed, 8 Mar 2023 20:59:33 -0600 Subject: [PATCH] Add is-interactive and is-login to NuVariable and allow running scripts with -i (#8306) Add two rows in `$nu`, `$nu.is-interactive` and `$nu.is-login`, which are true when nu is run in interactive and login mode respectively. The `-i` flag now behaves a bit more like that of bash's, where the any provided command or file is run without REPL but in "interactive mode". This should entail sourcing interactive-mode config files, but since we are planning on overhauling the config system soon, I'm holding off on that. For now, all `-i` does is set `$nu.is-interactive` to be true. About testing, I can't seem to find where cli-args get tested, so I haven't written any new tests for this. Also I don't think there are any docs that need updating. However if I'm wrong please tell me. --- crates/nu-cli/tests/completions.rs | 4 +- crates/nu-engine/src/nu_variable.rs | 11 ++++ crates/nu-protocol/src/engine/engine_state.rs | 4 ++ src/main.rs | 11 ++-- tests/shell/mod.rs | 56 +++++++++++++++++++ 5 files changed, 80 insertions(+), 6 deletions(-) diff --git a/crates/nu-cli/tests/completions.rs b/crates/nu-cli/tests/completions.rs index bfe7fb5d1..bbb56cb30 100644 --- a/crates/nu-cli/tests/completions.rs +++ b/crates/nu-cli/tests/completions.rs @@ -524,13 +524,15 @@ fn variables_completions() { // Test completions for $nu let suggestions = completer.complete("$nu.", 4); - assert_eq!(9, suggestions.len()); + assert_eq!(11, suggestions.len()); let expected: Vec = vec![ "config-path".into(), "env-path".into(), "history-path".into(), "home-path".into(), + "is-interactive".into(), + "is-login".into(), "loginshell-path".into(), "os-info".into(), "pid".into(), diff --git a/crates/nu-engine/src/nu_variable.rs b/crates/nu-engine/src/nu_variable.rs index fe7b481d3..dc6677064 100644 --- a/crates/nu-engine/src/nu_variable.rs +++ b/crates/nu-engine/src/nu_variable.rs @@ -37,6 +37,9 @@ impl LazyRecord for NuVariable { cols.push("pid"); cols.push("os-info"); + cols.push("is-interactive"); + cols.push("is-login"); + cols } @@ -177,6 +180,14 @@ impl LazyRecord for NuVariable { Ok(os_record) } + "is-interactive" => Ok(Value::Bool { + val: self.engine_state.is_interactive, + span: self.span, + }), + "is-login" => Ok(Value::Bool { + val: self.engine_state.is_login, + span: self.span, + }), _ => err(&format!("Could not find column '{column}'")), } } diff --git a/crates/nu-protocol/src/engine/engine_state.rs b/crates/nu-protocol/src/engine/engine_state.rs index 298ea3b99..6a1666743 100644 --- a/crates/nu-protocol/src/engine/engine_state.rs +++ b/crates/nu-protocol/src/engine/engine_state.rs @@ -145,6 +145,8 @@ pub struct EngineState { // If Nushell was started, e.g., with `nu spam.nu`, the file's parent is stored here pub currently_parsed_cwd: Option, pub regex_cache: Arc>>, + pub is_interactive: bool, + pub is_login: bool, } // The max number of compiled regexes to keep around in a LRU cache, arbitrarily chosen @@ -195,6 +197,8 @@ impl EngineState { regex_cache: Arc::new(Mutex::new(LruCache::new( NonZeroUsize::new(REGEX_CACHE_SIZE).expect("tried to create cache of size zero"), ))), + is_interactive: false, + is_login: false, } } diff --git a/src/main.rs b/src/main.rs index 813a71791..ce0dda984 100644 --- a/src/main.rs +++ b/src/main.rs @@ -63,6 +63,9 @@ fn main() -> Result<()> { let parsed_nu_cli_args = parse_commandline_args(&args_to_nushell.join(" "), &mut engine_state) .unwrap_or_else(|_| std::process::exit(1)); + engine_state.is_interactive = parsed_nu_cli_args.interactive_shell.is_some(); + engine_state.is_login = parsed_nu_cli_args.login_shell.is_some(); + let use_color = engine_state.get_config().use_ansi_coloring; if let Some(level) = parsed_nu_cli_args .log_level @@ -122,10 +125,7 @@ fn main() -> Result<()> { start_time = std::time::Instant::now(); // keep this condition in sync with the branches below - acquire_terminal( - parsed_nu_cli_args.commands.is_none() - && (script_name.is_empty() || parsed_nu_cli_args.interactive_shell.is_some()), - ); + acquire_terminal(parsed_nu_cli_args.commands.is_none() && script_name.is_empty()); perf( "acquire_terminal", start_time, @@ -234,7 +234,7 @@ fn main() -> Result<()> { &commands, input, ) - } else if !script_name.is_empty() && parsed_nu_cli_args.interactive_shell.is_none() { + } else if !script_name.is_empty() { run_file( &mut engine_state, parsed_nu_cli_args, @@ -244,6 +244,7 @@ fn main() -> Result<()> { input, ) } else { + engine_state.is_interactive = true; run_repl(engine_state, parsed_nu_cli_args, entire_start_time) } } diff --git a/tests/shell/mod.rs b/tests/shell/mod.rs index 3e16a51f5..91406a82d 100644 --- a/tests/shell/mod.rs +++ b/tests/shell/mod.rs @@ -199,3 +199,59 @@ fn run_export_extern() { assert!(actual.out.contains("Usage")); }) } + +#[test] +fn run_in_login_mode() { + let child_output = std::process::Command::new("sh") + .arg("-c") + .arg(format!( + "{:?} -l -c 'echo $nu.is-login'", + nu_test_support::fs::executable_path() + )) + .output() + .expect("true"); + + assert!(child_output.stderr.is_empty()); +} + +#[test] +fn run_in_not_login_mode() { + let child_output = std::process::Command::new("sh") + .arg("-c") + .arg(format!( + "{:?} -c 'echo $nu.is-login'", + nu_test_support::fs::executable_path() + )) + .output() + .expect("false"); + + assert!(child_output.stderr.is_empty()); +} + +#[test] +fn run_in_interactive_mode() { + let child_output = std::process::Command::new("sh") + .arg("-c") + .arg(format!( + "{:?} -i -c 'echo $nu.is-interactive'", + nu_test_support::fs::executable_path() + )) + .output() + .expect("true"); + + assert!(child_output.stderr.is_empty()); +} + +#[test] +fn run_in_noninteractive_mode() { + let child_output = std::process::Command::new("sh") + .arg("-c") + .arg(format!( + "{:?} -c 'echo $nu.is-interactive'", + nu_test_support::fs::executable_path() + )) + .output() + .expect("false"); + + assert!(child_output.stderr.is_empty()); +}