wd.sh 8.89 KB
Newer Older
Markus Færevaag's avatar
Markus Færevaag committed
1
2
#!/bin/zsh

3
4
# WARP DIRECTORY
# ==============
Markus Færevaag's avatar
Markus Færevaag committed
5
6
7
8
9
# Jump to custom directories in terminal
# because `cd` takes too long...
#
# @github.com/mfaerevaag/wd

10
11
# version
readonly WD_VERSION=0.4
Markus Færevaag's avatar
Markus Færevaag committed
12

13
# colors
14
15
16
17
18
readonly WD_BLUE="\033[96m"
readonly WD_GREEN="\033[92m"
readonly WD_YELLOW="\033[93m"
readonly WD_RED="\033[91m"
readonly WD_NOC="\033[m"
Markus Færevaag's avatar
Markus Færevaag committed
19

20
## functions
Markus Færevaag's avatar
Markus Færevaag committed
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
# helpers
wd_yesorno()
{
    # variables
    local question="${1}"
    local prompt="${question} "
    local yes_RETVAL="0"
    local no_RETVAL="3"
    local RETVAL=""
    local answer=""

    # read-eval loop
    while true ; do
        printf $prompt
        read -r answer

        case ${answer:=${default}} in
            Y|y|YES|yes|Yes )
                RETVAL=${yes_RETVAL} && \
                    break
                ;;
            N|n|NO|no|No )
                RETVAL=${no_RETVAL} && \
                    break
                ;;
            * )
                echo "Please provide a valid answer (y or n)"
                ;;
        esac
    done
52

53
54
    return ${RETVAL}
}
55

56
57
58
59
60
61
wd_print_msg()
{
    if [[ -z $wd_quiet_mode ]]
    then
        local color=$1
        local msg=$2
Markus Færevaag's avatar
Markus Færevaag committed
62

63
64
65
66
67
68
69
70
        if [[ $color == "" || $msg == "" ]]
        then
            print " ${WD_RED}*${WD_NOC} Could not print message. Sorry!"
        else
            print " ${color}*${WD_NOC} ${msg}"
        fi
    fi
}
Markus Færevaag's avatar
Markus Færevaag committed
71

72
73
74
75
wd_print_usage()
{
    cat <<- EOF
Usage: wd [command] <point>
Markus Færevaag's avatar
Markus Færevaag committed
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
Commands:
	add <point>	Adds the current working directory to your warp points
	add! <point>	Overwrites existing warp point
	rm <point>	Removes the given warp point
	show		Print warp points to current directory
	show <point>	Print path to given warp point
	ls		Print all stored warp points
	clean!		Remove points warping to nonexistent directories

	-v | --version	Print version
	-d | --debug	Exit after execution with exit codes (for testing)
	-c | --config	Specify config file (default ~/.warprc)
	-q | --quiet	Suppress all output

	help		Show this extremely helpful text
EOF
}

wd_exit_fail()
{
    local msg=$1

    wd_print_msg $WD_RED $1
    WD_EXIT_CODE=1
}

wd_exit_warn()
{
    local msg=$1

    wd_print_msg $WD_YELLOW $msg
    WD_EXIT_CODE=1
}

# core
Markus Færevaag's avatar
Markus Færevaag committed
112
113
114

wd_warp()
{
115
116
117
    local point=$1

    if [[ $point =~ "^\.+$" ]]
Markus Færevaag's avatar
Markus Færevaag committed
118
    then
119
        if [ $#1 < 2 ]
Markus Færevaag's avatar
Markus Færevaag committed
120
        then
121
            wd_exit_warn "Warping to current directory?"
Markus Færevaag's avatar
Markus Færevaag committed
122
123
124
125
        else
            (( n = $#1 - 1 ))
            cd -$n > /dev/null
        fi
126
    elif [[ ${points[$point]} != "" ]]
Markus Færevaag's avatar
Markus Færevaag committed
127
    then
128
        cd ${points[$point]}
Markus Færevaag's avatar
Markus Færevaag committed
129
    else
130
        wd_exit_fail "Unknown warp point '${point}'"
Markus Færevaag's avatar
Markus Færevaag committed
131
132
133
134
135
    fi
}

wd_add()
{
136
137
138
139
140
    local force=$1
    local point=$2

    if [[ $point =~ "^[\.]+$" ]]
    then
141
        wd_exit_fail "Warp point cannot be just dots"
Markus Faerevaag's avatar
Markus Faerevaag committed
142
    elif [[ $point =~ "[[:space:]]+" ]]
Markus Færevaag's avatar
Markus Færevaag committed
143
    then
144
        wd_exit_fail "Warp point should not contain whitespace"
145
    elif [[ $point == *:* ]]
Markus Færevaag's avatar
Markus Færevaag committed
146
    then
147
        wd_exit_fail "Warp point cannot contain colons"
148
149
    elif [[ $point == "" ]]
    then
150
        wd_exit_fail "Warp point cannot be empty"
151
152
153
    elif [[ ${points[$2]} == "" ]] || $force
    then
        wd_remove $point > /dev/null
Markus Faerevaag's avatar
Markus Faerevaag committed
154
        printf "%q:%s\n" "${point}" "${PWD}" >> $WD_CONFIG
155
156

        wd_print_msg $WD_GREEN "Warp point added"
157

158
159
160
        # override exit code in case wd_remove did not remove any points
        # TODO: we should handle this kind of logic better
        WD_EXIT_CODE=0
Markus Færevaag's avatar
Markus Færevaag committed
161
    else
162
        wd_exit_warn "Warp point '${point}' already exists. Use 'add!' to overwrite."
Markus Færevaag's avatar
Markus Færevaag committed
163
164
165
166
167
    fi
}

wd_remove()
{
168
169
170
    local point=$1

    if [[ ${points[$point]} != "" ]]
Markus Færevaag's avatar
Markus Færevaag committed
171
    then
172
173
        local config_tmp=$WD_CONFIG.tmp
        if sed -n "/^${point}:.*$/!p" $WD_CONFIG > $config_tmp && mv $config_tmp $WD_CONFIG
Markus Færevaag's avatar
Markus Færevaag committed
174
        then
175
            wd_print_msg $WD_GREEN "Warp point removed"
Markus Færevaag's avatar
Markus Færevaag committed
176
        else
177
            wd_exit_fail "Something bad happened! Sorry."
Markus Færevaag's avatar
Markus Færevaag committed
178
179
        fi
    else
180
        wd_exit_fail "Warp point was not found"
Markus Færevaag's avatar
Markus Færevaag committed
181
182
183
184
185
    fi
}

wd_list_all()
{
186
    wd_print_msg $WD_BLUE "All warp points:"
187
188

    while IFS= read -r line
Markus Færevaag's avatar
Markus Færevaag committed
189
190
191
192
193
194
195
    do
        if [[ $line != "" ]]
        then
            arr=(${(s,:,)line})
            key=${arr[1]}
            val=${arr[2]}

196
197
198
199
            if [[ -z $wd_quiet_mode ]]
            then
                printf "%20s  ->  %s\n" $key $val
            fi
Markus Færevaag's avatar
Markus Færevaag committed
200
        fi
201
    done <<< $(sed "s:${HOME}:~:g" $WD_CONFIG)
202
203
204
205
}

wd_show()
{
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
    local name_arg=$1
    # if there's an argument we look up the value
    if [[ ! -z $name_arg ]]
    then
        if [[ -z $points[$name_arg] ]]
        then
            wd_print_msg $WD_BLUE "No warp point named $name_arg"
        else
            wd_print_msg $WD_GREEN "Warp point: ${WD_GREEN}$name_arg${WD_NOC} -> $points[$name_arg]"
        fi
    else
        # hax to create a local empty array
        local wd_matches
        wd_matches=()
        # do a reverse lookup to check whether PWD is in $points
        if [[ ${points[(r)$PWD]} == $PWD ]]
        then
            for name in ${(k)points}
            do
                if [[ $points[$name] == $PWD ]]
                then
                    wd_matches[$(($#wd_matches+1))]=$name
                fi
            done

            wd_print_msg $WD_BLUE "$#wd_matches warp point(s) to current directory: ${WD_GREEN}$wd_matches${WD_NOC}"
        else
233
            wd_print_msg $WD_YELLOW "No warp point to $(echo $PWD | sed "s:$HOME:~:")"
234
235
        fi
    fi
Markus Færevaag's avatar
Markus Færevaag committed
236
237
}

238
239
240
241
242
243
244
245
246
247
248
249
wd_clean() {
    local force=$1
    local count=0
    local wd_tmp=""

    while read line
    do
        if [[ $line != "" ]]
        then
            arr=(${(s,:,)line})
            key=${arr[1]}
            val=${arr[2]}
250

251
252
253
254
255
256
257
258
259
260
261
            if [ -d "$val" ]
            then
                wd_tmp=$wd_tmp"\n"`echo $line`
            else
                wd_print_msg $WD_YELLOW "Nonexistent directory: ${key} -> ${val}"
                count=$((count+1))
            fi
        fi
    done < $WD_CONFIG

    if [[ $count -eq 0 ]]
Markus Færevaag's avatar
Markus Færevaag committed
262
    then
263
        wd_print_msg $WD_BLUE "No warp points to clean, carry on!"
Markus Færevaag's avatar
Markus Færevaag committed
264
    else
265
266
267
268
269
270
271
        if $force || wd_yesorno "Removing ${count} warp points. Continue? (Y/n)"
        then
            echo $wd_tmp >! $WD_CONFIG
            wd_print_msg $WD_GREEN "Cleanup complete. ${count} warp point(s) removed"
        else
            wd_print_msg $WD_BLUE "Cleanup aborted"
        fi
Markus Færevaag's avatar
Markus Færevaag committed
272
273
274
    fi
}

275
276
277
278
local WD_CONFIG=$HOME/.warprc
local WD_QUIET=0
local WD_EXIT_CODE=0
local WD_DEBUG=0
279

280
281
282
# Parse 'meta' options first to avoid the need to have them before
# other commands. The `-D` flag consumes recognized options so that
# the actual command parsing won't be affected.
Markus Færevaag's avatar
Markus Færevaag committed
283

284
285
286
287
288
zparseopts -D -E \
    c:=wd_alt_config -config:=wd_alt_config \
    q=wd_quiet_mode -quiet=wd_quiet_mode \
    v=wd_print_version -version=wd_print_version \
    d=wd_debug_mode -debug=wd_debug_mode
Markus Færevaag's avatar
Markus Færevaag committed
289

290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
if [[ ! -z $wd_print_version ]]
then
    echo "wd version $WD_VERSION"
fi

if [[ ! -z $wd_alt_config ]]
then
    WD_CONFIG=$wd_alt_config[2]
fi

# check if config file exists
if [ ! -e $WD_CONFIG ]
then
    # if not, create config file
    touch $WD_CONFIG
fi

# load warp points
typeset -A points
while read -r line
do
    arr=(${(s,:,)line})
    key=${arr[1]}
    val=${arr[2]}

    points[$key]=$val
done < $WD_CONFIG
Markus Færevaag's avatar
Markus Færevaag committed
317
318

# get opts
319
args=$(getopt -o a:r:c:lhs -l add:,rm:,clean\!,ls,help,show -- $*)
Markus Færevaag's avatar
Markus Færevaag committed
320

321
322
# check if no arguments were given, and that version is not set
if [[ ($? -ne 0 || $#* -eq 0) && -z $wd_print_version ]]
Markus Færevaag's avatar
Markus Færevaag committed
323
324
325
then
    wd_print_usage

326
327
    # check if config file is writeable
elif [ ! -w $WD_CONFIG ]
328
then
329
    # do nothing
330
    # can't run `exit`, as this would exit the executing shell
331
    wd_exit_fail "\'$WD_CONFIG\' is not writeable."
332
333

else
334
335

    # parse rest of options
336
    for o
Markus Færevaag's avatar
Markus Færevaag committed
337
    do
338
        case "$o"
Markus Faerevaag's avatar
Markus Faerevaag committed
339
340
341
            in
            -a|--add|add)
                wd_add false $2
Markus Færevaag's avatar
Markus Færevaag committed
342
343
344
                break
                ;;
            -a!|--add!|add!)
Markus Faerevaag's avatar
Markus Faerevaag committed
345
                wd_add true $2
Markus Færevaag's avatar
Markus Færevaag committed
346
347
                break
                ;;
Markus Faerevaag's avatar
Markus Faerevaag committed
348
349
            -r|--remove|rm)
                wd_remove $2
Markus Færevaag's avatar
Markus Færevaag committed
350
351
                break
                ;;
Markus Faerevaag's avatar
Markus Faerevaag committed
352
353
            -l|--list|ls)
                wd_list_all
Markus Færevaag's avatar
Markus Færevaag committed
354
355
                break
                ;;
Markus Faerevaag's avatar
Markus Faerevaag committed
356
357
            -h|--help|help)
                wd_print_usage
Markus Færevaag's avatar
Markus Færevaag committed
358
359
                break
                ;;
Markus Faerevaag's avatar
Markus Faerevaag committed
360
            -s|--show|show)
361
362
363
364
365
366
367
368
369
                wd_show $2
                break
                ;;
            -c|--clean|clean)
                wd_clean false
                break
                ;;
            -c!|--clean!|clean!)
                wd_clean true
Markus Færevaag's avatar
Markus Færevaag committed
370
371
372
                break
                ;;
            *)
373
                wd_warp $o
Markus Færevaag's avatar
Markus Færevaag committed
374
375
                break
                ;;
Markus Faerevaag's avatar
Markus Faerevaag committed
376
377
378
379
            --)
                break
                ;;
        esac
Markus Færevaag's avatar
Markus Færevaag committed
380
381
382
383
384
385
    done
fi

## garbage collection
# if not, next time warp will pick up variables from this run
# remember, there's no sub shell
386
387
388
389
390
391
392

unset wd_warp
unset wd_add
unset wd_remove
unset wd_show
unset wd_list_all
unset wd_print_msg
393
unset wd_yesorno
394
unset wd_print_usage
395
396
397
unset wd_alt_config
unset wd_quiet_mode
unset wd_print_version
398

Markus Faerevaag's avatar
Markus Faerevaag committed
399
unset args
400
unset points
Markus Faerevaag's avatar
Markus Faerevaag committed
401
unset val &> /dev/null # fixes issue #1
402
403
404
405
406
407
408

if [[ ! -z $wd_debug_mode ]]
then
    exit $WD_EXIT_CODE
else
    unset wd_debug_mode
fi