From 182d7ce7321a4f5da19dec984f55da9ce7d7338f Mon Sep 17 00:00:00 2001 From: ridiculousfish Date: Sat, 3 Nov 2018 13:30:55 -0700 Subject: [PATCH] Teach cd completions about logical paths Prior to this fix, cding into a symlink and then completing .. would complete from the physical directory instead of the logical directory, which could not actually be cd'd to. Teach cd completiond to use the logical directory. --- src/expand.h | 2 +- src/wildcard.cpp | 16 ++++++++++++++-- tests/cd.err | 3 +++ tests/cd.in | 29 +++++++++++++++++++++++++++++ tests/cd.out | 16 ++++++++++++++++ 5 files changed, 63 insertions(+), 3 deletions(-) diff --git a/src/expand.h b/src/expand.h index b6fac9364..f7336d405 100644 --- a/src/expand.h +++ b/src/expand.h @@ -45,7 +45,7 @@ enum { /// EXPAND_FUZZY_MATCH is set. EXPAND_NO_FUZZY_DIRECTORIES = 1 << 10, /// Do expansions specifically to support cd. This means using CDPATH as a list of potential - /// working directories. + /// working directories, and to use logical instead of physical paths. EXPAND_SPECIAL_FOR_CD = 1 << 11, /// Do expansions specifically for cd autosuggestion. This is to differentiate between cd /// completions and cd autosuggestions. diff --git a/src/wildcard.cpp b/src/wildcard.cpp index d0b65a229..295d9a1b0 100644 --- a/src/wildcard.cpp +++ b/src/wildcard.cpp @@ -574,6 +574,9 @@ class wildcard_expander_t { wcstring abs_path = this->working_directory; append_path_component(abs_path, filepath); + // We must normalize the path to allow 'cd ..' to operate on logical paths. + if (flags & EXPAND_SPECIAL_FOR_CD) abs_path = normalize_path(abs_path); + size_t before = this->resolved_completions->size(); if (wildcard_test_flags_then_complete(abs_path, filename, wildcard.c_str(), this->flags, this->resolved_completions)) { @@ -591,8 +594,7 @@ class wildcard_expander_t { } // Implement EXPAND_SPECIAL_FOR_CD_AUTOSUGGEST by descending the deepest unique - // hierarchy we - // can, and then appending any components to each new result. + // hierarchy we can, and then appending any components to each new result. // Only descend deepest unique for cd autosuggest and not for cd tab completion // (issue #4402). if (flags & EXPAND_SPECIAL_FOR_CD_AUTOSUGGEST) { @@ -613,6 +615,16 @@ class wildcard_expander_t { DIR *open_dir(const wcstring &base_dir) const { wcstring path = this->working_directory; append_path_component(path, base_dir); + if (flags & EXPAND_SPECIAL_FOR_CD) { + // cd operates on logical paths. + // for example, cd ../ should complete "without resolving symlinks". + path = normalize_path(path); + } else { + // Other commands operate on physical paths. + if (auto tmp = wrealpath(path)) { + path = tmp.acquire(); + } + } return wopendir(path); } diff --git a/tests/cd.err b/tests/cd.err index 3dbd252c2..80bc5c0e6 100644 --- a/tests/cd.err +++ b/tests/cd.err @@ -1,3 +1,6 @@ #################### # cd symlink non-resolution + +#################### +# cd symlink completion diff --git a/tests/cd.in b/tests/cd.in index 281ff94ec..21104a432 100644 --- a/tests/cd.in +++ b/tests/cd.in @@ -7,3 +7,32 @@ test "$PWD" = "$link" || echo "\$PWD != \$link:"\n "\$PWD: $PWD"\n "\$link: test (pwd) = "$link" || echo "(pwd) != \$link:"\n "\$PWD: "(pwd)\n "\$link: $link"\n test (pwd -P) = "$real" || echo "(pwd -P) != \$real:"\n "\$PWD: $PWD"\n "\$real: $real"\n test (pwd -P -L) = "$link" || echo "(pwd -P -L) != \$link:"\n "\$PWD: $PWD"\n "\$link: $link"\n + +logmsg cd symlink completion + +# Create a symlink and verify logical completion. +# create directory $base/through/the/looking/glass +# symlink $base/somewhere/teleport -> $base/through/the/looking/glass +# verify that .. completions work +set -l base /tmp/cdcomp_test/ +rm -Rf $base +mkdir -p $base +cd $base +mkdir -p $base/through/the/looking/glass + +mkdir -p $base/somewhere +mkdir $base/somewhere/a1 +mkdir $base/somewhere/a2 +mkdir $base/somewhere/a3 +touch $base/through/the/looking/b(seq 1 3) +mkdir $base/through/the/looking/d1 +mkdir $base/through/the/looking/d2 +mkdir $base/through/the/looking/d3 +ln -s $base/through/the/looking/glass $base/somewhere/rabbithole + +cd $base/somewhere/rabbithole +echo "ls:" +complete -C'ls ../' +echo "cd:" +complete -C'cd ../' +rm -Rf $base diff --git a/tests/cd.out b/tests/cd.out index 3dbd252c2..86ea3299c 100644 --- a/tests/cd.out +++ b/tests/cd.out @@ -1,3 +1,19 @@ #################### # cd symlink non-resolution + +#################### +# cd symlink completion +ls: +../b1 +../b2 +../b3 +../d1/ +../d2/ +../d3/ +../glass/ +cd: +../a1/ +../a2/ +../a3/ +../rabbithole/