-
Notifications
You must be signed in to change notification settings - Fork 4
/
Copy path.shellrc
1183 lines (1049 loc) · 37 KB
/
.shellrc
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
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
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
317
318
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
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
# ==============================================================================
# = paradigm's bash/zsh shell configuration =
# ==============================================================================
cmd="$(basename $(readlink /proc/$$/exe))"
is_bash=false
is_zsh=false
if [ "${cmd}" = "bash" ]; then
is_bash=true
elif [ "${cmd}" = "zsh" ]; then
is_zsh=true
fi
in_path() {
type -p "${1}" >/dev/null 2>&1
}
# ==============================================================================
# = general-settings =
# ==============================================================================
umask 022
# None of the lines following this point are useful for non-interactive
# instances of this shell. If the prompt variable does not exist, this
# instances of the shell is non-interactive, so exit the rc file.
[ -z "${PS1}" ] && [ -z "${PROMPT}" ] && return
# Disable flow control. Specifically, ensure that ctrl-s does not stop
# terminal flow so that it can be used in other programs (such as Vim).
stty -ixon
if "${is_bash}"; then
# Update $LINES and $COLUMNS when terminal size changes.
shopt -s checkwinsize
# Enable extended globbing functionality.
shopt -s extglob
elif "${is_zsh}"; then
# Enable extended globbing functionality.
setopt extendedglob
# Do not record repeated lines in history. Note that this line is
# largely non-functional in this .zshrc as history has not been
# enabled.
setopt histignoredups
# If non-ambiguous, allow changing into a directory just by typing its
# name (ie, make "cd" optional)
setopt autocd
# Detect and prompt to correct typos in commands. Note there is a
# "correctall" variant which also prompts to correct arguments to
# commands, but this ends up being more troublesome than useful.
setopt correct
# Disables the beep zsh would otherwise make when giving invalid input
# (such as hitting backspace on an empty command line).
setopt nobeep
# Do not change the nice (ie, scheduling priority) of backgrounded
# commands.
setopt nobgnice
# Do not kill background processes when closing the shell.
setopt nohup
# Do not warn about closing the shell with background jobs running.
setopt nocheckjobs
# Allow comments on the command line. Without this comments are only
# allowed in scripts.
setopt interactivecomments
# Automatically run pushd after every cd
# this allows for tab-completion on "cd -<tab>"
setopt autopushd
fi
# ==============================================================================
# = general-envvars =
# ==============================================================================
for dir in .bin .priv .go/bin .cargo/bin .local/bin; do
if echo "${PATH}" | grep -q "\(:\|^\)${HOME}/${dir}:"; then
export PATH="${HOME}/${dir}:$(echo "${PATH}" | sed "s,:${HOME}/${dir}:,:,")"
else
export PATH="${HOME}/${dir}:${PATH}"
fi
done
if uname -a | grep -qi "cygwin"; then
for app in /cygdrive/c/apps/*; do
if ! echo "${PATH}" | grep -q '\(:\|^\)'"${app}"'\(:\|$\)'; then
export PATH="${PATH}:${app}"
fi
done
fi
if in_path "vim"; then
export MANPAGER="vim --cmd 'set modelines=0' -c 'set filetype=man nomod nolist foldlevel=999' -"
export GIT_PAGER="vim --cmd 'set modelines=0' -c 'set filetype=git nomod nolist foldlevel=999' -"
export EDITOR="vim"
fi
if [ -n "$DISPLAY" ] && in_path "firefox"; then
export BROWSER="firefox"
elif [ -z "$DISPLAY" ] && in_path "elinks"; then
export BROWSER="elinks"
fi
if in_path "mupdf"; then
export PDFREADER="mupdf"
export PDFVIEWER="mupdf"
fi
if in_path "sxiv"; then
export IMAGEVIEWER="sxiv"
fi
if [ -n "${DISPLAY}" ] && [ "$TERM" = "xterm" ]; then
export TERM="xterm-256color"
elif [ -n "${DISPLAY}" ] && [ "$TERM" = "screen" ]; then
export TERM="screen-256color"
fi
export MAIL="~/.mail"
# needed for /bedrock repo
export GIT_DISCOVERY_ACROSS_FILESYSTEM=1
# Use gpg-agent if available
export GPG_TTY="$(tty)"
export GOPATH="${HOME}/.go"
if "${is_bash}"; then
# Disable history.
export HISTFILE=""
elif "${is_zsh}"; then
# When offering typo corrections, do not propose anything which starts
# with an underscore (such as many of zsh's shell functions).
export CORRECT_IGNORE='_*'
# Do not consider "/" a word character. One benefit of this is that
# when hitting ctrl-w in insert mode (which deletes the word before the
# cursor) just before a filesystem path, it only removes the last item
# of the path and not the entire thing.
export WORDCHARS=${WORDCHARS//\/}
fi
# ==============================================================================
# = XDG =
# ==============================================================================
export XDG_DESKTOP_DIR="${HOME}"
export XDG_DOCUMENTS_DIR="${HOME}"
export XDG_DOWNLOAD_DIR="${HOME}"
export XDG_MUSIC_DIR="${HOME}/.music"
export XDG_PICTURES_DIR="${HOME}"
export XDG_PUBLICSHARE_DIR="$HOME/public"
export XDG_RUNTIME_DIR="${HOME}/.run"
export XDG_TEMPLATES_DIR="${HOME}"
export XDG_VIDEOS_DIR="${HOME}"
export NO_AT_BRIDGE=1 # disable at-spi2-registryd daemon autostart
# ==============================================================================
# = runtime-directory =
# ==============================================================================
# Some programs are awkward to configure for things like socket location.
# Instead of fighting this, point them to a expected position then symlink the
# position to a desired area.
(
if [ -d /dev/shm/ ] && [ -w /dev/shm/ ] && [ -k /dev/shm/ ]
then
dir="/dev/shm/.$(id -un)"
elif [ -d /tmp/ ] && [ -w /tmp/ ] && [ -k /dev/shm/ ]
then
dir="/tmp/.$(id -un)"
else
dir="${HOME}/.cache/"
fi
umask 077
if ! [ -d "${dir}/" ]
then
mkdir -p "${dir}/"
chmod a+rx "${dir}/" # needed for sub-user sharing
fi
if ! [ -h "${XDG_RUNTIME_DIR}" ]
then
ln -fs "${dir}" "${XDG_RUNTIME_DIR}"
fi
)
if ! [ -d "/run/user/$(id -u)" ]; then
# requires ~/.gnupg/S.gpg-agent contains:
#
# %Assuan
# socket=/path/to/socket
#
# and gnupg v2.1.1 or above for gpg-agent to create the socket in the specified
# location
#
# *and* that /run/user isn't available
#
# GPG_AGENT_INFO is only respected by gpg-agent-connect.
export GPG_AGENT_INFO="${XDG_RUNTIME_DIR}/S.gpg-agent::1"
else
# if /run/user/$(id -u) is available, newer versions of gpg use it,
# ignoring any other configuration.
export GPG_AGENT_INFO="/run/user/$(id -u)/gnupg/S.gpg-agent::1"
fi
# specify socket path with -a flag
export SSH_AUTH_SOCK="${XDG_RUNTIME_DIR}/S.ssh-agent"
# dbus reads and uses this variable
export DBUS_SESSION_BUS_ADDRESS="unix:path=${XDG_RUNTIME_DIR}/S.dbus"
# pulseaudio creates socket at ${XDG_RUNTIME_DIR}/pulse/native
export PULSE_SERVER="unix:/dev/shm/.pulse/S.pulse"
# ==============================================================================
# = theme =
# ==============================================================================
if [ -r ~/.themes/current/terminal/256-theme ] && [ -n "${DISPLAY}" ] || [ "$TERM" = "xterm-256color" ]; then
source ~/.themes/current/terminal/256-theme
if [ "${EUID}" -eq "0" ]
then
"${is_bash}" && export PS1="\[\e[38;5;${ERROR_FOREGROUND}m\e[48;5;${ERROR_BACKGROUND}m#\e[0m\]\] "
"${is_zsh}" && export PROMPT="%F{$ERROR_FOREGROUND}%K{$ERROR_BACKGROUND}#%f%k "
elif [ -n "${SSH_CLIENT}" ] || [ -n "${SSH_TTY}" ] || [ -n "${SSH_CONNECTION}" ]
then
"${is_bash}" && export PS1="\[\e[38;5;${MISCELLANEOUS_FOREGROUND}m\e[48;5;${MISCELLANEOUS_BACKGROUND}m$\e[0m\]\] "
"${is_zsh}" && export PROMPT="%F{$MISCELLANEOUS_FOREGROUND}%K{$MISCELLANEOUS_BACKGROUND}$%f%k "
else
"${is_bash}" && export PS1="\[\e[38;5;${HIGHLIGHT_FOREGROUND}m\e[48;5;${HIGHLIGHT_BACKGROUND}m$\e[0m\]\] "
"${is_zsh}" && export PROMPT="%F{$HIGHLIGHT_FOREGROUND}%K{$HIGHLIGHT_BACKGROUND}$%f%k "
fi
# set the colors ls --color uses, if available
export LS_COLORS="\
di=38;5;${MISCELLANEOUS_FOREGROUND};48;5;${MISCELLANEOUS_BACKGROUND}:\
ex=38;5;${HIGHLIGHT_FOREGROUND};48;5;${HIGHLIGHT_BACKGROUND}:\
ln=04:\
mi=38;5;${ERROR_FOREGROUND};48;5;${ERROR_BACKGROUND}:\
su=38;5;${ERROR2_FOREGROUND};48;5;${ERROR2_BACKGROUND}:\
sg=38;5;${ERROR2_FOREGROUND};48;5;${ERROR2_BACKGROUND}:\
"
export GTK2_RC_FILES="${HOME}/.gtkrc-2.0"
export GTK_THEME="current"
export QT_STYLE_OVERRIDE=GTK+
fi
# ==============================================================================
# = aliases =
# ==============================================================================
# Take ownership of file or directory
alias mine="sudo chown -R $(whoami):$(whoami)"
# allow others to read/execute
alias yours="sudo find . -perm -u+x -exec chmod a+x {} \; && sudo find . -perm -u+r -exec chmod a+r {} \;"
alias ..="cd .."
alias :q="exit"
alias :wq="exit"
alias c="chore"
alias s="sudo"
alias sd="sudo poweroff"
alias sr="sudo reboot"
alias ss="slock & sudo suspend"
alias sx="conctl u gui-session & exit"
alias ta="tmux attach"
alias v="vim"
alias vs="vim --servername vim"
if [ -d "/dev/shm/" ]; then
export vv="/dev/shm/"
elif [ -d "/storage/emulated/0" ]; then
export vv="/storage/emulated/0"
fi
alias vv="${vv}"
alias 2pdf="libreoffice --headless --invisible --convert-to pdf"
alias df="df -h"
alias du="du -hs"
alias greps="grep -IR --color=yes -D skip --exclude-dir=.git"
alias la="ls -A --color=auto -h"
alias ll="ls -lA --color=auto -h"
alias ls="ls --color=auto -h"
alias octave="octave --silent"
alias xpdfr="xpdf -remote 127.0.0.1"
alias xpdfv="xpdf -rv"
rem() {
# $@ can be start date e.g. 2020-01-01
remind -cu12 -g -q ~/.reminders $@ |\
sed 's///' |\
vim - +'set buftype=nofile' '+/<TODAY>'
}
alias balance='ledger balance'
alias register='ledger register --no-revalued'
alias wine32='WINEPREFIX=~/.wine32/ WINEARCH=win32 wine'
alias ga="git add"
alias gb="git --no-pager branch"
alias gc="git commit -v"
alias gcd='([ -r "$(git rev-parse --git-dir)/lazycommit" ] || (echo "non-lazy repo, aborting"; false )) && git commit -a -v -m "$(date)"'
alias gco="git checkout"
alias gdh='git diff HEAD'
alias gf="git fetch"
alias gg="git grep --color"
alias gl="git log --no-merges"
alias gm="git merge"
alias gr="git reset --hard HEAD"
alias gs="git status"
alias gsync='gcd ; gull && guss'
alias gt="git rev-parse --show-toplevel"
alias gT='cd "$(git rev-parse --show-toplevel)"; pwd'
alias gul="git pull"
alias gull='git pull origin $(git branch | awk '\''/^\*/{print$2}'\'')'
alias gus="git push"
alias guss='git push origin $(git branch | awk '\''/^\*/{print$2}'\'')'
alias gusu='git submodule foreach git pull origin master'
alias gv='$EDITOR -- $(git status | awk "\$1 == \"modified:\" {print\$2}")'
alias gw="git show"
if [ -r ~/.mozilla/firefox/profiles.ini ]; then
for profile in $(awk -F= '/^Name=/{print$2}' ~/.mozilla/firefox/profiles.ini)
do
alias "fx-${profile}"="firefox -P $profile -no-remote"
done
fi
if [ -x /bedrock/bin/brl ]; then
for stratum in $(brl list -aA)
do
hash -d "${stratum}"="/bedrock/strata/${stratum}"
command -v "${stratum}" >/dev/null 2>&1 && continue
alias "${stratum}=strat ${stratum}"
alias "r${stratum}=strat -r ${stratum}"
alias "u${stratum}=strat -u ${stratum}"
alias "s${stratum}=sudo strat ${stratum}"
done
fi
if "${is_zsh}"; then
alias -g L="|less"
alias -g G="|grep"
alias -g B="&exit"
alias -g H="|head"
alias -g T="|tail"
alias -g C="|column -t"
alias -g V="|vim -m -c 'set nomod' -"
alias -g Q=">/dev/null 2>&1"
alias -s html=$BROWSER
alias -s htm=$BROWSER
alias -s org=$BROWSER
alias -s php=$BROWSER
alias -s com=$BROWSER
alias -s edu=$BROWSER
alias -s txt=$EDITOR
alias -s tex=$EDITOR
alias -s pdf=$PDFREADER
alias -s gz=tar -xzvf
alias -s bz2=tar -xjvf
alias mkdir="nocorrect mkdir"
alias cp="nocorrect cp"
alias mv="nocorrect mv"
alias ln="nocorrect ln"
fi
# ==============================================================================
# = completion =
# ==============================================================================
if "${is_zsh}"; then
if [ -x /bedrock/bin/brl ]; then
zcachedir="${HOME}/.zsh/$(hostname)-$(brl which -c)"
else
zcachedir="${HOME}/.zsh/$(hostname)"
fi
if ! [ -d "${CACHEDIR}" ]; then
mkdir -p "${zcachedir}"
fi
# Use completion functionality.
autoload -U compinit
compinit -d "${zcachedir}/zcompdump" 2>/dev/null
# Set the cache location.
zstyle ':completion:*' cache-path "${zcachedir}/cache"
# Use cache to speed completion up.
zstyle ':completion:*' use-cache on
# Do not require running "rehash" manually
zstyle ':completion:*' rehash true
# If the <tab> key is pressed with multiple possible options, print the
# options. If the options are printed, begin cycling through them.
zstyle ':completion:*' menu select
# Print the categories the completion options fit into.
zstyle ':completion:*:descriptions' format '%U%B%d%b%u'
# Organize into groups
zstyle ':completion:*' group-name ''
# Set format for warnings
zstyle ':completion:*:warnings' format 'No matches for: %d%b'
# Use colors when outputting file names for completion options.
zstyle ':completion:*' list-colors ${(s.:.)LS_COLORS}
# Do not prompt to cd into current directory.
# For example, cd ../<tab> should not prompt current directory.
zstyle ':completion:*:cd:*' ignore-parents parent pwd
# When using history-complete-(newer/older), complete with the first item on
# the first request (as opposed to 'menu select' which only shows the menu on
# the first request)
zstyle ':completion:history-words:*' menu yes
# Have zsh treat "mosh" like "ssh"
compdef mosh=ssh
# Have zsh treat "rerun" like "sudo"
compdef rerun=sudo
fi
# ==============================================================================
# = cwd-functions =
# ==============================================================================
#
# This is a family of functions for improved navigation the filesystem,
# superseding `cd` in specific situations.
#
# d(), u() and h() all take an argument which should be a substring of the
# desired filepath. To anchor the substring to the start or the end of the
# target directory name, put a "/" on the desired anchor point. Also supports
# using "/" as part of the substring to specify a parent directory.
#
# d() searches _d_ownward for the specified directory. It returns the match
# closest to the pwd (i.e. does a breadth-first search). If there is only one
# downward directory, it will go to it without any argument.
#
# u() searches _u_pward for the specified directory. It returns the match
# closest to the pwd (i.e. the deepest directory which matches).
# Alternatively, it could take a number which indicates the number of
# directories to go up.
#
# h() searches the directory stack for the specified directory. It returns the
# match closest to a previous pwd that is the most recent. This is most useful
# when paired with zsh's autopushd or equivalent.
#
# t() _t_ags the current pwd into a list of tagged directories. The first
# argument specifies the desired tag name. If no argument is used, it defaults
# to the basename.
#
# r() _r_estores the pwd to the most recent match from the tagged directories.
# Can use "/" as anchors for consistency with d(), u() and h(). No argument
# dumps the tagged directory list.
#
# Careful with special characters; globbing and regex are used under-the-hood.
d() {
# no arg. If there is a single downward directory, go to it.
if [ -z "${1}" ]; then
[ "$(ls -lA | grep -c '^d')" -eq 1 ] && cd "$(ls -lA | awk '/^d/{print$NF}')"
pwd
return
fi
# get argument for `find`
# -> last directory with "*" set for globbing on non-anchored sides
findarg="$(echo "${1}" | awk -F/ 'BEGIN{l="*";r="*"}$NF==""{NF--;r=""}{x=$NF}NF>1{l=""}END{print l""x""r}')"
# get argument for `grep`
# -> replace right anchor with "$"
greparg="$(echo "${1}" | sed 's/\/$/$/')"
# starting and ending depth. Gives up after maxdepth.
depth=1
maxdepth=10
# Find target directory.
#
# If there's no "/" other than trailing anchor and GNU find is
# available,
# use slightly faster version.
target=""
if ! echo "${greparg}" | grep -q "/" && find --version 2>&1| grep -q "GNU"; then
target="$(
while [ "${depth}" != "${maxdepth}" ] && ! find -mindepth "${depth}" -maxdepth "${depth}" -type d -name "${findarg}" -print -quit 2>/dev/null | grep "."; do
((depth++))
done
)"
else
# slightly slower version due to non-trailing-anchor "/" or
# find without -quit
target="$(
while [ "${depth}" != "${maxdepth}" ] && ! find -mindepth "${depth}" -maxdepth "${depth}" -type d -name "${findarg}" -print 2>/dev/null | grep "${greparg}"; do
((depth++))
done | head -n1
)"
fi
[ -n "${target}" ] && cd "${target}"
pwd
}
u() {
if [ -z "${1}" ]; then
# no argument, just act like `cd ..`
cd ..
elif [ "${1}" = "/" ]; then
# This is fairly meaningless in normal context. Treat as `cd /`
cd /
elif echo "${1}" | grep -q '[0-9]\+'; then
# `cd ..` the specified number of directories
cd "$(pwd | awk 'BEGIN{FS=OFS="/"}NF>'"${1}"'{NF-='"${1}"'}NF==1{print"/"}NF>1')"
else
# Go to matching parent directory
p="$(pwd)/"
if echo "${1}" | grep -q "/$"
then
# Ends in a slash, anchor right. Just cut out the fields we don't want.
cd "${p%"${1}"*}$1"
else
# Does not end in a slash, we need to expand the last directory.
# Use parameter expansion to cut out the part we don't want, then
# count the number of fields that exist with the remaining part.
fields="$(echo "${p%${1}*}$1" | awk -F/ '{print NF}')"
# Have awk simply print the desired number of fields
cd "$(pwd | awk -F/ 'BEGIN{IFS=OFS="/"}{NF='"${fields}"'}1')"
fi
fi
pwd
}
h() {
if [ -z "${1}" ]; then
# if no argument is specified, list the history
dirs -l -p
return
fi
if [ "${1}" = "/" ]; then
# This is fairly meaningless in normal context. Treat as `cd /`
cd /
return
fi
# iterate over lines in history
# append a "/" so the trailing anchor will match
target="$(dirs -l -p | sed 's/$/\//' | awk -v"pat=${1}" '
BEGIN {
OFS=FS="/"
# due to how awk fields work, at one point we do not want to consider
# trailing slash in pattern
if (pat ~ "/$") {
patx = substr(pat,1,length(pat)-1)
} else {
patx = pat
}
}
$0 ~ pat {
# strip away all the fields from the left that we can without breaking
# the pattern to ensure we have the right-most match.
line=$0
while ($0 ~ pat) {
pre_last=$0
# strip field from left
$0=substr($0,2)
$1=""
}
# strip away fields from the right to count how far from pwd the match
# is
$0=pre_last
rank=0
while ($0 ~ patx) {
NF--;
rank++;
}
# compare to current best
if (rank > bestrank) {
$0 = line
NF -= rank-1
bestrank = rank
bestline = $0
}
}
END {
print bestline
}
')"
[ -n "${target}" ] && cd "${target}"
pwd
}
t() {
if [ -z "${1}" ]; then
# if no argument is specified, use base name as tag
tag="$(basename $(pwd))"
else
tag="${1}"
fi
echo "${tag} $(pwd) >> ~/.dirtags"
echo "${tag} $(pwd)" >> ~/.dirtags
}
r() {
if [ -z "${1}" ]; then
# if no argument is specified, dump tags
column -t ~/.dirtags
return
fi
pat="$(echo "${1}" | sed 's/\/$/$/' | sed 's/^\//^/')"
target="$(awk -v"pat=${pat}" '$1 ~ pat{$1="";print substr($0,2);exit}' ~/.dirtags)"
[ -n "${target}" ] && cd "${target}"
pwd
}
# ==============================================================================
# = misc-functions =
# ==============================================================================
# use vim ex commands in a UNIX pipe
vp() {
vim - -u NONE -es '+1' "+$*" '+%print' '+:qa!' | tail -n +2
}
# use vim normal mode commands in a UNIX pipe
vn() {
vim - -u NONE -es '+1' "+normal $*" '+%print' '+:qa!' | tail -n +2
}
if "${is_bash}"; then
# implement zsh's autopushd
cd() {
if [ -z "${1}" ]; then
dir="${HOME}"
else
dir="${1}"
fi
builtin pushd "${dir}" >/dev/null
}
fi
# ==============================================================================
# = key-bindings =
# ==============================================================================
#
if "${is_bash}"; then
# Note that bash uses readline, and thus further bindings can be placed
# in
# ~/.inputrc
#
# Use vi-like insert/normal mode functionality at the command line.
# Bash's vi-mode is not sufficiently extendable, so this remains
# disabled.
#set -o vi
# By default, ctrl-w will delete the word before the cursor. This
# considers "/" a word-character which makes fixing typos in filesystem
# paths a pain. Meta-backspace acts as desired, but doesn't fit my
# muscle memory. This is a work around to make ctrl-w work like
# meta-backspace.
bind '"":""'
elif "${is_zsh}"; then
# Prepend "sudo" to the command line if it is not already there.
prepend-sudo() {
if ! echo "$BUFFER" | grep -q "^sudo "
then
BUFFER="sudo $BUFFER"
CURSOR+=5
fi
}
zle -N prepend-sudo
# Prepend "vim" to the command line if it is not already there.
prepend-vim() {
if ! echo "$BUFFER" | grep -q "^vim "
then
BUFFER="vim $BUFFER"
CURSOR+=5
fi
}
zle -N prepend-vim
# Delete all characters between a pair of characters. Mimics Vim's
# "di" text object functionality.
delete-in() {
# Create locally-scoped variables we'll need
local CHAR LCHAR RCHAR LSEARCH RSEARCH COUNT
# Read the character to indicate which text object we're deleting.
read -k CHAR
if [ "$CHAR" = "w" ]
then # diw, delete the word.
# find the beginning of the word under the cursor
zle vi-backward-word
# set the left side of the delete region at this point
LSEARCH=$CURSOR
# find the end of the word under the cursor
zle vi-forward-word
# set the right side of the delete region at this point
RSEARCH=$CURSOR
# Set the BUFFER to everything except the word we are removing.
RBUFFER="$BUFFER[$RSEARCH+1,${#BUFFER}]"
LBUFFER="$LBUFFER[1,$LSEARCH]"
return
# diw was unique. For everything else, we just have to define the
# characters to the left and right of the cursor to be removed
elif [ "$CHAR" = "(" ] || [ "$CHAR" = ")" ] || [ "$CHAR" = "b" ]
then # di), delete inside of a pair of parenthesis
LCHAR="("
RCHAR=")"
elif [ "$CHAR" = "[" ] || [ "$CHAR" = "]" ]
then # di], delete inside of a pair of square brackets
LCHAR="["
RCHAR="]"
elif [ $CHAR = "{" ] || [ $CHAR = "}" ] || [ "$CHAR" = "B" ]
then # di], delete inside of a pair of braces
LCHAR="{"
RCHAR="}"
else
# The character entered does not have a special definition.
# Simply find the first instance to the left and right of the
# cursor.
LCHAR="$CHAR"
RCHAR="$CHAR"
fi
# Find the first instance of LCHAR to the left of the cursor and the
# first instance of RCHAR to the right of the cursor, and remove
# everything in between.
# Begin the search for the left-sided character directly the
# left of the cursor.
LSEARCH=${#LBUFFER}
# Keep going left until we find the character or hit the
# beginning of the buffer.
while [ "$LSEARCH" -gt 0 ] && [ "$LBUFFER[$LSEARCH]" != "$LCHAR" ]; do
LSEARCH=$(expr $LSEARCH - 1)
done
# If we hit the beginning of the command line without finding
# the character, abort.
if [ "$LBUFFER[$LSEARCH]" != "$LCHAR" ]
then
return
fi
# start the search directly to the right of the cursor
RSEARCH=0
# Keep going right until we find the character or hit the end
# of the buffer.
while [ "$RSEARCH" -lt $(expr ${#RBUFFER} + 1 ) ] && [ "$RBUFFER[$RSEARCH]" != "$RCHAR" ]; do
RSEARCH=$(expr $RSEARCH + 1)
done
# If we hit the end of the command line without finding the
# character, abort.
if [ "$RBUFFER[$RSEARCH]" != "$RCHAR" ]
then
return
fi
# Set the BUFFER to everything except the text we are removing.
RBUFFER="$RBUFFER[$RSEARCH,${#RBUFFER}]"
LBUFFER="$LBUFFER[1,$LSEARCH]"
}
zle -N delete-in
# Delete all characters between a pair of characters and then go to
# insert mode. Mimics Vim's "ci" text object functionality.
change-in() {
zle delete-in
zle vi-insert
}
zle -N change-in
# Delete all characters between a pair of characters as well as the
# surrounding characters themselves. Mimics Vim's "da" text object
# functionality.
delete-around() {
zle delete-in
zle vi-backward-char
zle vi-delete-char
zle vi-delete-char
}
zle -N delete-around
# Delete all characters between a pair of characters as well as the
# surrounding characters themselves and then go into insert mode
# Mimics Vim's "ca" text object functionality.
change-around() {
zle delete-in
zle vi-backward-char
zle vi-delete-char
zle vi-delete-char
zle vi-insert
}
zle -N change-around
# Increment the number under the cursor, or find the next number to the
# right of the cursor and increment that number. Emulate vim's ctrl-a
# functionality. This code is not my style at all; presumably I found
# it somewhere online, but I no longer remember the source to cite or
# credit.
increment-number() {
emulate -L zsh
setopt extendedglob
local pos num newnum sign buf
if [[ $BUFFER[$((CURSOR + 1))] = [0-9] ]]; then
pos=$((${#LBUFFER%%[0-9]##} + 1))
else
pos=$(($CURSOR + ${#RBUFFER%%[0-9]*} + 1))
fi
(($pos <= ${#BUFFER})) || return
num=${${BUFFER[$pos,-1]}%%[^0-9]*}
if ((pos > 0)) && [[ $BUFFER[$((pos - 1))] = '-' ]]; then
num=$((0 - num))
((pos--))
fi
newnum=$((num + ${NUMERIC:-${incarg:-1}}))
if ((pos > 1)); then
buf=${BUFFER[0,$((pos - 1))]}${BUFFER[$pos,-1]/$num/$newnum}
else
buf=${BUFFER/$num/$newnum}
fi
BUFFER=$buf
CURSOR=$((pos + $#newnum - 2))
}
zle -N increment-number
# Decrement the number under the cursor, or find the next number to the
# right of the cursor and increment that number. Emulate vim's ctrl-x
# functionality. This code is not my style at all; presumably I found
# it somewhere online, but I no longer remember the source to cite or
# credit.
decrement-number() {
emulate -L zsh
setopt extendedglob
local pos num newnum sign buf
if [[ $BUFFER[$((CURSOR + 1))] = [0-9] ]]; then
pos=$((${#LBUFFER%%[0-9]##} + 1))
else
pos=$(($CURSOR + ${#RBUFFER%%[0-9]*} + 1))
fi
(($pos <= ${#BUFFER})) || return
num=${${BUFFER[$pos,-1]}%%[^0-9]*}
if ((pos > 0)) && [[ $BUFFER[$((pos - 1))] = '-' ]]; then
num=$((0 - num))
((pos--))
fi
newnum=$((num - ${NUMERIC:-${incarg:-1}}))
if ((pos > 1)); then
buf=${BUFFER[0,$((pos - 1))]}${BUFFER[$pos,-1]/$num/$newnum}
else
buf=${BUFFER/$num/$newnum}
fi
BUFFER=$buf
CURSOR=$((pos + $#newnum - 2))
}
zle -N decrement-number
# Zsh's history-beginning-search-backward is very close to Vim's
# i_ctrl-x_ctrl-l; however, with Vim, it leaves you in insert mode with
# the cursor at the end of the line. Surprisingly, there was nothing
# closer to Vim in Zsh by default. This creates something closer to
# Vim's i_ctrl-x_ctrl-l.
history-beginning-search-backward-then-append() {
zle history-beginning-search-backward
zle vi-add-eol
}
zle -N history-beginning-search-backward-then-append
# Use vi-style keybindings
bindkey -v
# Minimize escape sequence detection delay
export KEYTIMEOUT=1
# Completely remove escape key detection delay. Disabled because this
# breaks bracketed paste detection.
#
## Remove escape timeout in insert mode
#bindkey -rpM viins '^['
#
## Remove escape timeout in normal mode
#bindkey -rpM vicmd '^['
# Have i_backspace work as it does in Vim.
bindkey -M viins "^?" backward-delete-char
# Have i_ctrl-a work as it does in Vim.
bindkey -M viins "^A" beginning-of-line
# Have i_ctrl-p work as c_ctrl-p does in Vim.
bindkey -M viins "^P" up-line-or-history
# Have i_ctrl-e work as it does in Vim.
bindkey -M viins "^E" end-of-line
# Have i_ctrl-n work as c_ctrl-n does in Vim.
bindkey -M viins "^N" down-line-or-history
# Have i_ctrl-h work as it does in Vim.
bindkey -M viins "^H" backward-delete-char
# Have i_ctrl-b work as i_ctrl-p does in Vim.
bindkey -M viins "^B" _history-complete-newer
# Have i_ctrl-f work as i_ctrl-n does in Vim.
bindkey -M viins "^F" _history-complete-older
# Prepend "sudo ". This does not have a Vim parallel.
bindkey "^S" prepend-sudo
# Prepend "vim ". This does not have a Vim parallel.
bindkey "^V" prepend-vim
# Have i_ctrl-u work as it does in Vim.
bindkey -M viins "^U" backward-kill-line
# Have i_ctrl-w work as it does in Vim.
bindkey -M viins "^W" backward-kill-word
# Have i_ctrl-x_i_ctrl-l work as it does in Vim.
bindkey -M viins "^X^L" history-beginning-search-backward-then-append
# Display _completion_help for creating completion functions. This
# does not have a Vim parallel.
bindkey -M viins "^X^H" _complete_help
# attempt to complete line based on history, roughly as i_ctrl-x_ctrl-l
# does in Vim.
bindkey -M viins "^X^L" history-incremental-search-backward
# Cut the contents of the line and paste immediately when the next
# prompt appears. This does not have a clean Vim parallel.
bindkey -M viins "^Y" push-line
# Have ctrl-a work as it does in Vim.
bindkey -M vicmd "^A" increment-number
# Mimics Vim's "ca" text object functionality.
bindkey -M vicmd "ca" change-around
# Mimics Vim's "ci" text object functionality.
bindkey -M vicmd "ci" change-in
# If not explicitly set, above ca/ci bindings will cause a delay
bindkey -M vicmd "cc" vi-change-whole-line
# Mimic Vim's da text-object functionality.
bindkey -M vicmd "da" delete-around
# Mimic Vim's di text-object functionality.
bindkey -M vicmd "di" delete-in
#
# If not explicitly set, above da/di bindings will cause a delay
bindkey -M vicmd "dd" kill-whole-line
# Have ctrl-e work as it does in Vim.
bindkey -M vicmd "^E" vi-add-eol
# Have g~ work as it does in Vim.
bindkey -M vicmd "g~" vi-oper-swap-case
# Have ga work as it does in Vim.
bindkey -M vicmd "ga" what-cursor-position
# Have gg work as it does in Vim.
bindkey -M vicmd "gg" beginning-of-history
# Have G work as it does in Vim.
bindkey -M vicmd "G" end-of-history
# Have ctrl-r work as it does in Vim.
bindkey -M vicmd "^R" redo
# Editing the line in Vim proper.
autoload edit-command-line
zle -N edit-command-line
bindkey -M vicmd "^V" edit-command-line
bindkey -M vicmd v edit-command-line
# Have ctrl-a work as it does in Vim.
bindkey -M vicmd "^X" decrement-number
# Breadth-first search for file or directory in pwd that matches the
# provided pattern.
#
# first arg is file type as is interpreted by `find -type`
#
# second arg is pattern for which to search
# - pattern is a substring of target file/filepath
# - a leading or trailing slash can be used as an anchor
# - a starting slash indicates a file path separator and should make
# intuitive sense
# - cannot end a file/directory name on the filesystem with a slash,
# so the trailing one can't conflict with anything and is easier to
# type than, say, "$" on US-qwerty keyboards. If you want to select
# any file in a directory, use "/*"
# - "/" can be used mid-pattern to indicate a file path separator to, for
# example, describe the parent directory of the target file.
# - a "*" can match any number of non-"/" characters
# - a "**" can match any number of any character
#
expand_fuzz() {
# ensure there's something for which to search