implement cdh command

Fixes #2847
This commit is contained in:
Kurtis Rader 2017-07-04 22:30:10 -07:00
parent 6b92f830ff
commit 8cc4639ea6
9 changed files with 139 additions and 1 deletions

View file

@ -8,6 +8,7 @@
- `string unescape` has been implemented to reverse the effects of `string escape` (#3543).
- The history file can now be specified by setting the `FISH_HISTORY` variable (#102).
- Read history is now controlled by the `FISH_HISTORY` variable rather than the `--mode-name` flag (#1504).
- Implement a `cdh` (change directory using recent history) command to provide a more friendly alternative to prevd/nextd and pushd/popd (#2847).
## Other significant changes

View file

@ -14,7 +14,7 @@ If `DIRECTORY` is a relative path, the paths found in the `CDPATH` environment v
Note that the shell will attempt to change directory without requiring `cd` if the name of a directory is provided (starting with `.`, `/` or `~`, or ending with `/`).
Fish also ships a wrapper function around the builtin `cd` that understands `cd -` as changing to the previous directory. See also <a href="commands.html#prevd">`prevd`</a>. This wrapper function maintains a history of the 25 most recently visited directories in the `$dirprev` and `$dirnext` global variables.
Fish also ships a wrapper function around the builtin `cd` that understands `cd -` as changing to the previous directory. See also <a href="commands.html#prevd">`prevd`</a>. This wrapper function maintains a history of the 25 most recently visited directories in the `$dirprev` and `$dirnext` global variables. If you make those universal variables your `cd` history is shared among all fish instances.
\subsection cd-example Examples
@ -25,3 +25,7 @@ cd
cd /usr/src/fish-shell
# changes the working directory to /usr/src/fish-shell
\endfish
\subsection cd-see-also See Also
See also the <a href="commands.html#cdh">`cdh`</a> command for changing to a recently visited directory.

16
doc_src/cdh.txt Normal file
View file

@ -0,0 +1,16 @@
\section cdh cdh - change to a recently visited directory
\subsection cdh-synopsis Synopsis
\fish{synopsis}
cdh [ directory ]
\endfish
\subsection cdh-description Description
`cdh` with no arguments presents a list of recently visited directories. You can then select one of the entries by letter or number. You can also press @key{tab} to use the completion pager to select an item from the list. If you give it a single argument it is equivalent to `cd directory`.
Note that the `cd` command limits directory history to the 25 most recently visited directories. The history is stored in the `$dirprev` and `$dirnext` variables which this command manipulates. If you make those universal variables your `cd` history is shared among all fish instances.
\subsection cdh-see-also See Also
See also the <a href="commands.html#prevd">`prevd`</a> and <a href="commands.html#pushd">`pushd`</a> commands which also work with the recent `cd` history and are provided for compatibility with other shells.

View file

@ -13,6 +13,8 @@ If the `-l` or `--list` flag is specified, the current directory history is also
Note that the `cd` command limits directory history to the 25 most recently visited directories. The history is stored in the `$dirprev` and `$dirnext` variables which this command manipulates.
You may be interested in the <a href="commands.html#cdh">`cdh`</a> command which provides a more intuitive way to navigate to recently visited directories.
\subsection nextd-example Example
\fish

View file

@ -9,6 +9,7 @@ popd
`popd` removes the top directory from the directory stack and changes the working directory to the new top directory. Use <a href="#pushd">`pushd`</a> to add directories to the stack.
You may be interested in the <a href="commands.html#cdh">`cdh`</a> command which provides a more intuitive way to navigate to recently visited directories.
\subsection popd-example Example

View file

@ -13,6 +13,8 @@ If the `-l` or `--list` flag is specified, the current history is also displayed
Note that the `cd` command limits directory history to the 25 most recently visited directories. The history is stored in the `$dirprev` and `$dirnext` variables which this command manipulates.
You may be interested in the <a href="commands.html#cdh">`cdh`</a> command which provides a more intuitive way to navigate to recently visited directories.
\subsection prevd-example Example
\fish

View file

@ -17,6 +17,8 @@ Without arguments, it exchanges the top two directories in the stack.
See also `dirs` and `dirs -c`.
You may be interested in the <a href="commands.html#cdh">`cdh`</a> command which provides a more intuitive way to navigate to recently visited directories.
\subsection pushd-example Example
\fish

View file

@ -0,0 +1,23 @@
function __fish_cdh_args
set -l all_dirs $dirprev $dirnext
set -l uniq_dirs
# This next bit of code doesn't do anything useful at the moment since the fish pager always
# sorts, and eliminates duplicate, entries. But we do this to mimic the modal behavor of `cdh`
# and in hope that the fish pager behavior will be changed to preserve the order of entries.
for dir in $all_dirs[-1..1]
if not contains $dir $uniq_dirs
set uniq_dirs $uniq_dirs $dir
end
end
for dir in $uniq_dirs
set -l home_dir (string match -r "$HOME(/.*|\$)" "$dir")
if set -q home_dir[2]
set dir "~$home_dir[2]"
end
echo $dir
end
end
complete -c cdh -xa '(__fish_cdh_args)'

87
share/functions/cdh.fish Normal file
View file

@ -0,0 +1,87 @@
# Provide a menu of the directories recently navigated to and ask the user to
# choose one to make the new current working directory (cwd).
function cdh --description "Menu based cd command"
# See if we've been invoked with an argument. Presumably from the `cdh` completion script.
# If we have just treat it as `cd` to the specified directory.
if set -q argv[1]
cd $argv
return
end
if set -q argv[2]
echo (_ "cdh: Expected zero or one arguments") >&2
return 1
end
set -l all_dirs $dirprev $dirnext
if not set -q all_dirs[1]
echo (_ 'No previous directories to select. You have to cd at least once.') >&2
return 0
end
# Reverse the directories so the most recently visited is first in the list.
# Also, eliminate duplicates; i.e., we only want the most recent visit to a
# given directory in the selection list.
set -l uniq_dirs
for dir in $all_dirs[-1..1]
if not contains $dir $uniq_dirs
set uniq_dirs $uniq_dirs $dir
end
end
set -l letters a b c d e f g h i j k l m n o p q r s t u v w x y z
set -l dirc (count $uniq_dirs)
if test $dirc -gt (count $letters)
set -l msg (_ 'This should not happen. Have you changed the cd function?')
printf "$msg\n"
set -l msg (_ 'There are %s unique dirs in your history but I can only handle %s')
printf "$msg\n" $dirc (count $letters)
return 1
end
# Print the recent directories, oldest to newest. Since we previously
# reversed the list, making the newest entry the first item in the array,
# we count down rather than up.
for i in (seq $dirc -1 1)
set -l dir $uniq_dirs[$i]
set -l label_color normal
set -q fish_color_cwd; and set label_color $fish_color_cwd
set -l dir_color_reset (set_color normal)
set -l dir_color
if test "$dir" = "$PWD"
set dir_color (set_color $fish_color_history_current)
end
set -l home_dir (string match -r "$HOME(/.*|\$)" "$dir")
if set -q home_dir[2]
set dir "~$home_dir[2]"
end
printf '%s %s %2d) %s %s%s%s\n' (set_color $label_color) $letters[$i] \
$i (set_color normal) $dir_color $dir $dir_color_reset
end
# Ask the user which directory from their history they want to cd to.
set -l msg (_ 'Select directory by letter or number: ')
read -l -p "echo '$msg'" choice
if test "$choice" = ""
return 0
else if string match -q -r '^[a-z]$' $choice
# Convert the letter to an index number.
set choice (contains -i $choice $letters)
end
set -l msg (_ 'Error: expected a number between 1 and %d or letter in that range, got "%s"')
if string match -q -r '^\d+$' $choice
if test $choice -ge 1 -a $choice -le $dirc
cd $uniq_dirs[$choice]
return
else
printf "$msg\n" $dirc $choice
return 1
end
else
printf "$msg\n" $dirc $choice
return 1
end
end