-
Notifications
You must be signed in to change notification settings - Fork 3
/
dirnav
148 lines (129 loc) · 4.24 KB
/
dirnav
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
# dirnav - directory navigation for zsh
# Greg Parker [email protected]
#
# Functions available for keybindings:
# _dirnav_parent: Change the current directory to the parent directory.
# _dirnav_child: Change the current directory to the child
# directory that was previously vacated by _dirnav_parent.
#
# Example zshrc:
# # load
# fpath=( /parent/of/dirnav/ "${fpath[@]}" )
# autoload -Uz dirnav
# dirnav
#
# # keybindings
# bindkey "^[[1;5D" _dirnav_parent # ctrl-left
# bindkey "^[[5D" _dirnav_parent # ctrl-left
# bindkey "^[[1;5C" _dirnav_child # ctrl-right
# bindkey "^[[5C" _dirnav_child # ctrl-right
autoload -Uz add-zsh-hook
# Keybinding: navigate to cwd's parent directory
function _dirnav_parent() {
# Don't allow auto_pushd to record these intermediate directory changes.
# We'll save the final position during preexec if necessary.
setopt local_options
unsetopt auto_pushd
if [[ $PWD = '/' ]]; then
[[ -o histbeep ]] && zle beep
return 0
fi
if [[ -z $_dirnav_deepest ]]; then
# _no _dirnav_deepest yet - save pwd
_dirnav_deepest=$PWD
else
case $_dirnav_deepest in
$PWD/*|$PWD ) ;; # pwd is some parent of _dirnav_deepest
* ) _dirnav_deepest=$PWD;; # save new _dirnav_deepest
esac
fi
cd ..
zle reset-prompt
return 0
}
# Keybinding: navigate to the previously-used child directory
function _dirnav_child() {
# Don't allow auto_pushd to record these intermediate directory changes.
# We'll save the final position during preexec if necessary.
setopt local_options
unsetopt auto_pushd
if [[ -z $_dirnav_deepest ]]; then
# _no _dirnav_deepest yet - can't navigate to the child
_dirnav_deepest=$PWD
[[ -o histbeep ]] && zle beep
elif [[ $_dirnav_deepest = $PWD ]]; then
# pwd is already equal to _dirnav_deepest- can't navigate to the child
[[ -o histbeep ]] && zle beep
else
# Remove trailing components from _dirnav_deepest until we reach pwd
# then keep the penultimate value.
local dir
local prevdir
dir=$_dirnav_deepest
while true; do
if [[ $dir = $PWD ]]; then
# success
cd -- $prevdir
zle reset-prompt
break
elif [[ -z $dir || $dir = '/' ]]; then
# failure?
[[ -o histbeep ]] && zle beep
break
else
# chop
prevdir=$dir
dir=${dir:h}
fi
done
fi
return 0
}
# Perform auto_pushd on behalf of dirnav parent and child motion
# (because auto_pushd was suppressed during them).
# We save the final destination to the dirstack during preexec instead.
# A precmd is used to record the PWD before dirnav does anything.
function _dirnav_preexec() {
# Do nothing if we're in the same place we were before
# or if we don't have a save from precmd.
if [[ -z $_dirnav_saved_cwd || $_dirnav_saved_cwd = $PWD ]]; then
return 0
fi
# Do nothing if auto_pushd is disabled.
if [[ ! -o auto_pushd ]]; then
_dirnav_saved_cwd=
return 0
fi
setopt local_options
unsetopt auto_pushd
# PWD has changed and auto_pushd is set.
# Record the previous CWD to the directory stack.
# We use -q because we're "already" in $newpwd.
local newpwd=$PWD
cd -q -- $_dirnav_saved_cwd
pushd -q -- $newpwd
# Clear saved cwd. We record a new one during the next precmd.
_dirnav_saved_cwd=
return 0
}
function _dirnav_precmd() {
# Set _dirnav_saved_cwd if it is not set already
# There may be multiple precmds for a single preexec due to zle reset-prompt
if [[ -z $_dirnav_saved_cwd ]]; then
_dirnav_saved_cwd=$PWD
fi
return 0
}
# Install hooks
add-zsh-hook precmd _dirnav_precmd
add-zsh-hook preexec _dirnav_preexec
# Register the keybindable functions
zle -N _dirnav_parent
zle -N _dirnav_child
# clean up
# Check if function dirnav exists before calling it.
# Some module systems don't create it in the first place.
if [ ${+functions[dirnav]} -eq 1 ]; then
unfunction dirnav;
fi
return 0