-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathM-O.sh
263 lines (218 loc) · 6.5 KB
/
M-O.sh
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
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
# Implementation of M-O.
#########
# STATE #
#########
# State variables control the execution of M-O and keep track of changes between
# updates. The variables are initialized to themselves (with expected default values)
# to ensure that re-registering is a no-op.
# The directory that was the current directory (symlinks resolved)
# the last time that _MO_update() was called.
# Is not exported to ensure that subshells build their state from scratch.
MO_CUR_DIR="$MO_CUR_DIR"
# The command that's being evaluated before the shell shows the prompt.
# It serves basically the same purpose as PROMPT_COMMAND,
# but works across shells and adds customizability.
# Is not exported to ensure that subshells build their state from scratch.
MO_PROMPT_COMMAND='_MO_update "$(pwd -P)"'
# Event handlers.
# Are not exported to ensure that subshells build their state from scratch.
MO_ENTER_HANDLER="$MO_ENTER_HANDLER"
MO_LEAVE_HANDLER="$MO_LEAVE_HANDLER"
# Log level: 1+: action info, 2+: event info.
export MO_LOG_LEVEL="${MO_LOG_LEVEL:-0}"
#####################
# REGISTER FUNCTION #
#####################
# TODO Remove function - doesn't serve any real purpose anymore.
# Function to be invoked for each prompt.
_MO_prompt_command() {
eval ${MO_PROMPT_COMMAND}
}
#######################################
# UPDATE AND EVENT EMITTING FUNCTIONS #
#######################################
# Arg 1: target_dir (new directory)
_MO_update() {
local -r target_dir="${1%/}"
# TODO Verify that this makes sense/makes a difference.
local -r x=$?
# Common case.
if [ "$MO_CUR_DIR" = "$target_dir" ]; then
MO_log 1 "(staying in $MO_CUR_DIR)"
return $x
fi
# Traverse from $old_dir up the tree ("leaving" directories on the way)
# until $MO_CUR_DIR is an ancestor (i.e. prefix) of $target_dir.
until _MO_is_ancestor "$MO_CUR_DIR" "$target_dir"; do
_MO_leave "$MO_CUR_DIR"
done
# Relative path from $MO_CUR_DIR to $target_dir.
local -r relative_path="${target_dir#"$MO_CUR_DIR"}"
if [ -n "$relative_path" ]; then
local dir
while read -d'/' dir; do
_MO_enter "$MO_CUR_DIR/$dir"
done <<< "${relative_path#/}"
_MO_enter "$MO_CUR_DIR/$dir"
fi
return $?
}
# Arg 1: dir
_MO_enter() {
# TODO Only trim slash if necessary.
local -r dir="${1%/}"
local -r event='enter'
eval ${MO_ENTER_HANDLER}
MO_CUR_DIR="$dir"
}
# Arg 1: dir
_MO_leave() {
# TODO Only trim slash if necessary.
local -r dir="${1%/}"
local -r event='leave'
eval ${MO_LEAVE_HANDLER}
MO_CUR_DIR="$(_MO_dirname "$dir")"
}
######################
# PRINTING FUNCTIONS #
######################
# TODO Try to write with printf such that echo can be aliased
# (cannot use `command echo` because that doesn't work with color codes in zsh). (<- but how about `builtin echo`?)
# Print a M-O head as a prefix for an echo message.
_MO_echo_head() {
# Bold foreground and black background.
echo -ne "\033[1;40m"
# "[": Bold white foreground on black background.
echo -ne "\033[97m["
# "--": Bold yellow foreground on black background.
echo -ne "\033[33m--"
# "]": Bold white foreground on default background.
echo -ne "\033[97m]"
# Reset.
echo -en "\033[0m"
}
# Print an angry M-O head as a prefix for a errcho message.
_MO_echo_angry_head() {
# Bold foreground and black background.
echo -ne "\033[1;40m"
# "[": Bold white foreground on black background.
echo -ne "\033[97m["
# "><": Bold red foreground on black background.
echo -ne "\033[31m><"
# "]": Bold white foreground on default background.
echo -ne "\033[97m]"
# Reset.
echo -en "\033[0m"
}
# Print a curious M-O head as a prefix for a debucho message.
_MO_echo_curious_head() {
# Bold foreground and black background.
echo -ne "\033[1;40m"
# "[": Bold white foreground on black background.
echo -ne "\033[97m["
# "==": Bold red foreground on black background.
echo -ne "\033[36m=="
# "]": Bold white foreground on default background.
echo -ne "\033[97m]"
# Reset.
echo -en "\033[0m"
}
# Print a message prefixed with a M-O head prefix.
MO_echo() {
local -r msg="$@"
if [ -n "$msg" ]; then
_MO_echo_head
>&2 echo " $msg"
fi
}
# Print a message prefixed with an angry M-O head prefix.
MO_errcho() {
local -r msg="$@"
if [ -n "$msg" ]; then
_MO_echo_angry_head
>&2 echo " $msg"
fi
}
# Print a message prefixed with a curious M-O head prefix.
MO_debucho() {
local -r msg="$@"
if [ -n "$msg" ]; then
_MO_echo_curious_head
>&2 echo " $msg"
fi
}
MO_log() {
local -r level="$1"
local -r msg="$2"
[ "$MO_LOG_LEVEL" -ge "$level" ] &&
if [ "$level" -eq 0 ]; then
MO_echo "$msg"
elif [ "$level" -gt 0 ]; then
MO_debucho "$msg"
else
MO_errcho "$msg"
fi
}
####################
# HELPER FUNCTIONS #
####################
# Arg 1: dir
# Print the dirname of dir unless it's '/'.
_MO_dirname() {
local -r dir="$1"
local -r result="$(dirname "$dir")"
[ "$result" != '/' ] && builtin echo "$result"
}
# Arg 1: ancestor
# Arg 2: descendant
_MO_is_ancestor() {
local -r ancestor="${1%/}/"
local -r descendant="${2%/}/"
# $descendant with the (literal) prefix $ancestor removed.
local suffix="${descendant#"$ancestor"}"
# If $ancestor is a (non-empty) prefix, then
# $suffix will be different from $descendant.
[ "$suffix" != "$descendant" ]
}
#####################
# UTILITY FUNCTIONS #
#####################
# Utility function which aren't needed in this file, but defined here to ensure their availability throughout the pro
# TODO Move these functions to separate utility project:
join_stmts() {
local -r left="$1"
local -r right="$2"
local sep=''
[ -n "$left" ] && [ -n "$right" ] && sep='; '
builtin echo "$left$sep$right"
}
export -f join_stmts
# From 'https://stackoverflow.com/a/13864829/883073'.
is_set() {
declare -p "$1" &>/dev/null
}
export -f is_set
dereference() {
local -r var="$1"
eval builtin echo "\$$var" # Like "${!var}" but works in both bash and zsh.
}
export -f dereference
# Generalization of quote from bash-completion to multiple arguments:
# For each argument, escape all single quotes and surround it with single quotes.
# Output all the results joined by single space.
# This ensures that the output can be used in both echo and eval correctly.
# The function behaves identically to the original quote function for exactly one argument.
#
# If no or multiple arguments are
# Minor incompatibility with the original quote function:
# If no inputs are provided, this function produces no output.
# The original one prints a quoted empty string.
quote() {
local p
for v in "$@"; do
local q="${v//\'/\'\\\'\'}"
printf "$p'%s'" "$q"
p=' '
done
}
export -f quote