diff --git a/crates/nu-parser/src/parse_keywords.rs b/crates/nu-parser/src/parse_keywords.rs index 8fd3cf16ff65..2d940b5b8544 100644 --- a/crates/nu-parser/src/parse_keywords.rs +++ b/crates/nu-parser/src/parse_keywords.rs @@ -2834,25 +2834,29 @@ pub fn parse_register( /// This helper function is used to find files during parsing /// -/// Checks whether the file is: -/// 1. relative to the directory of the file currently being parsed -/// 2. relative to the current working directory -/// 3. within one of the NU_LIB_DIRS entries +/// First, the actual current working directory is selected as +/// a) the directory of a file currently being parsed +/// b) current working directory (PWD) /// -/// Always returns absolute path +/// Then, if the file is not found in the actual cwd, NU_LIB_DIRS is checked. +/// If there is a relative path in NU_LIB_DIRS, it is assumed to be relative to the actual cwd +/// determined in the first step. +/// +/// Always returns an absolute path fn find_in_dirs( filename: &str, working_set: &StateWorkingSet, cwd: &str, dirs_env: &str, ) -> Option { - if let Some(currently_parsed_cwd) = &working_set.currently_parsed_cwd { - if let Ok(p) = canonicalize_with(filename, currently_parsed_cwd) { - Some(p) - } else { - None - } - } else if let Ok(p) = canonicalize_with(filename, cwd) { + // Choose whether to use file-relative or PWD-relative path + let actual_cwd = if let Some(currently_parsed_cwd) = &working_set.currently_parsed_cwd { + currently_parsed_cwd.as_path() + } else { + Path::new(cwd) + }; + + if let Ok(p) = canonicalize_with(filename, actual_cwd) { Some(p) } else { let path = Path::new(filename); @@ -2862,8 +2866,8 @@ fn find_in_dirs( if let Ok(dirs) = lib_dirs.as_list() { for lib_dir in dirs { if let Ok(dir) = lib_dir.as_path() { - if let Ok(dir_abs) = canonicalize_with(&dir, cwd) { - // make sure the dir is absolute path + // make sure the dir is absolute path + if let Ok(dir_abs) = canonicalize_with(&dir, actual_cwd) { if let Ok(path) = canonicalize_with(filename, dir_abs) { return Some(path); } diff --git a/tests/shell/mod.rs b/tests/shell/mod.rs index d4122fadf7ce..72e501fb651c 100644 --- a/tests/shell/mod.rs +++ b/tests/shell/mod.rs @@ -1,3 +1,6 @@ +use super::nu_repl::nu_repl; +use nu_test_support::fs::Stub::FileWithContentToBeTrimmed; +use nu_test_support::playground::Playground; use nu_test_support::{nu, pipeline}; #[cfg(feature = "which-support")] @@ -56,3 +59,115 @@ fn do_not_panic_if_broken_pipe() { assert!(child_output.stderr.is_empty()); } + +#[test] +fn nu_lib_dirs_repl() { + Playground::setup("nu_lib_dirs_repl", |dirs, sandbox| { + sandbox + .mkdir("scripts") + .with_files(vec![FileWithContentToBeTrimmed( + "scripts/foo.nu", + r#" + let-env FOO = "foo" + "#, + )]); + + let inp = &[ + r#"let-env NU_LIB_DIRS = [ ('scripts' | path expand) ]"#, + r#"source foo.nu"#, + r#"$env.FOO"#, + ]; + + let actual_repl = nu_repl(dirs.test().to_str().unwrap(), inp); + + assert!(actual_repl.err.is_empty()); + assert_eq!(actual_repl.out, "foo"); + }) +} + +#[test] +fn nu_lib_dirs_script() { + Playground::setup("nu_lib_dirs_script", |dirs, sandbox| { + sandbox + .mkdir("scripts") + .with_files(vec![FileWithContentToBeTrimmed( + "scripts/foo.nu", + r#" + let-env FOO = "foo" + "#, + )]) + .with_files(vec![FileWithContentToBeTrimmed( + "main.nu", + r#" + source foo.nu + "#, + )]); + + let inp = &[ + r#"let-env NU_LIB_DIRS = [ ('scripts' | path expand) ]"#, + r#"source main.nu"#, + r#"$env.FOO"#, + ]; + + let actual_repl = nu_repl(dirs.test().to_str().unwrap(), inp); + + assert!(actual_repl.err.is_empty()); + assert_eq!(actual_repl.out, "foo"); + }) +} + +#[test] +fn nu_lib_dirs_relative_repl() { + Playground::setup("nu_lib_dirs_relative_repl", |dirs, sandbox| { + sandbox + .mkdir("scripts") + .with_files(vec![FileWithContentToBeTrimmed( + "scripts/foo.nu", + r#" + let-env FOO = "foo" + "#, + )]); + + let inp = &[ + r#"let-env NU_LIB_DIRS = [ 'scripts' ]"#, + r#"source foo.nu"#, + r#"$env.FOO"#, + ]; + + let actual_repl = nu_repl(dirs.test().to_str().unwrap(), inp); + + assert!(actual_repl.err.is_empty()); + assert_eq!(actual_repl.out, "foo"); + }) +} + +#[test] +fn nu_lib_dirs_relative_script() { + Playground::setup("nu_lib_dirs_relative_script", |dirs, sandbox| { + sandbox + .mkdir("scripts") + .with_files(vec![FileWithContentToBeTrimmed( + "scripts/main.nu", + r#" + source ../foo.nu + "#, + )]) + .with_files(vec![FileWithContentToBeTrimmed( + "foo.nu", + r#" + let-env FOO = "foo" + "#, + )]); + + let inp = &[ + r#"let-env NU_LIB_DIRS = [ 'scripts' ]"#, + r#"source scripts/main.nu"#, + r#"$env.FOO"#, + ]; + + let actual_repl = nu_repl(dirs.test().to_str().unwrap(), inp); + + assert!(actual_repl.err.is_empty()); + assert_eq!(actual_repl.out, "foo"); + }) +}