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