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