]> git.sev.monster Git - dotfiles.git/blob - etc/zsh/.zshrc
zshrc: enable EXTENDED_HISTORY
[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             autoload -Uz zkbd && zkbd
149             unfunction zkbd
150             find_keymap
151         }
152         echo
153     }
154     if [[ -n $keymap ]] {
155         source $keymap
156     } else {
157         echo -E "Failed to source keymap file $keymap" >&2
158     }
159     unfunction find_keymap; unset keymap
160 }
161
162 ## load history search
163 autoload -Uz up-line-or-beginning-search down-line-or-beginning-search
164 zle -N up-line-or-beginning-search
165 zle -N down-line-or-beginning-search
166
167 ## bind keys in both viins and vicmd modes
168 # NOTE: cursor keys are bound to normal/raw/nontransmit mode strings by
169 #       default, and keys like Home and End are not bound at all, so we rebind
170 #       everything to cover all scenarios
171 typeset -A a
172 a=(
173     #key       viins                         vicmd
174     Backspace 'backward-delete-char          vi-backward-char'
175     Insert    'overwrite-mode                vi-insert'
176     Home      'beginning-of-line'
177     PageUp    'up-history                    -'
178     Delete    'delete-char'
179     End       'end-of-line'
180     PageDown  'down-history                  -'
181     Up        'up-line-or-beginning-search   vi-up-line-or-history'
182     Down      'down-line-or-beginning-search vi-down-line-or-history'
183     Left      'backward-char'
184     Right     'forward-char'
185 )
186 for k v (${(kv)a}) {
187     k=$key[$k]
188     if [[ -z $k ]] { continue }
189     v=($=v)
190     bindkey -- $k $v[1]
191     if [[ $v[2] == - ]] {
192         # copy viins to vicmd verbatim
193         bindkey -a -- $k $v[1]
194     } elif (( $#v != 1 )) {
195         # set vicmd to any other value
196         bindkey -a -- $k $v[2]
197     } else {
198         # copy viins to vicmd and prepend vi- to it
199         bindkey -a -- $k vi-$v[1]
200     }
201 }
202 unset a k v
203
204 ## fzf
205 # bash-style reverse-search-history (i.e. reverse-i-search)
206 if [[ -v commands[fzf] ]] {
207     function _history-incremental-pattern-search-fzf {
208         l=(${(f)"$(fc -li -1 0 | fzf -e -m -n 1 +s --preview-window=hidden ${BUFFER:+-q $BUFFER})"})
209         l=$(for x ("${l[@]}") { echo ${${=x}:3}; })
210         BUFFER="$l"
211     }
212     zle -N _history-incremental-pattern-search-fzf
213     bindkey '^R' _history-incremental-pattern-search-fzf
214 } else {
215     bindkey '^R' history-incremental-pattern-search-backward
216 }
217
218 ### aliases
219 ## builtins
220 alias rehash='_sev_setpath; rehash'
221 ## utils
222 alias h='fc -l -25'
223 alias j='jobs -l'
224 alias l='ls -AF'
225 if [[ "$OSTYPE" =~ '^(free|net)bsd' ]] {
226     alias ll='ls -lAFho'
227 } else {
228     alias ll='ls -lAFh'
229 }
230 alias p=\$PAGER
231 alias e=\$EDITOR
232 alias se=sudoedit
233 alias syncwatch='sync & watch -d grep -Fe Dirty: -e Writeback: /proc/meminfo'
234 if [[ -v commands[grep] ]] {
235     [[ -v commands[fgrep] ]] || alias fgrep='grep -F'
236     [[ -v commands[egrep] ]] || alias fgrep='grep -E'
237 }
238 for x (cat cmp diff grep test update) {
239     [[ -v commands[zutils-z$x] ]] || alias z$x=zutils-z$x
240 }
241 # be paranoid
242 alias cp='cp -ip'
243 alias mv='mv -i'
244 # zsh zmv with noglob wildcards
245 alias zm='noglob zmv -WiM'
246 alias zc='noglob zmv -WiC'
247 alias zl='noglob zmv -WiL'
248 alias sm='noglob zmv -Wip"sudo mv"'
249 alias sc='noglob zmv -Wip"sudo cp"'
250 alias sl='noglob zmv -Wip"sudo ln"'
251 if [[ "$OSTYPE" =~ '^freebsd' ]] {
252     # don't confirm if only a few files are deleted
253     alias rm='rm -I'
254 } else {
255     # TODO: similar behavior for non-freebsd, or impliment in zsh
256     alias rm='rm -i'
257 }
258 [[ -v commands[trash-put] ]] && alias t=trash-put
259 # ps
260 if [[ -v commands[pstree] && $commands[pstree]:A:t != busybox ]] {
261     # use pstree, but NOT busybox pstree because it kinda sucks
262     ps='pstree -wg3'
263 } elif [[ "$OSTYPE" =~ '^freebsd' ]] {
264     ps='ps -aSdfxwwouser=USR -ogroup=GRP -opid,nice=NI \
265        -o%cpu,%mem,tty,stat,start=START -oetime,command'
266 } elif [[ $commands[ps]:A:t == busybox ]] {
267     # busybox compatible
268     ps="ps -eouser='USR     ' -ogroup='GRP     ' \
269        -opid='  PID' -onice=' NI' -ovsz=' MEM' \
270        -otty,stat,etime,comm"
271 } else {
272     # XXX: untested, posix
273     # TODO: support gnu ps
274     ps='ps -eouser=USR -ogroup=GRP -opid,nice=NI \
275        -opcpu=CPU -ovsz=MEM -otty,stat,etime,comm'
276 }
277 if [[ "$(basename "$PAGER")" = "less" ]] {
278     ps="$ps | less -S"
279 } else {
280     ps="$ps | \"${PAGER:-more}\""
281 }
282 alias pa=$ps
283 alias spa="sudo $ps"
284 unset ps
285 ## py venv
286 alias va='source bin/activate'
287 alias vd=deactivate
288 alias vu="python3 -mvenv --upgrade"
289 alias svu="sudo python3 -mvenv --upgrade"
290 ## git
291 alias g=git
292 alias gd='git diff'
293 alias gdh='git diff HEAD'
294 alias gdp='git diff HEAD\^'
295 alias gds='git diff --staged'
296 alias ga='git add'
297 alias ga.='git add .'
298 alias gai='git add -i'
299 alias gap='git add -p'
300 alias gc='git commit'
301 alias gca='git commit --amend'
302 alias gp='git push'
303 alias gu='git pull'
304 alias gl='git log'
305 alias gt='git tree' # from gitconfig
306 alias gsh='git show'
307 alias gshn='git show --name-status'
308 alias gst='git status'
309 alias gsts='git status --short'
310 alias gs='git stash'
311 alias gsp='git stash pop'
312 alias grc='git rebase --continue'
313 ## cd/zoxide
314 function up {
315     \cd $(printf '../%.0s' {1..${1:-1}})
316 }
317 alias u=up
318 if [[ -v commands[zoxide] ]] {
319     # https://github.com/ajeetdsouza/zoxide/issues/513
320     eval "${$(zoxide init zsh):s#_files -/#_cd#}"
321     alias cd=z
322     alias z-='z -'
323 }
324 alias cd..=up
325 ## dotfiles
326 alias dfu='function {
327     pushd -q ${$(echo -E - ~/.zshenv):P:h:h}
328     git pull &&
329     git submodule init &&
330     git submodule sync &&
331     git submodule update
332     popd -q
333 }'
334 ## nocorrect
335 # zsh doesnt really handle sudo very well, so ignore it
336 alias sudo='nocorrect sudo'
337 ## docker compose
338 alias dcp='sudo docker compose pull'
339 alias dcu='sudo docker compose up -d'
340 alias dcr='sudo docker compose restart'
341 alias dcl='sudo docker compose logs -f'
342
343 ### hooks
344 autoload -Uz add-zsh-hook
345 typeset -gi _sev_exectime
346 function sev_preexec {
347     # change terminal title to show command
348     print -n "\e]2;$(print -P '%#')${SSH_CLIENT+$USER@$HOST:}$1\e\\"
349     # save last exec time for bell
350     # XXX: does not run for blank cmdline
351     _sev_exectime=$SECONDS
352     # update gpg forward, to always have unique filename and avoid clashes
353     if [[ -v _GNUPG_SOCK_DEST_EXT ]] {
354         export _GNUPG_SOCK_DEST_EXT=$(date +%s).$RANDOM
355         export _GNUPG_SOCK_DEST=$_GNUPG_SOCK_DEST_BASE.$_GNUPG_SOCK_DEST_EXT
356     }
357 }
358 add-zsh-hook preexec sev_preexec
359 function sev_precmd {
360     # change terminal title
361     # TODO: update and send BEL when job status changes
362     print -Pn "\e]2;%(1j,%j,)%#${SSH_CLIENT+$USER@$HOST:}%~\e\\"
363     # bell if exec takes 5s
364     if (( SECONDS - _sev_exectime >= 5 )) print "\a"
365     # we could update vcs_info here, but let prompt take care of it
366     # if it doesn't use vcs, it can be ignored safely
367 }
368 add-zsh-hook precmd sev_precmd
369 function sev_chpwd {
370     # echo dir on cwd change
371     ls -AF
372 }
373 add-zsh-hook chpwd sev_chpwd
374
375 ### system-specific configs and aliases
376 case $OSTYPE; in
377     freebsd*)
378         ## colors
379         export CLICOLOR=
380         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'
381
382         ## sound
383         function s { sysctl hw.snd.default_unit${1:+\=$1} }
384         alias vol mixer
385
386         ## install port dependencies from pkg (like pkgsrc `bmake bin-install')
387         # XXX: should probably use package-depends where possible, breaks when
388         #      port name is different to package name
389         #      (eg. graphics/sdl20 == sdl2, devel/glib20 == glib2, etc)
390         function portpkg {
391             case "$1" {
392             build|run)
393                 sudo pkg install -AU $(make ${1}-depends-list |
394                            sed 's_/usr/ports/_ _' | tr -d '\n')
395                 ;;
396             *)  echo "Usage: \`portpkg <build|run>' in a port directory"
397                 return 1;;
398             }
399         };;
400     netbsd)
401         ## sound
402         function s {
403             if [[ -z "$1" ]] {
404                 ll /dev/mixer /dev/sound /dev/audio
405                 return
406             }
407             for x in mixer sound audio; do
408                 ln -sf /dev/$x"$1" /dev/$x
409             done
410         }
411         function vol {
412             if [[ -z "$1" ]] {
413                 for x in $(mixerctl -a | grep 'outputs\.master'); do
414                     echo -E - $x
415                 done
416                 return
417             }
418             mixerctl -w outputs.master"$2"="$1"
419         };;
420     *)
421         ## sound
422         # TODO: test alsa/oss/sndio/portaudio/pulse/pipewire in order of importance
423         function s {}
424         function vol {}
425 esac
426
427 ### prompt
428 autoload -Uz promptinit && promptinit
429 prompt arrows
430
431 ### plugins
432 load-plugins zshrc
433
434 ### load site-specific
435 load-site-dotfile zshrc
This page took 0.095835 seconds and 4 git commands to generate.