]>
Commit | Line | Data |
---|---|---|
1 | ### options | |
2 | setopts=( | |
3 | ## 16.2.1 Changing Directories | |
4 | AUTO_CD CDABLE_VARS | |
5 | ## 16.2.2 Completion | |
6 | COMPLETE_IN_WORD GLOB_COMPLETE REC_EXACT | |
7 | ## 16.2.3 Expansion and Globbing | |
8 | EXTENDED_GLOB GLOB_DOTS GLOB_STAR_SHORT MAGIC_EQUAL_SUBST MARK_DIRS | |
9 | NUMERIC_GLOB_SORT | |
10 | ## 16.2.4 History | |
11 | # NOTE: NO_HIST_SAVE_BY_COPY to allow saving histfile if updating another | |
12 | # user's histfile. this is for compatibility with zsu. | |
13 | HIST_FCNTL_LOCK HIST_IGNORE_DUPS HIST_IGNORE_SPACE HIST_LEX_WORDS | |
14 | HIST_NO_STORE HIST_REDUCE_BLANKS NO_HIST_SAVE_BY_COPY SHARE_HISTORY | |
15 | ## 16.2.6 Input/Output | |
16 | NO_CLOBBER CLOBBER_EMPTY CORRECT_ALL INTERACTIVE_COMMENTS | |
17 | HASH_EXECUTABLES_ONLY | |
18 | ## 16.2.7 Job Control | |
19 | AUTO_CONTINUE LONG_LIST_JOBS | |
20 | ## 16.2.12 Zle | |
21 | NO_BEEP | |
22 | ) | |
23 | setopt $setopts | |
24 | unset setopts | |
25 | ||
26 | ### exports | |
27 | ## common | |
28 | export EDITOR=${$(whence -p nvim vim vi micro nano emacs)[(f)1]} | |
29 | export PAGER=${$(whence -p less micro nano more)[(f)1]:s/micro/& -readonly true -multiopen tab/:s/nano/& --view} | |
30 | ## grep | |
31 | # XXX: deprecated in GNU | |
32 | export GREP_OPTIONS=--color=auto | |
33 | ## histfile | |
34 | export HISTFILE=~/.histfile | |
35 | export HISTSIZE=10000 | |
36 | export SAVEHIST=$HISTSIZE | |
37 | ## python | |
38 | export PYTHONSTARTUP=${XDG_CONFIG_HOME:-~/.config}/pythonrc | |
39 | ## vim | |
40 | export VIMINIT='let$MYVIMRC=($XDG_CONFIG_HOME??($HOME."/.config"))."/vim/.vimrc"|execute"source"$MYVIMRC' | |
41 | ||
42 | ### imports | |
43 | autoload -Uz zmv | |
44 | autoload -Uz zmathfunc && zmathfunc | |
45 | ||
46 | ## vcs | |
47 | zstyle ':vcs_info:*' enable git | |
48 | #zstyle ':vcs_info:git*' check-for-changes true #too slow | |
49 | zstyle ':vcs_info:git*:dotfiles' check-for-changes true | |
50 | zstyle ':vcs_info:git*' check-for-staged-changes true | |
51 | autoload -Uz vcs_info | |
52 | ||
53 | ## compinit | |
54 | zstyle ':completion:*' auto-description '[arg] %d' | |
55 | zstyle ':completion:*' expand suffix | |
56 | zstyle ':completion:*' format '# %d' | |
57 | zstyle ':completion:*' group-name '' | |
58 | zstyle ':completion:*' ignore-parents parent | |
59 | zstyle ':completion:*' insert-unambiguous false | |
60 | zstyle ':completion:*' list-colors ${(s.:.)LS_COLORS} | |
61 | zstyle ':completion:*' list-prompt '%B%i%b' | |
62 | zstyle ':completion:*' list-suffixes true | |
63 | zstyle ':completion:*' matcher-list '' 'm:{[:lower:]}={[:upper:]}' 'm:{[:lower:][:upper:]}={[:upper:][:lower:]} r:|[._-]=* r:|=*' 'm:{[:lower:][:upper:]}={[:upper:][:lower:]} l:|=* r:|=*' | |
64 | zstyle ':completion:*' menu select=1 | |
65 | zstyle ':completion:*' original false | |
66 | zstyle ':completion:*' select-prompt '%B%l%b' | |
67 | zstyle ':completion:*' verbose true | |
68 | autoload -Uz compinit | |
69 | cache=${XDG_CACHE_HOME:-~/.cache}/zsh | |
70 | [[ -d $cache ]] || mkdir -p $cache | |
71 | compinit -d $cache/.zcompdump | |
72 | unset cache | |
73 | ||
74 | ### keys | |
75 | ## set up zkbd-style key array | |
76 | if [[ ! -v _sev_force_zkbd && ( -v terminfo || -v termcap ) ]] { | |
77 | # use application (keypad transmit) mode if the terminal supports it | |
78 | # NOTE: we have to do this because termcap/terminfo keys are usually | |
79 | # defined in application mode. terminals that do not define these | |
80 | # capabilities are assumed to always be in application mode. | |
81 | if [[ ( -v terminfo[smkx] && -v terminfo[rmkx] ) || | |
82 | ( -v termcap[ks] && -v terminfo[ke] ) ]] { | |
83 | autoload -Uz add-zle-hook-widget | |
84 | function _enter-application-mode { | |
85 | [[ -v terminfo[smkx] ]] && echoti smkx || echotc ks | |
86 | } | |
87 | add-zle-hook-widget line-init _enter-application-mode | |
88 | function _exit-application-mode { | |
89 | [[ -v terminfo[rmkx] ]] && echoti rmkx || echotc ke | |
90 | } | |
91 | add-zle-hook-widget line-finish _exit-application-mode | |
92 | trap _exit-application-mode EXIT | |
93 | } | |
94 | # pull keys from terminfo/termcap | |
95 | # TODO: Menu and more Shift- modifiers | |
96 | typeset -gA key | |
97 | typeset -lA newkey | |
98 | if [[ -v terminfo ]] { | |
99 | src=terminfo | |
100 | newkey=(F1 kf1 F2 kf2 F3 kf3 F4 kf4 F5 kf5 F6 kf6 F7 kf7 F8 kf8 F9 kf9 | |
101 | F10 kf10 F11 kf11 F12 kf12 | |
102 | Backspace kbs | |
103 | Shift-Tab kcbt | |
104 | Insert kich1 | |
105 | Home khome | |
106 | PageUp kpp | |
107 | Delete kdch1 | |
108 | End kend | |
109 | PageDown knp | |
110 | Up kcuu1 | |
111 | Down kcud1 | |
112 | Left kcub1 | |
113 | Right kcuf1 | |
114 | ) | |
115 | } elif [[ -v termcap ]] { | |
116 | src=termcap | |
117 | newkey=(F1 k1 F2 k2 F3 k3 F4 k4 F5 k5 F6 k6 F7 k7 F8 k8 F9 k9 | |
118 | F10 k\; F11 F1 F12 F2 | |
119 | Backspace kb | |
120 | Shift-Tab kB | |
121 | Insert kI | |
122 | Home kh | |
123 | PageUp kP | |
124 | Delete kD | |
125 | End @7 | |
126 | PageDown kN | |
127 | Up ku | |
128 | Down kd | |
129 | Left kl | |
130 | Right kr | |
131 | ) | |
132 | } | |
133 | for k v (${(kv)newkey}) { | |
134 | key[$k]=${${(P)src}[$v]} | |
135 | } | |
136 | unset k v src newkey | |
137 | } else { | |
138 | # use zkbd if termcap/terminfo unavailable | |
139 | function find_keymap { | |
140 | for f in ${ZDOTDIR:-~}/.zkbd/$TERM{-${DISPLAY:-$VENDOR-$OSTYPE},} | |
141 | [[ -f $f ]] && keymap=$f && break | |
142 | } | |
143 | find_keymap | |
144 | if [[ -z $keymap ]] { | |
145 | if read -q "?Can't read terminfo. Add new zkbd keymap? [y/N]"; then | |
146 | echo | |
147 | autoload -Uz zkbd && zkbd | |
148 | unfunction zkbd | |
149 | find_keymap | |
150 | fi | |
151 | echo | |
152 | } | |
153 | if [[ -n $keymap ]] { | |
154 | source $keymap | |
155 | } else { | |
156 | echo -E "Failed to source keymap file $keymap" >&2 | |
157 | } | |
158 | unfunction find_keymap; unset keymap | |
159 | } | |
160 | ||
161 | ## load history search | |
162 | autoload -Uz up-line-or-beginning-search down-line-or-beginning-search | |
163 | zle -N up-line-or-beginning-search | |
164 | zle -N down-line-or-beginning-search | |
165 | ||
166 | ## bind keys in both viins and vicmd modes | |
167 | # NOTE: cursor keys are bound to normal/raw/nontransmit mode strings by | |
168 | # default, and keys like Home and End are not bound at all, so we rebind | |
169 | # everything to cover all scenarios | |
170 | typeset -A a | |
171 | a=( | |
172 | #key viins vicmd | |
173 | Backspace 'backward-delete-char vi-backward-char' | |
174 | Insert 'overwrite-mode vi-insert' | |
175 | Home 'beginning-of-line' | |
176 | PageUp 'up-history -' | |
177 | Delete 'delete-char' | |
178 | End 'end-of-line' | |
179 | PageDown 'down-history -' | |
180 | Up 'up-line-or-beginning-search vi-up-line-or-history' | |
181 | Down 'down-line-or-beginning-search vi-down-line-or-history' | |
182 | Left 'backward-char' | |
183 | Right 'forward-char' | |
184 | ) | |
185 | for k v (${(kv)a}) { | |
186 | k=$key[$k] | |
187 | if [[ -z $k ]] { continue } | |
188 | v=($=v) | |
189 | bindkey -- $k $v[1] | |
190 | if [[ $v[2] == - ]] { | |
191 | # copy viins to vicmd verbatim | |
192 | bindkey -a -- $k $v[1] | |
193 | } elif (( $#v != 1 )) { | |
194 | # set vicmd to any other value | |
195 | bindkey -a -- $k $v[2] | |
196 | } else { | |
197 | # copy viins to vicmd and prepend vi- to it | |
198 | bindkey -a -- $k vi-$v[1] | |
199 | } | |
200 | } | |
201 | unset a k v | |
202 | ||
203 | ## fzf | |
204 | # bash-style reverse-search-history (i.e. reverse-i-search) | |
205 | if [[ -v commands[fzf] ]] { | |
206 | function _history-incremental-pattern-search-fzf { | |
207 | l=(${(f)"$(fc -li -1 0 | fzf -e -m -n 1 +s --preview-window=hidden ${BUFFER:+-q $BUFFER})"}) | |
208 | l=$(for x ("${l[@]}") { echo ${${=x}:3}; }) | |
209 | BUFFER="$l" | |
210 | } | |
211 | zle -N _history-incremental-pattern-search-fzf | |
212 | bindkey '^R' _history-incremental-pattern-search-fzf | |
213 | } else { | |
214 | bindkey '^R' history-incremental-pattern-search-backward | |
215 | } | |
216 | ||
217 | ### aliases | |
218 | ## builtins | |
219 | alias rehash='_sev_setpath; rehash' | |
220 | ## utils | |
221 | alias h='fc -l -25' | |
222 | alias j='jobs -l' | |
223 | alias l='ls -AF' | |
224 | if [[ "$OSTYPE" =~ '^(free|net)bsd' ]] { | |
225 | alias ll='ls -lAFho' | |
226 | } else { | |
227 | alias ll='ls -lAFh' | |
228 | } | |
229 | alias p=\$PAGER | |
230 | alias e=\$EDITOR | |
231 | alias se=sudoedit | |
232 | alias syncwatch='sync & watch -d grep -Fe Dirty: -e Writeback: /proc/meminfo' | |
233 | if [[ -v commands[grep] ]] { | |
234 | [[ -v commands[fgrep] ]] || alias fgrep='grep -F' | |
235 | [[ -v commands[egrep] ]] || alias fgrep='grep -E' | |
236 | } | |
237 | for x (cat cmp diff grep test update) { | |
238 | [[ -v commands[zutils-z$x] ]] || alias z$x=zutils-z$x | |
239 | } | |
240 | # be paranoid | |
241 | alias cp='cp -ip' | |
242 | alias mv='mv -i' | |
243 | # zsh zmv with noglob wildcards | |
244 | alias zm='noglob zmv -WiM' | |
245 | alias zc='noglob zmv -WiC' | |
246 | alias zl='noglob zmv -WiL' | |
247 | alias sm='noglob zmv -Wip"sudo mv"' | |
248 | alias sc='noglob zmv -Wip"sudo cp"' | |
249 | alias sl='noglob zmv -Wip"sudo ln"' | |
250 | if [[ "$OSTYPE" =~ '^freebsd' ]] { | |
251 | # don't confirm if only a few files are deleted | |
252 | alias rm='rm -I' | |
253 | } else { | |
254 | # TODO: similar behavior for non-freebsd, or impliment in zsh | |
255 | alias rm='rm -i' | |
256 | } | |
257 | [[ -v commands[trash-put] ]] && alias t=trash-put | |
258 | # ps | |
259 | if [[ -v commands[pstree] && $commands[pstree]:A:t != busybox ]] { | |
260 | # use pstree, but NOT busybox pstree because it kinda sucks | |
261 | ps='pstree -wg3' | |
262 | } elif [[ "$OSTYPE" =~ '^freebsd' ]] { | |
263 | ps='ps -aSdfxwwouser=USR -ogroup=GRP -opid,nice=NI \ | |
264 | -o%cpu,%mem,tty,stat,start=START -oetime,command' | |
265 | } elif [[ $commands[ps]:A:t == busybox ]] { | |
266 | # busybox compatible | |
267 | ps="ps -eouser='USR ' -ogroup='GRP ' \ | |
268 | -opid=' PID' -onice=' NI' -ovsz=' MEM' \ | |
269 | -otty,stat,etime,comm" | |
270 | } else { | |
271 | # XXX: untested, posix | |
272 | # TODO: support gnu ps | |
273 | ps='ps -eouser=USR -ogroup=GRP -opid,nice=NI \ | |
274 | -opcpu=CPU -ovsz=MEM -otty,stat,etime,comm' | |
275 | } | |
276 | if [[ "$(basename "$PAGER")" = "less" ]] { | |
277 | ps="$ps | less -S" | |
278 | } else { | |
279 | ps="$ps | \"${PAGER:-more}\"" | |
280 | } | |
281 | alias pa=$ps | |
282 | alias spa="sudo $ps" | |
283 | unset ps | |
284 | ## py venv | |
285 | alias va='source bin/activate' | |
286 | alias vd=deactivate | |
287 | alias vu="python3 -mvenv --upgrade" | |
288 | alias svu="sudo python3 -mvenv --upgrade" | |
289 | ## git | |
290 | alias g=git | |
291 | alias gd='git diff' | |
292 | alias gdh='git diff HEAD' | |
293 | alias gds='git diff --staged' | |
294 | alias ga='git add' | |
295 | alias ga.='git add .' | |
296 | alias gai='git add -i' | |
297 | alias gap='git add -p' | |
298 | alias gc='git commit' | |
299 | alias gca='git commit --amend' | |
300 | alias gp='git push' | |
301 | alias gu='git pull' | |
302 | alias gl='git log' | |
303 | alias gt='git tree' # from gitconfig | |
304 | alias gsh='git show' | |
305 | alias gst='git status' | |
306 | alias gs='git stash' | |
307 | alias gsp='git stash pop' | |
308 | ## cd/zoxide | |
309 | function up { | |
310 | \cd $(printf '../%.0s' {1..${1:-1}}) | |
311 | } | |
312 | alias u=up | |
313 | if [[ -v commands[zoxide] ]] { | |
314 | # https://github.com/ajeetdsouza/zoxide/issues/513 | |
315 | eval "${$(zoxide init zsh):s#_files -/#_cd#}" | |
316 | alias cd=z | |
317 | } | |
318 | alias cd..=up | |
319 | ## dotfiles | |
320 | alias dfu='function { | |
321 | pushd -q ${$(echo -E - ~/.zshenv):P:h:h} | |
322 | git pull && | |
323 | git submodule init && | |
324 | git submodule sync && | |
325 | git submodule update | |
326 | popd -q | |
327 | }' | |
328 | ## nocorrect | |
329 | # zsh doesnt really handle sudo very well, so ignore it | |
330 | alias sudo='nocorrect sudo' | |
331 | ## docker compose | |
332 | alias dcp='sudo docker compose pull' | |
333 | alias dcu='sudo docker compose up -d' | |
334 | alias dcr='sudo docker compose restart' | |
335 | alias dcl='sudo docker compose logs -f' | |
336 | ||
337 | ### hooks | |
338 | autoload -Uz add-zsh-hook | |
339 | typeset -gi _sev_exectime | |
340 | function sev_preexec { | |
341 | # change terminal title to show command | |
342 | print -n "\e]2;$(print -P '%#')${SSH_CLIENT+$USER@$HOST:}$1\e\\" | |
343 | # save last exec time for bell | |
344 | # XXX: does not run for blank cmdline | |
345 | _sev_exectime=$SECONDS | |
346 | # update gpg forward, to always have unique filename and avoid clashes | |
347 | if [[ -v _GNUPG_SOCK_DEST_EXT ]] { | |
348 | export _GNUPG_SOCK_DEST_EXT=$(date +%s).$RANDOM | |
349 | export _GNUPG_SOCK_DEST=$_GNUPG_SOCK_DEST_BASE.$_GNUPG_SOCK_DEST_EXT | |
350 | } | |
351 | } | |
352 | add-zsh-hook preexec sev_preexec | |
353 | function sev_precmd { | |
354 | # change terminal title | |
355 | # TODO: update and send BEL when job status changes | |
356 | print -Pn "\e]2;%(1j,%j,)%#${SSH_CLIENT+$USER@$HOST:}%~\e\\" | |
357 | # bell if exec takes 5s | |
358 | if (( SECONDS - _sev_exectime >= 5 )) print "\a" | |
359 | # we could update vcs_info here, but let prompt take care of it | |
360 | # if it doesn't use vcs, it can be ignored safely | |
361 | } | |
362 | add-zsh-hook precmd sev_precmd | |
363 | function sev_chpwd { | |
364 | # echo dir on cwd change | |
365 | ls -AF | |
366 | } | |
367 | add-zsh-hook chpwd sev_chpwd | |
368 | ||
369 | ### system-specific configs and aliases | |
370 | case $OSTYPE; in | |
371 | freebsd*) | |
372 | ## colors | |
373 | export CLICOLOR= | |
374 | export LS_COLORS='di=34:ln=35:so=32:pi=33:ex=31:bd=46;34:cd=43;34:su=41;30:sg=46;30:tw=42;30:ow=43;30' | |
375 | ||
376 | ## sound | |
377 | function s { sysctl hw.snd.default_unit${1:+\=$1} } | |
378 | alias vol mixer | |
379 | ||
380 | ## install port dependencies from pkg (like pkgsrc `bmake bin-install') | |
381 | # XXX: should probably use package-depends where possible, breaks when | |
382 | # port name is different to package name | |
383 | # (eg. graphics/sdl20 == sdl2, devel/glib20 == glib2, etc) | |
384 | function portpkg { | |
385 | case "$1" { | |
386 | build|run) | |
387 | sudo pkg install -AU $(make ${1}-depends-list | | |
388 | sed 's_/usr/ports/_ _' | tr -d '\n') | |
389 | ;; | |
390 | *) echo "Usage: \`portpkg <build|run>' in a port directory" | |
391 | return 1;; | |
392 | } | |
393 | };; | |
394 | netbsd) | |
395 | ## sound | |
396 | function s { | |
397 | if [[ -z "$1" ]] { | |
398 | ll /dev/mixer /dev/sound /dev/audio | |
399 | return | |
400 | } | |
401 | for x in mixer sound audio; do | |
402 | ln -sf /dev/$x"$1" /dev/$x | |
403 | done | |
404 | } | |
405 | function vol { | |
406 | if [[ -z "$1" ]] { | |
407 | for x in $(mixerctl -a | grep 'outputs\.master'); do | |
408 | echo -E - $x | |
409 | done | |
410 | return | |
411 | } | |
412 | mixerctl -w outputs.master"$2"="$1" | |
413 | };; | |
414 | *) | |
415 | ## sound | |
416 | # TODO: test alsa/oss/sndio/portaudio/pulse/pipewire in order of importance | |
417 | function s {} | |
418 | function vol {} | |
419 | esac | |
420 | ||
421 | ### prompt | |
422 | autoload -Uz promptinit && promptinit | |
423 | prompt arrows | |
424 | ||
425 | ### plugins | |
426 | load-plugins zshrc | |
427 | ||
428 | ### load site-specific | |
429 | load-site-dotfile zshrc |