wd.sh 9.97 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
# version
11
readonly WD_VERSION=0.4.2
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
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
Markus Faerevaag's avatar
Markus Faerevaag committed
83
84
85
	list	        Print all stored warp points
ls  <point>     Show files from given warp point
path <point>    Show the path to given warp point
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
	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

Markus Faerevaag's avatar
Markus Faerevaag committed
101
    wd_print_msg $WD_RED $msg
102
103
104
105
106
107
108
109
110
111
112
    WD_EXIT_CODE=1
}

wd_exit_warn()
{
    local msg=$1

    wd_print_msg $WD_YELLOW $msg
    WD_EXIT_CODE=1
}

Markus Faerevaag's avatar
Markus Faerevaag committed
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
wd_getdir()
{
    local name_arg=$1

    point=$(wd_show $name_arg)
    dir=${point:28+$#name_arg+7}

    if [[ -z $name_arg ]]; then
        wd_exit_fail "You must enter a warp point"
        break
    elif [[ -z $dir ]]; then
        wd_exit_fail "Unknown warp point '${name_arg}'"
        break
    fi
}

129
# core
Markus Færevaag's avatar
Markus Færevaag committed
130
131
132

wd_warp()
{
133
134
135
    local point=$1

    if [[ $point =~ "^\.+$" ]]
Markus Færevaag's avatar
Markus Færevaag committed
136
    then
137
        if [ $#1 < 2 ]
Markus Færevaag's avatar
Markus Færevaag committed
138
        then
139
            wd_exit_warn "Warping to current directory?"
Markus Færevaag's avatar
Markus Færevaag committed
140
141
142
143
        else
            (( n = $#1 - 1 ))
            cd -$n > /dev/null
        fi
144
    elif [[ ${points[$point]} != "" ]]
Markus Færevaag's avatar
Markus Færevaag committed
145
    then
146
        cd ${points[$point]/#\~/$HOME}
Markus Færevaag's avatar
Markus Færevaag committed
147
    else
148
        wd_exit_fail "Unknown warp point '${point}'"
Markus Færevaag's avatar
Markus Færevaag committed
149
150
151
152
153
    fi
}

wd_add()
{
154
155
156
157
158
    local force=$1
    local point=$2

    if [[ $point =~ "^[\.]+$" ]]
    then
159
        wd_exit_fail "Warp point cannot be just dots"
Markus Faerevaag's avatar
Markus Faerevaag committed
160
    elif [[ $point =~ "[[:space:]]+" ]]
Markus Færevaag's avatar
Markus Færevaag committed
161
    then
162
        wd_exit_fail "Warp point should not contain whitespace"
163
    elif [[ $point == *:* ]]
Markus Færevaag's avatar
Markus Færevaag committed
164
    then
165
        wd_exit_fail "Warp point cannot contain colons"
166
167
    elif [[ $point == "" ]]
    then
168
        wd_exit_fail "Warp point cannot be empty"
169
170
171
    elif [[ ${points[$2]} == "" ]] || $force
    then
        wd_remove $point > /dev/null
172
        printf "%q:%s\n" "${point}" "${PWD/#$HOME/~}" >> $WD_CONFIG
173
174

        wd_print_msg $WD_GREEN "Warp point added"
175

176
177
178
        # 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
179
    else
180
        wd_exit_warn "Warp point '${point}' already exists. Use 'add!' to overwrite."
Markus Færevaag's avatar
Markus Færevaag committed
181
182
183
184
185
    fi
}

wd_remove()
{
186
187
188
    local point=$1

    if [[ ${points[$point]} != "" ]]
Markus Færevaag's avatar
Markus Færevaag committed
189
    then
190
191
        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
192
        then
193
            wd_print_msg $WD_GREEN "Warp point removed"
Markus Færevaag's avatar
Markus Færevaag committed
194
        else
195
            wd_exit_fail "Something bad happened! Sorry."
Markus Færevaag's avatar
Markus Færevaag committed
196
197
        fi
    else
198
        wd_exit_fail "Warp point was not found"
Markus Færevaag's avatar
Markus Færevaag committed
199
200
201
202
203
    fi
}

wd_list_all()
{
204
    wd_print_msg $WD_BLUE "All warp points:"
205

206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
    entries=$(sed "s:${HOME}:~:g" $WD_CONFIG)

    max_warp_point_length=0
    while IFS= read -r line
    do
        arr=(${(s,:,)line})
        key=${arr[1]}

        length=${#key}
        if [[ length -gt max_warp_point_length ]]
        then
            max_warp_point_length=$length
        fi
    done <<< $entries

221
    while IFS= read -r line
Markus Færevaag's avatar
Markus Færevaag committed
222
223
224
225
226
227
228
    do
        if [[ $line != "" ]]
        then
            arr=(${(s,:,)line})
            key=${arr[1]}
            val=${arr[2]}

229
230
            if [[ -z $wd_quiet_mode ]]
            then
231
                printf "%${max_warp_point_length}s  ->  %s\n" $key $val
232
            fi
Markus Færevaag's avatar
Markus Færevaag committed
233
        fi
234
    done <<< $entries
235
236
}

Markus Faerevaag's avatar
Markus Faerevaag committed
237
238
239
wd_ls()
{
    wd_getdir $1
240
    ls ${dir/#\~/$HOME}
Markus Faerevaag's avatar
Markus Faerevaag committed
241
242
243
244
245
246
247
248
}

wd_path()
{
    wd_getdir $1
    echo $(echo $dir | sed "s:${HOME}:~:g")
}

249
250
wd_show()
{
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
    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
266
        PWD="${PWD/$HOME/~}"
267
268
269
270
271
272
273
274
275
276
277
278
        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
279
            wd_print_msg $WD_YELLOW "No warp point to $(echo $PWD | sed "s:$HOME:~:")"
280
281
        fi
    fi
Markus Færevaag's avatar
Markus Færevaag committed
282
283
}

284
285
286
287
288
289
290
291
292
293
294
295
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]}
296

297
298
299
300
301
302
303
304
305
306
307
            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
308
    then
309
        wd_print_msg $WD_BLUE "No warp points to clean, carry on!"
Markus Færevaag's avatar
Markus Færevaag committed
310
    else
311
312
313
314
315
316
317
        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
318
319
320
    fi
}

321
322
323
324
local WD_CONFIG=$HOME/.warprc
local WD_QUIET=0
local WD_EXIT_CODE=0
local WD_DEBUG=0
325

326
327
328
# 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
329

330
331
332
333
334
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
335

336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
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
363
364

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

367
368
# 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
369
370
371
then
    wd_print_usage

372
373
    # check if config file is writeable
elif [ ! -w $WD_CONFIG ]
374
then
375
    # do nothing
376
    # can't run `exit`, as this would exit the executing shell
377
    wd_exit_fail "\'$WD_CONFIG\' is not writeable."
378
379

else
380
381

    # parse rest of options
382
    for o
Markus Færevaag's avatar
Markus Færevaag committed
383
    do
384
        case "$o"
Markus Faerevaag's avatar
Markus Faerevaag committed
385
386
387
            in
            -a|--add|add)
                wd_add false $2
Markus Færevaag's avatar
Markus Færevaag committed
388
389
390
                break
                ;;
            -a!|--add!|add!)
Markus Faerevaag's avatar
Markus Faerevaag committed
391
                wd_add true $2
Markus Færevaag's avatar
Markus Færevaag committed
392
393
                break
                ;;
Markus Faerevaag's avatar
Markus Faerevaag committed
394
395
            -r|--remove|rm)
                wd_remove $2
Markus Færevaag's avatar
Markus Færevaag committed
396
397
                break
                ;;
Markus Faerevaag's avatar
Markus Faerevaag committed
398
            -l|list)
Markus Faerevaag's avatar
Markus Faerevaag committed
399
                wd_list_all
Markus Færevaag's avatar
Markus Færevaag committed
400
401
                break
                ;;
Markus Faerevaag's avatar
Markus Faerevaag committed
402
403
404
405
406
407
408
409
            -ls|ls)
                wd_ls $2
                break
                ;;
            -p|--path|path)
                wd_path $2
                break
                ;;
Markus Faerevaag's avatar
Markus Faerevaag committed
410
411
            -h|--help|help)
                wd_print_usage
Markus Færevaag's avatar
Markus Færevaag committed
412
413
                break
                ;;
Markus Faerevaag's avatar
Markus Faerevaag committed
414
            -s|--show|show)
415
416
417
418
419
420
421
422
423
                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
424
425
426
                break
                ;;
            *)
427
                wd_warp $o
Markus Færevaag's avatar
Markus Færevaag committed
428
429
                break
                ;;
Markus Faerevaag's avatar
Markus Faerevaag committed
430
431
432
433
            --)
                break
                ;;
        esac
Markus Færevaag's avatar
Markus Færevaag committed
434
435
436
437
438
439
    done
fi

## garbage collection
# if not, next time warp will pick up variables from this run
# remember, there's no sub shell
440
441
442
443
444
445
446

unset wd_warp
unset wd_add
unset wd_remove
unset wd_show
unset wd_list_all
unset wd_print_msg
447
unset wd_yesorno
448
unset wd_print_usage
449
450
451
unset wd_alt_config
unset wd_quiet_mode
unset wd_print_version
452

Markus Faerevaag's avatar
Markus Faerevaag committed
453
unset args
454
unset points
Markus Faerevaag's avatar
Markus Faerevaag committed
455
unset val &> /dev/null # fixes issue #1
456
457
458
459
460
461
462

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