diff --git a/crates/nu-cli/src/config_files.rs b/crates/nu-cli/src/config_files.rs index 3a02f75e86..e51b34cf7a 100644 --- a/crates/nu-cli/src/config_files.rs +++ b/crates/nu-cli/src/config_files.rs @@ -226,15 +226,8 @@ pub fn eval_config_contents( engine_state.file = prev_file; // Merge the environment in case env vars changed in the config - match engine_state.cwd(Some(stack)) { - Ok(cwd) => { - if let Err(e) = engine_state.merge_env(stack, cwd) { - report_error_new(engine_state, &e); - } - } - Err(e) => { - report_error_new(engine_state, &e); - } + if let Err(e) = engine_state.merge_env(stack) { + report_error_new(engine_state, &e); } } } diff --git a/crates/nu-cli/src/lib.rs b/crates/nu-cli/src/lib.rs index 6f151adad1..8be40d9354 100644 --- a/crates/nu-cli/src/lib.rs +++ b/crates/nu-cli/src/lib.rs @@ -20,7 +20,6 @@ pub use config_files::eval_config_contents; pub use eval_cmds::{evaluate_commands, EvaluateCommandsOpts}; pub use eval_file::evaluate_file; pub use menus::NuHelpCompleter; -pub use nu_cmd_base::util::get_init_cwd; pub use nu_highlight::NuHighlight; pub use print::Print; pub use prompt::NushellPrompt; diff --git a/crates/nu-cli/src/repl.rs b/crates/nu-cli/src/repl.rs index 5b0db96741..03ebb61982 100644 --- a/crates/nu-cli/src/repl.rs +++ b/crates/nu-cli/src/repl.rs @@ -15,10 +15,7 @@ use crate::{ use crossterm::cursor::SetCursorStyle; use log::{error, trace, warn}; use miette::{ErrReport, IntoDiagnostic, Result}; -use nu_cmd_base::{ - hook::eval_hook, - util::{get_editor, get_guaranteed_cwd}, -}; +use nu_cmd_base::{hook::eval_hook, util::get_editor}; use nu_color_config::StyleComputer; #[allow(deprecated)] use nu_engine::{convert_env_values, current_dir_str, env_to_strings}; @@ -111,8 +108,7 @@ pub fn evaluate_repl( PipelineData::empty(), false, ); - let cwd = get_guaranteed_cwd(engine_state, &unique_stack); - engine_state.merge_env(&mut unique_stack, cwd)?; + engine_state.merge_env(&mut unique_stack)?; } let hostname = System::host_name(); @@ -260,12 +256,10 @@ fn loop_iteration(ctx: LoopContext) -> (bool, Stack, Reedline) { hostname, } = ctx; - let cwd = get_guaranteed_cwd(engine_state, &stack); - let mut start_time = std::time::Instant::now(); // Before doing anything, merge the environment from the previous REPL iteration into the // permanent state. - if let Err(err) = engine_state.merge_env(&mut stack, cwd) { + if let Err(err) = engine_state.merge_env(&mut stack) { report_error_new(engine_state, &err); } perf!("merge env", start_time, use_color); diff --git a/crates/nu-cli/tests/completions/mod.rs b/crates/nu-cli/tests/completions/mod.rs index a7f88dc3b0..47f16da8a1 100644 --- a/crates/nu-cli/tests/completions/mod.rs +++ b/crates/nu-cli/tests/completions/mod.rs @@ -18,11 +18,11 @@ use support::{ #[fixture] fn completer() -> NuCompleter { // Create a new engine - let (dir, _, mut engine, mut stack) = new_engine(); + let (_, _, mut engine, mut stack) = new_engine(); // Add record value as example let record = "def tst [--mod -s] {}"; - assert!(support::merge_input(record.as_bytes(), &mut engine, &mut stack, dir).is_ok()); + assert!(support::merge_input(record.as_bytes(), &mut engine, &mut stack).is_ok()); // Instantiate a new completer NuCompleter::new(Arc::new(engine), Arc::new(stack)) @@ -31,12 +31,12 @@ fn completer() -> NuCompleter { #[fixture] fn completer_strings() -> NuCompleter { // Create a new engine - let (dir, _, mut engine, mut stack) = new_engine(); + let (_, _, mut engine, mut stack) = new_engine(); // Add record value as example let record = r#"def animals [] { ["cat", "dog", "eel" ] } def my-command [animal: string@animals] { print $animal }"#; - assert!(support::merge_input(record.as_bytes(), &mut engine, &mut stack, dir).is_ok()); + assert!(support::merge_input(record.as_bytes(), &mut engine, &mut stack).is_ok()); // Instantiate a new completer NuCompleter::new(Arc::new(engine), Arc::new(stack)) @@ -45,7 +45,7 @@ fn completer_strings() -> NuCompleter { #[fixture] fn extern_completer() -> NuCompleter { // Create a new engine - let (dir, _, mut engine, mut stack) = new_engine(); + let (_, _, mut engine, mut stack) = new_engine(); // Add record value as example let record = r#" @@ -56,7 +56,7 @@ fn extern_completer() -> NuCompleter { -b: string@animals ] "#; - assert!(support::merge_input(record.as_bytes(), &mut engine, &mut stack, dir).is_ok()); + assert!(support::merge_input(record.as_bytes(), &mut engine, &mut stack).is_ok()); // Instantiate a new completer NuCompleter::new(Arc::new(engine), Arc::new(stack)) @@ -65,7 +65,7 @@ fn extern_completer() -> NuCompleter { #[fixture] fn custom_completer() -> NuCompleter { // Create a new engine - let (dir, _, mut engine, mut stack) = new_engine(); + let (_, _, mut engine, mut stack) = new_engine(); // Add record value as example let record = r#" @@ -79,7 +79,7 @@ fn custom_completer() -> NuCompleter { completer: $external_completer } "#; - assert!(support::merge_input(record.as_bytes(), &mut engine, &mut stack, dir).is_ok()); + assert!(support::merge_input(record.as_bytes(), &mut engine, &mut stack).is_ok()); // Instantiate a new completer NuCompleter::new(Arc::new(engine), Arc::new(stack)) @@ -801,11 +801,11 @@ fn folder_with_directorycompletions() { #[test] fn variables_completions() { // Create a new engine - let (dir, _, mut engine, mut stack) = new_engine(); + let (_, _, mut engine, mut stack) = new_engine(); // Add record value as example let record = "let actor = { name: 'Tom Hardy', age: 44 }"; - assert!(support::merge_input(record.as_bytes(), &mut engine, &mut stack, dir).is_ok()); + assert!(support::merge_input(record.as_bytes(), &mut engine, &mut stack).is_ok()); // Instantiate a new completer let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack)); @@ -916,11 +916,11 @@ fn variables_completions() { #[test] fn alias_of_command_and_flags() { - let (dir, _, mut engine, mut stack) = new_engine(); + let (_, _, mut engine, mut stack) = new_engine(); // Create an alias let alias = r#"alias ll = ls -l"#; - assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack, dir).is_ok()); + assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack).is_ok()); let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack)); @@ -935,11 +935,11 @@ fn alias_of_command_and_flags() { #[test] fn alias_of_basic_command() { - let (dir, _, mut engine, mut stack) = new_engine(); + let (_, _, mut engine, mut stack) = new_engine(); // Create an alias let alias = r#"alias ll = ls "#; - assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack, dir).is_ok()); + assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack).is_ok()); let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack)); @@ -954,14 +954,14 @@ fn alias_of_basic_command() { #[test] fn alias_of_another_alias() { - let (dir, _, mut engine, mut stack) = new_engine(); + let (_, _, mut engine, mut stack) = new_engine(); // Create an alias let alias = r#"alias ll = ls -la"#; - assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack, dir.clone()).is_ok()); + assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack).is_ok()); // Create the second alias let alias = r#"alias lf = ll -f"#; - assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack, dir).is_ok()); + assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack).is_ok()); let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack)); @@ -978,7 +978,7 @@ fn run_external_completion(completer: &str, input: &str) -> Vec { let completer = format!("$env.config.completions.external.completer = {completer}"); // Create a new engine - let (dir, _, mut engine_state, mut stack) = new_engine(); + let (_, _, mut engine_state, mut stack) = new_engine(); let (block, delta) = { let mut working_set = StateWorkingSet::new(&engine_state); let block = parse(&mut working_set, None, completer.as_bytes(), false); @@ -994,7 +994,7 @@ fn run_external_completion(completer: &str, input: &str) -> Vec { ); // Merge environment into the permanent state - assert!(engine_state.merge_env(&mut stack, &dir).is_ok()); + assert!(engine_state.merge_env(&mut stack).is_ok()); // Instantiate a new completer let mut completer = NuCompleter::new(Arc::new(engine_state), Arc::new(stack)); @@ -1172,11 +1172,11 @@ fn custom_completer_triggers_cursor_after_word(mut custom_completer: NuCompleter #[ignore = "was reverted, still needs fixing"] #[rstest] fn alias_offset_bug_7648() { - let (dir, _, mut engine, mut stack) = new_engine(); + let (_, _, mut engine, mut stack) = new_engine(); // Create an alias let alias = r#"alias ea = ^$env.EDITOR /tmp/test.s"#; - assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack, dir).is_ok()); + assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack).is_ok()); let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack)); @@ -1191,11 +1191,11 @@ fn alias_offset_bug_7648() { #[ignore = "was reverted, still needs fixing"] #[rstest] fn alias_offset_bug_7754() { - let (dir, _, mut engine, mut stack) = new_engine(); + let (_, _, mut engine, mut stack) = new_engine(); // Create an alias let alias = r#"alias ll = ls -l"#; - assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack, dir).is_ok()); + assert!(support::merge_input(alias.as_bytes(), &mut engine, &mut stack).is_ok()); let mut completer = NuCompleter::new(Arc::new(engine), Arc::new(stack)); diff --git a/crates/nu-cli/tests/completions/support/completions_helpers.rs b/crates/nu-cli/tests/completions/support/completions_helpers.rs index 027ac9d997..f6daf1223d 100644 --- a/crates/nu-cli/tests/completions/support/completions_helpers.rs +++ b/crates/nu-cli/tests/completions/support/completions_helpers.rs @@ -62,7 +62,7 @@ pub fn new_engine() -> (PathBuf, String, EngineState, Stack) { ); // Merge environment into the permanent state - let merge_result = engine_state.merge_env(&mut stack, &dir); + let merge_result = engine_state.merge_env(&mut stack); assert!(merge_result.is_ok()); (dir, dir_str, engine_state, stack) @@ -143,7 +143,7 @@ pub fn new_quote_engine() -> (PathBuf, String, EngineState, Stack) { ); // Merge environment into the permanent state - let merge_result = engine_state.merge_env(&mut stack, &dir); + let merge_result = engine_state.merge_env(&mut stack); assert!(merge_result.is_ok()); (dir, dir_str, engine_state, stack) @@ -178,7 +178,7 @@ pub fn new_partial_engine() -> (PathBuf, String, EngineState, Stack) { ); // Merge environment into the permanent state - let merge_result = engine_state.merge_env(&mut stack, &dir); + let merge_result = engine_state.merge_env(&mut stack); assert!(merge_result.is_ok()); (dir, dir_str, engine_state, stack) @@ -223,7 +223,6 @@ pub fn merge_input( input: &[u8], engine_state: &mut EngineState, stack: &mut Stack, - dir: PathBuf, ) -> Result<(), ShellError> { let (block, delta) = { let mut working_set = StateWorkingSet::new(engine_state); @@ -246,5 +245,5 @@ pub fn merge_input( .is_ok()); // Merge environment into the permanent state - engine_state.merge_env(stack, &dir) + engine_state.merge_env(stack) } diff --git a/crates/nu-cmd-base/src/hook.rs b/crates/nu-cmd-base/src/hook.rs index cef5348618..bc2db8eef9 100644 --- a/crates/nu-cmd-base/src/hook.rs +++ b/crates/nu-cmd-base/src/hook.rs @@ -1,4 +1,3 @@ -use crate::util::get_guaranteed_cwd; use miette::Result; use nu_engine::{eval_block, eval_block_with_early_return}; use nu_parser::parse; @@ -284,8 +283,7 @@ pub fn eval_hook( } } - let cwd = get_guaranteed_cwd(engine_state, stack); - engine_state.merge_env(stack, cwd)?; + engine_state.merge_env(stack)?; Ok(output) } diff --git a/crates/nu-cmd-base/src/util.rs b/crates/nu-cmd-base/src/util.rs index 905c990a9e..a2af4d4773 100644 --- a/crates/nu-cmd-base/src/util.rs +++ b/crates/nu-cmd-base/src/util.rs @@ -2,21 +2,7 @@ use nu_protocol::{ engine::{EngineState, Stack}, Range, ShellError, Span, Value, }; -use std::{ops::Bound, path::PathBuf}; - -pub fn get_init_cwd() -> PathBuf { - std::env::current_dir().unwrap_or_else(|_| { - std::env::var("PWD") - .map(Into::into) - .unwrap_or_else(|_| nu_path::home_dir().unwrap_or_default()) - }) -} - -pub fn get_guaranteed_cwd(engine_state: &EngineState, stack: &Stack) -> PathBuf { - engine_state - .cwd(Some(stack)) - .unwrap_or(crate::util::get_init_cwd()) -} +use std::ops::Bound; type MakeRangeError = fn(&str, Span) -> ShellError; diff --git a/crates/nu-cmd-lang/src/example_support.rs b/crates/nu-cmd-lang/src/example_support.rs index bb03bbaf8c..d938729fef 100644 --- a/crates/nu-cmd-lang/src/example_support.rs +++ b/crates/nu-cmd-lang/src/example_support.rs @@ -138,7 +138,7 @@ pub fn check_example_evaluates_to_expected_output( stack.add_env_var("PWD".to_string(), Value::test_string(cwd.to_string_lossy())); engine_state - .merge_env(&mut stack, cwd) + .merge_env(&mut stack) .expect("Error merging environment"); let empty_input = PipelineData::empty(); diff --git a/crates/nu-command/src/filesystem/cd.rs b/crates/nu-command/src/filesystem/cd.rs index 4e09d900d2..91c43435db 100644 --- a/crates/nu-command/src/filesystem/cd.rs +++ b/crates/nu-command/src/filesystem/cd.rs @@ -1,4 +1,3 @@ -use nu_cmd_base::util::get_init_cwd; use nu_engine::command_prelude::*; use nu_utils::filesystem::{have_permission, PermissionResult}; @@ -41,9 +40,11 @@ impl Command for Cd { let physical = call.has_flag(engine_state, stack, "physical")?; let path_val: Option> = call.opt(engine_state, stack, 0)?; - // If getting PWD failed, default to the initial directory. This way, the - // user can use `cd` to recover PWD to a good state. - let cwd = engine_state.cwd(Some(stack)).unwrap_or(get_init_cwd()); + // If getting PWD failed, default to the home directory. The user can + // use `cd` to reset PWD to a good state. + let cwd = engine_state + .cwd(Some(stack)) + .unwrap_or(nu_path::home_dir().unwrap_or_default()); let path_val = { if let Some(path) = path_val { diff --git a/crates/nu-command/src/filesystem/start.rs b/crates/nu-command/src/filesystem/start.rs index 87aa34cf72..9b0fb6d84f 100644 --- a/crates/nu-command/src/filesystem/start.rs +++ b/crates/nu-command/src/filesystem/start.rs @@ -58,9 +58,8 @@ impl Command for Start { open_path(url.as_str(), engine_state, stack, path.span)?; } else { // try to distinguish between file not found and opening url without prefix - if let Ok(canon_path) = - canonicalize_with(path_no_whitespace, std::env::current_dir()?.as_path()) - { + let cwd = engine_state.cwd(Some(stack))?; + if let Ok(canon_path) = canonicalize_with(path_no_whitespace, cwd) { open_path(canon_path, engine_state, stack, path.span)?; } else { // open crate does not allow opening URL without prefix diff --git a/crates/nu-protocol/src/engine/engine_state.rs b/crates/nu-protocol/src/engine/engine_state.rs index e393d78d41..90af0f785e 100644 --- a/crates/nu-protocol/src/engine/engine_state.rs +++ b/crates/nu-protocol/src/engine/engine_state.rs @@ -295,11 +295,7 @@ impl EngineState { } /// Merge the environment from the runtime Stack into the engine state - pub fn merge_env( - &mut self, - stack: &mut Stack, - cwd: impl AsRef, - ) -> Result<(), ShellError> { + pub fn merge_env(&mut self, stack: &mut Stack) -> Result<(), ShellError> { let mut config_updated = false; for mut scope in stack.env_vars.drain(..) { @@ -329,9 +325,6 @@ impl EngineState { } } - // TODO: better error - std::env::set_current_dir(cwd)?; - if config_updated { // Make plugin GC config changes take effect immediately. #[cfg(feature = "plugin")] diff --git a/crates/nu-std/src/lib.rs b/crates/nu-std/src/lib.rs index a20e3fc5da..e6c8f30556 100644 --- a/crates/nu-std/src/lib.rs +++ b/crates/nu-std/src/lib.rs @@ -98,8 +98,7 @@ use std pwd eval_block::(engine_state, &mut stack, &block, pipeline_data)?; - let cwd = engine_state.cwd(Some(&stack))?; - engine_state.merge_env(&mut stack, cwd)?; + engine_state.merge_env(&mut stack)?; Ok(()) } diff --git a/src/config_files.rs b/src/config_files.rs index 30977a6d0e..913a332d38 100644 --- a/src/config_files.rs +++ b/src/config_files.rs @@ -165,15 +165,8 @@ pub(crate) fn read_default_env_file(engine_state: &mut EngineState, stack: &mut ); // Merge the environment in case env vars changed in the config - match engine_state.cwd(Some(stack)) { - Ok(cwd) => { - if let Err(e) = engine_state.merge_env(stack, cwd) { - report_error_new(engine_state, &e); - } - } - Err(e) => { - report_error_new(engine_state, &e); - } + if let Err(e) = engine_state.merge_env(stack) { + report_error_new(engine_state, &e); } } @@ -243,15 +236,8 @@ fn eval_default_config( ); // Merge the environment in case env vars changed in the config - match engine_state.cwd(Some(stack)) { - Ok(cwd) => { - if let Err(e) = engine_state.merge_env(stack, cwd) { - report_error_new(engine_state, &e); - } - } - Err(e) => { - report_error_new(engine_state, &e); - } + if let Err(e) = engine_state.merge_env(stack) { + report_error_new(engine_state, &e); } } diff --git a/src/main.rs b/src/main.rs index 2c0c4d5e4b..cd23a35ce7 100644 --- a/src/main.rs +++ b/src/main.rs @@ -21,7 +21,6 @@ use command::gather_commandline_args; use log::{trace, Level}; use miette::Result; use nu_cli::gather_parent_env_vars; -use nu_cmd_base::util::get_init_cwd; use nu_lsp::LanguageServer; use nu_path::canonicalize_with; use nu_protocol::{ @@ -44,6 +43,27 @@ fn get_engine_state() -> EngineState { nu_explore::add_explore_context(engine_state) } +/// Get the directory where the Nushell executable is located. +fn current_exe_directory() -> PathBuf { + let mut path = std::env::current_exe().expect("current_exe() should succeed"); + path.pop(); + path +} + +/// Get the current working directory from the environment. +fn current_dir_from_environment() -> PathBuf { + if let Ok(cwd) = std::env::current_dir() { + return cwd; + } + if let Ok(cwd) = std::env::var("PWD") { + return cwd.into(); + } + if let Some(home) = nu_path::home_dir() { + return home; + } + current_exe_directory() +} + fn main() -> Result<()> { let entire_start_time = std::time::Instant::now(); let mut start_time = std::time::Instant::now(); @@ -54,10 +74,11 @@ fn main() -> Result<()> { miette_hook(x); })); - // Get initial current working directory. - let init_cwd = get_init_cwd(); let mut engine_state = get_engine_state(); + // Get the current working directory from the environment. + let init_cwd = current_dir_from_environment(); + // Custom additions let delta = { let mut working_set = nu_protocol::engine::StateWorkingSet::new(&engine_state); @@ -318,6 +339,12 @@ fn main() -> Result<()> { _ => std::process::exit(1), } std::process::exit(0) + } else { + // If we're not running a testbin, set the current working directory to + // the location of the Nushell executable. This prevents the OS from + // locking the directory where the user launched Nushell. + std::env::set_current_dir(current_exe_directory()) + .expect("set_current_dir() should succeed"); } perf!("run test_bins", start_time, use_color); diff --git a/src/test_bins.rs b/src/test_bins.rs index b20c01f997..7cd542e732 100644 --- a/src/test_bins.rs +++ b/src/test_bins.rs @@ -251,13 +251,9 @@ pub fn nu_repl() { for (i, line) in source_lines.iter().enumerate() { let mut stack = Stack::with_parent(top_stack.clone()); - let cwd = engine_state - .cwd(Some(&stack)) - .unwrap_or_else(|err| outcome_err(&engine_state, &err)); - // Before doing anything, merge the environment from the previous REPL iteration into the // permanent state. - if let Err(err) = engine_state.merge_env(&mut stack, &cwd) { + if let Err(err) = engine_state.merge_env(&mut stack) { outcome_err(&engine_state, &err); }