wd.sh 10.6 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.6
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
wd_print_usage()
{
    cat <<- EOF
75
Usage: wd [command] [point]
Markus Færevaag's avatar
Markus Færevaag committed
76

77
Commands:
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
    add <point>     Adds the current working directory to your warp points
    add             Adds the current working directory to your warp points with current directory's name
    add! <point>    Overwrites existing warp point
    add!            Overwrites existing warp point with current directory's name
    rm <point>      Removes the given warp point
    rm              Removes the given warp point with current directory's name
    show <point>    Print path to given warp point
    show            Print warp points to current directory
    list            Print all stored warp points
    ls  <point>     Show files from given warp point (ls)
    path <point>    Show the path to given warp point (pwd)
    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
97
98
99
100
101
102
103
EOF
}

wd_exit_fail()
{
    local msg=$1

Markus Faerevaag's avatar
Markus Faerevaag committed
104
    wd_print_msg $WD_RED $msg
105
106
107
108
109
110
111
112
113
114
115
    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
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
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
}

132
# core
Markus Færevaag's avatar
Markus Færevaag committed
133
134
135

wd_warp()
{
136
    local point=$1
137
    local sub=$2
138
139

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

wd_add()
{
163
164
165
    local force=$1
    local point=$2

166
167
168
169
170
    if [[ $point == "" ]]
    then
        point=$(basename $PWD)
    fi

171
172
    if [[ $point =~ "^[\.]+$" ]]
    then
173
        wd_exit_fail "Warp point cannot be just dots"
Markus Faerevaag's avatar
Markus Faerevaag committed
174
    elif [[ $point =~ "[[:space:]]+" ]]
Markus Færevaag's avatar
Markus Færevaag committed
175
    then
176
        wd_exit_fail "Warp point should not contain whitespace"
177
    elif [[ $point == *:* ]]
Markus Færevaag's avatar
Markus Færevaag committed
178
    then
179
        wd_exit_fail "Warp point cannot contain colons"
180
    elif [[ ${points[$point]} == "" ]] || $force
181
182
    then
        wd_remove $point > /dev/null
183
        printf "%q:%s\n" "${point}" "${PWD/#$HOME/~}" >> $WD_CONFIG
184
185

        wd_print_msg $WD_GREEN "Warp point added"
186

187
188
189
        # 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
190
    else
191
        wd_exit_warn "Warp point '${point}' already exists. Use 'add!' to overwrite."
Markus Færevaag's avatar
Markus Færevaag committed
192
193
194
195
196
    fi
}

wd_remove()
{
197
198
    local point=$1

199
200
201
202
203
    if [[ $point == "" ]]
    then
        point=$(basename $PWD)
    fi

204
    if [[ ${points[$point]} != "" ]]
Markus Færevaag's avatar
Markus Færevaag committed
205
    then
206
207
        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
208
        then
209
            wd_print_msg $WD_GREEN "Warp point removed"
Markus Færevaag's avatar
Markus Færevaag committed
210
        else
211
            wd_exit_fail "Something bad happened! Sorry."
Markus Færevaag's avatar
Markus Færevaag committed
212
213
        fi
    else
214
        wd_exit_fail "Warp point was not found"
Markus Færevaag's avatar
Markus Færevaag committed
215
216
217
218
219
    fi
}

wd_list_all()
{
220
    wd_print_msg $WD_BLUE "All warp points:"
221

222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
    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

237
    while IFS= read -r line
Markus Færevaag's avatar
Markus Færevaag committed
238
239
240
241
242
243
244
    do
        if [[ $line != "" ]]
        then
            arr=(${(s,:,)line})
            key=${arr[1]}
            val=${arr[2]}

245
246
            if [[ -z $wd_quiet_mode ]]
            then
247
                printf "%${max_warp_point_length}s  ->  %s\n" $key $val
248
            fi
Markus Færevaag's avatar
Markus Færevaag committed
249
        fi
250
    done <<< $entries
251
252
}

Markus Faerevaag's avatar
Markus Faerevaag committed
253
254
255
wd_ls()
{
    wd_getdir $1
256
    ls ${dir/#\~/$HOME}
Markus Faerevaag's avatar
Markus Faerevaag committed
257
258
259
260
261
262
263
264
}

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

265
266
wd_show()
{
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
    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
282
        PWD="${PWD/$HOME/~}"
283
284
285
286
287
288
289
290
291
292
293
294
        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
295
            wd_print_msg $WD_YELLOW "No warp point to $(echo $PWD | sed "s:$HOME:~:")"
296
297
        fi
    fi
Markus Færevaag's avatar
Markus Færevaag committed
298
299
}

300
301
302
303
304
305
306
307
308
309
310
311
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]}
312

313
            if [ -d "${val/#\~/$HOME}" ]
314
315
316
317
318
319
320
321
322
323
            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
324
    then
325
        wd_print_msg $WD_BLUE "No warp points to clean, carry on!"
Markus Færevaag's avatar
Markus Færevaag committed
326
    else
327
328
329
330
331
332
333
        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
334
335
336
    fi
}

337
338
339
340
local WD_CONFIG=$HOME/.warprc
local WD_QUIET=0
local WD_EXIT_CODE=0
local WD_DEBUG=0
341

342
343
344
# 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
345

346
347
348
349
350
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
351

352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
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]}
375
376
    # join the rest, in case the path contains colons
    val=${(j,:,)arr[2,-1]}
377
378
379

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

# get opts
Markus Faerevaag's avatar
Markus Faerevaag committed
382
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
383

384
385
# 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
386
387
388
then
    wd_print_usage

389
390
    # check if config file is writeable
elif [ ! -w $WD_CONFIG ]
391
then
392
    # do nothing
393
    # can't run `exit`, as this would exit the executing shell
394
    wd_exit_fail "\'$WD_CONFIG\' is not writeable."
395
396

else
397
398

    # parse rest of options
399
    for o
Markus Færevaag's avatar
Markus Færevaag committed
400
    do
401
        case "$o"
Markus Faerevaag's avatar
Markus Faerevaag committed
402
403
404
            in
            -a|--add|add)
                wd_add false $2
Markus Færevaag's avatar
Markus Færevaag committed
405
406
407
                break
                ;;
            -a!|--add!|add!)
Markus Faerevaag's avatar
Markus Faerevaag committed
408
                wd_add true $2
Markus Færevaag's avatar
Markus Færevaag committed
409
410
                break
                ;;
Markus Faerevaag's avatar
Markus Faerevaag committed
411
412
            -r|--remove|rm)
                wd_remove $2
Markus Færevaag's avatar
Markus Færevaag committed
413
414
                break
                ;;
Markus Faerevaag's avatar
Markus Faerevaag committed
415
            -l|list)
Markus Faerevaag's avatar
Markus Faerevaag committed
416
                wd_list_all
Markus Færevaag's avatar
Markus Færevaag committed
417
418
                break
                ;;
Markus Faerevaag's avatar
Markus Faerevaag committed
419
420
421
422
423
424
425
426
            -ls|ls)
                wd_ls $2
                break
                ;;
            -p|--path|path)
                wd_path $2
                break
                ;;
Markus Faerevaag's avatar
Markus Faerevaag committed
427
428
            -h|--help|help)
                wd_print_usage
Markus Færevaag's avatar
Markus Færevaag committed
429
430
                break
                ;;
Markus Faerevaag's avatar
Markus Faerevaag committed
431
            -s|--show|show)
432
433
434
435
436
437
438
439
440
                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
441
442
443
                break
                ;;
            *)
444
                wd_warp $o $2
Markus Færevaag's avatar
Markus Færevaag committed
445
446
                break
                ;;
Markus Faerevaag's avatar
Markus Faerevaag committed
447
448
449
450
            --)
                break
                ;;
        esac
Markus Færevaag's avatar
Markus Færevaag committed
451
452
453
454
455
456
    done
fi

## garbage collection
# if not, next time warp will pick up variables from this run
# remember, there's no sub shell
457
458
459
460
461
462
463

unset wd_warp
unset wd_add
unset wd_remove
unset wd_show
unset wd_list_all
unset wd_print_msg
464
unset wd_yesorno
465
unset wd_print_usage
466
467
468
unset wd_alt_config
unset wd_quiet_mode
unset wd_print_version
469

Markus Faerevaag's avatar
Markus Faerevaag committed
470
unset args
471
unset points
Markus Faerevaag's avatar
Markus Faerevaag committed
472
unset val &> /dev/null # fixes issue #1
473
474
475
476
477
478
479

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