diff --git a/crates/nu-cli/src/shell/filesystem_shell.rs b/crates/nu-cli/src/shell/filesystem_shell.rs index 5d3a7980ac..9c39f13e36 100644 --- a/crates/nu-cli/src/shell/filesystem_shell.rs +++ b/crates/nu-cli/src/shell/filesystem_shell.rs @@ -14,7 +14,7 @@ use nu_parser::ExpandContext; use nu_protocol::{Primitive, ReturnSuccess, UntaggedValue}; use rustyline::completion::FilenameCompleter; use rustyline::hint::{Hinter, HistoryHinter}; -use std::path::{Path, PathBuf}; +use std::path::{Component, Path, PathBuf}; use std::sync::atomic::Ordering; use trash as SendToTrash; @@ -78,6 +78,29 @@ impl FilesystemShell { hinter: HistoryHinter {}, } } + + fn canonicalize(&self, path: impl AsRef) -> std::io::Result { + let path = if path.as_ref().is_relative() { + let components = path.as_ref().components(); + let mut result = PathBuf::from(self.path()); + for component in components { + match component { + Component::CurDir => { /* ignore current dir */ } + Component::ParentDir => { + result.pop(); + } + Component::Normal(normal) => result.push(normal), + _ => {} + } + } + + result + } else { + path.as_ref().into() + }; + + dunce::canonicalize(path) + } } impl Shell for FilesystemShell { @@ -201,11 +224,10 @@ impl Shell for FilesystemShell { Some(v) => { let target = v.as_path()?; - if PathBuf::from("-") == target { + if target == Path::new("-") { PathBuf::from(&self.last_path) } else { - let path = PathBuf::from(self.path()); - let path = dunce::canonicalize(path.join(&target)).map_err(|_| { + let path = self.canonicalize(target).map_err(|_| { ShellError::labeled_error( "Cannot change to directory", "directory not found", diff --git a/crates/nu-cli/tests/commands/cd.rs b/crates/nu-cli/tests/commands/cd.rs index 24ac6eb2a4..e0fc4b0b67 100644 --- a/crates/nu-cli/tests/commands/cd.rs +++ b/crates/nu-cli/tests/commands/cd.rs @@ -85,6 +85,28 @@ fn filesystem_change_current_directory_to_parent_directory() { }) } +#[test] +fn filesystem_change_current_directory_to_parent_directory_after_delete_cwd() { + Playground::setup("cd_test_5_1", |dirs, sandbox| { + sandbox.within("foo").mkdir("bar"); + + let actual = nu!( + cwd: dirs.test().join("foo/bar"), + r#" + rm {}/foo/bar + echo "," + cd .. + pwd | echo $it + "#, + dirs.test() + ); + + let actual = actual.split(',').nth(1).unwrap(); + + assert_eq!(PathBuf::from(actual), *dirs.test().join("foo")); + }) +} + #[test] fn filesystem_change_to_home_directory() { Playground::setup("cd_test_6", |dirs, _| {