wd.sh 9.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
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
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]}
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
Markus Faerevaag's avatar
Markus Faerevaag committed
172
        printf "%q:%s\n" "${point}" "${PWD}" >> $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

    while IFS= read -r line
Markus Færevaag's avatar
Markus Færevaag committed
207
208
209
210
211
212
213
    do
        if [[ $line != "" ]]
        then
            arr=(${(s,:,)line})
            key=${arr[1]}
            val=${arr[2]}

214
215
216
217
            if [[ -z $wd_quiet_mode ]]
            then
                printf "%20s  ->  %s\n" $key $val
            fi
Markus Færevaag's avatar
Markus Færevaag committed
218
        fi
219
    done <<< $(sed "s:${HOME}:~:g" $WD_CONFIG)
220
221
}

Markus Faerevaag's avatar
Markus Faerevaag committed
222
223
224
225
226
227
228
229
230
231
232
233
wd_ls()
{
    wd_getdir $1
    ls $dir
}

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

234
235
wd_show()
{
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
    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
263
            wd_print_msg $WD_YELLOW "No warp point to $(echo $PWD | sed "s:$HOME:~:")"
264
265
        fi
    fi
Markus Færevaag's avatar
Markus Færevaag committed
266
267
}

268
269
270
271
272
273
274
275
276
277
278
279
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]}
280

281
282
283
284
285
286
287
288
289
290
291
            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
292
    then
293
        wd_print_msg $WD_BLUE "No warp points to clean, carry on!"
Markus Færevaag's avatar
Markus Færevaag committed
294
    else
295
296
297
298
299
300
301
        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
302
303
304
    fi
}

305
306
307
308
local WD_CONFIG=$HOME/.warprc
local WD_QUIET=0
local WD_EXIT_CODE=0
local WD_DEBUG=0
309

310
311
312
# 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
313

314
315
316
317
318
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
319

320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
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
347
348

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

351
352
# 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
353
354
355
then
    wd_print_usage

356
357
    # check if config file is writeable
elif [ ! -w $WD_CONFIG ]
358
then
359
    # do nothing
360
    # can't run `exit`, as this would exit the executing shell
361
    wd_exit_fail "\'$WD_CONFIG\' is not writeable."
362
363

else
364
365

    # parse rest of options
366
    for o
Markus Færevaag's avatar
Markus Færevaag committed
367
    do
368
        case "$o"
Markus Faerevaag's avatar
Markus Faerevaag committed
369
370
371
            in
            -a|--add|add)
                wd_add false $2
Markus Færevaag's avatar
Markus Færevaag committed
372
373
374
                break
                ;;
            -a!|--add!|add!)
Markus Faerevaag's avatar
Markus Faerevaag committed
375
                wd_add true $2
Markus Færevaag's avatar
Markus Færevaag committed
376
377
                break
                ;;
Markus Faerevaag's avatar
Markus Faerevaag committed
378
379
            -r|--remove|rm)
                wd_remove $2
Markus Færevaag's avatar
Markus Færevaag committed
380
381
                break
                ;;
Markus Faerevaag's avatar
Markus Faerevaag committed
382
            -l|list)
Markus Faerevaag's avatar
Markus Faerevaag committed
383
                wd_list_all
Markus Færevaag's avatar
Markus Færevaag committed
384
385
                break
                ;;
Markus Faerevaag's avatar
Markus Faerevaag committed
386
387
388
389
390
391
392
393
            -ls|ls)
                wd_ls $2
                break
                ;;
            -p|--path|path)
                wd_path $2
                break
                ;;
Markus Faerevaag's avatar
Markus Faerevaag committed
394
395
            -h|--help|help)
                wd_print_usage
Markus Færevaag's avatar
Markus Færevaag committed
396
397
                break
                ;;
Markus Faerevaag's avatar
Markus Faerevaag committed
398
            -s|--show|show)
399
400
401
402
403
404
405
406
407
                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
408
409
410
                break
                ;;
            *)
411
                wd_warp $o
Markus Færevaag's avatar
Markus Færevaag committed
412
413
                break
                ;;
Markus Faerevaag's avatar
Markus Faerevaag committed
414
415
416
417
            --)
                break
                ;;
        esac
Markus Færevaag's avatar
Markus Færevaag committed
418
419
420
421
422
423
    done
fi

## garbage collection
# if not, next time warp will pick up variables from this run
# remember, there's no sub shell
424
425
426
427
428
429
430

unset wd_warp
unset wd_add
unset wd_remove
unset wd_show
unset wd_list_all
unset wd_print_msg
431
unset wd_yesorno
432
unset wd_print_usage
433
434
435
unset wd_alt_config
unset wd_quiet_mode
unset wd_print_version
436

Markus Faerevaag's avatar
Markus Faerevaag committed
437
unset args
438
unset points
Markus Faerevaag's avatar
Markus Faerevaag committed
439
unset val &> /dev/null # fixes issue #1
440
441
442
443
444
445
446

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