diff --git a/zsh/aliases.zsh b/zsh/aliases.zsh index a1f8046..69eb869 100644 --- a/zsh/aliases.zsh +++ b/zsh/aliases.zsh @@ -1,15 +1,12 @@ #!/usr/bin/env zsh -# remove Oh-My-Zsh correction aliases -for cmd in cp ebuild gist heroku hpodder man mkdir mv mysql sudo; do - unalias $cmd -done - # this alias removes leading dollar sign (useful when copying code from Stackoverflow) alias '$'='' # this alias allows aliases to work with sudo alias sudo='sudo ' +alias history='fc -i -l 1' + alias cdd='dirs -v' alias grep='grep --color=auto' @@ -18,17 +15,16 @@ alias diff='diff --color=auto' # exa is a modern replacement for ls - https://the.exa.website/ if command_exists exa; then alias ls="exa --classify --group-directories-first" - alias lsa="${aliases[ls]} --all" - alias l="${aliases[ls]} --long --header --binary --group" - alias la="${aliases[l]} --all" - alias tree="${aliases[ls]} --tree" + alias lsa="ls --all" + alias l="ls --long --header --binary --group" + alias la="l --all" + alias tree="ls --tree" else alias ls="ls --classify --group-directories-first --color=auto" - alias lsa="${aliases[ls]} --almost-all" - alias l="${aliases[ls]} -l --human-readable" - alias la="${aliases[l]} --almost-all" + alias lsa="ls --almost-all" + alias l="ls -l --human-readable" + alias la="l --almost-all" fi -unalias ll # remove this Oh-My-Zsh alias # fd is a simple, fast and user-friendly alternative to find - https://github.com/sharkdp/fd if command_exists fd; then @@ -36,17 +32,28 @@ if command_exists fd; then fi # git with hub -command_exists hub && alias git="hub" +if command_exists hub; then + alias git="hub" +fi # make these utils more verbose alias cp='cp -iv' alias mv='mv -iv' alias rm='rm -iv' -alias rmdir='rmdir -v' +alias rmdir='rmdir -v' rd='rmdir' alias chmod='chmod -v' alias chown='chown -v' alias ln='ln -iv' -alias mkdir='mkdir -v' +alias mkdir='mkdir -v' md='mkdir -p' + +for n in {1..9}; do + alias "$n"="cd +$n" +done; unset n + +alias ...='../..' +alias ....='../../..' +alias .....='../../../..' +alias ......='../../../../..' # print file sizes in human readable format alias du='du -h' diff --git a/zsh/completion.zsh b/zsh/completion.zsh new file mode 100644 index 0000000..548955d --- /dev/null +++ b/zsh/completion.zsh @@ -0,0 +1,41 @@ +# http://zsh.sourceforge.net/Doc/Release/Completion-System.html +# https://github.com/robbyrussell/oh-my-zsh/blob/master/lib/completion.zsh + +# load fancier completion menu which (most notably) supports `list-colors` +zmodload zsh/complist +zstyle ':completion:*' menu select +# show even more completion results +zstyle ':completion:*' verbose yes + +zstyle ':completion::complete:*' use-cache yes +zstyle ':completion::complete:*' cache-path "$ZSH_CACHE_DIR" + +# group completion result based on their categories +zstyle ':completion:*' group-name '' +# format for displaying category names +zstyle ':completion:*:descriptions' format '%F{yellow}[%d]%f' + +# Sometimes zsh completion authors for whatever reason add descriptions for +# option values, but don't do describe the options themselves (e.g. ffmpeg, +# some options for GCC). In such cases description of an option can be inferred +# from the description of its value. That's the purpose of `auto-description`. +zstyle ':completion:*:options' auto-description '%d' + +# case insensitive (all), partial-word and substring completion +zstyle ':completion:*' matcher-list 'm:{a-zA-Z-_}={A-Za-z_-}' 'r:|=*' 'l:|=* r:|=*' + +zstyle ':completion:*' list-dirs-first yes +# complete . and .. directories +# This is very useful when I just want to quickly look around inside a +# directory without running `ls` +zstyle ':completion:*' special-dirs yes + +if [[ -n "$LS_COLORS" ]]; then + zstyle ':completion:*' list-colors "${(@s.:.)LS_COLORS}" +fi + +zstyle ':completion:*:processes' command "ps xo pid,user,cmd" +zstyle ':completion:*:processes-names' command "ps xho comm=" +zstyle ':completion:*:processes' force-list always + +zstyle -e ':completion:*:hosts' hosts 'reply=("${(@f)$(awk "match(\$0, /^Host[[:blank:]]*/) { print substr(\$0, RLENGTH+1); }" ~/.ssh/config)}")' diff --git a/zsh/env.zsh b/zsh/env.zsh index 6511883..83a5605 100644 --- a/zsh/env.zsh +++ b/zsh/env.zsh @@ -1,17 +1,19 @@ #!/usr/bin/env zsh -export USER="${USER:-$USERNAME}" - # find editor export EDITOR="nvim" export VISUAL="$EDITOR" alias edit="$EDITOR" alias e="$EDITOR" +export PAGER='less' +export LESS='--RAW-CONTROL-CHARS' + export CLICOLOR=1 -READNULLCMD=cat - +# BSD ls colors +export LSCOLORS="Gxfxcxdxbxegedabagacad" +# GNU ls colors if [[ -z "$LS_COLORS" ]] && command_exists dircolors; then eval "$(dircolors --bourne-shell)" fi diff --git a/zsh/functions.zsh b/zsh/functions.zsh index 8e47200..90dd126 100644 --- a/zsh/functions.zsh +++ b/zsh/functions.zsh @@ -1,24 +1,16 @@ #!/usr/bin/env zsh -is_linux() { - [[ "$OSTYPE" == linux* ]] +count() { echo "$#"; } + +mkcd() { + mkdir -p "$@" && cd "${@[-1]}" } -is_macos() { - [[ "$OSTYPE" == darwin* ]] -} +is_linux() { [[ "$OSTYPE" == linux* ]]; } +is_macos() { [[ "$OSTYPE" == darwin* ]]; } +is_android() { [[ "$OSTYPE" == linux-android ]]; } -is_android() { - [[ "$OSTYPE" == linux-android ]] -} - -command_exists() { - command -v "$1" &>/dev/null -} - -source_if_exists() { - [[ -f "$1" ]] && source "$1" -} +command_exists() { command -v "$1" &>/dev/null; } lazy_load() { local command="$1" @@ -31,16 +23,31 @@ lazy_load() { }" } -welcome() { - "$ZSH_DOTFILES/welcome/main.py" -} +welcome() { "$ZSH_DOTFILES/welcome/main.py"; } if is_android; then - alias open='termux-open' -elif is_linux && command_exists xdg-open; then - open() { nohup xdg-open "$@" &> /dev/null; } + open_cmd='termux-open' +elif command_exists xdg-open; then + open_cmd='nohup xdg-open &> /dev/null' +else + open_cmd='print >&2 "open: Platform $OSTYPE is not supported"; return 1' fi +eval "open(){$open_cmd \"\$@\";}" +unset open_cmd -set-my-syntax-theme() { - fast-theme "$ZSH_DOTFILES/my-syntax-theme.ini" "$@" -} +if is_macos; then + copy_cmd='pbcopy' paste_cmd='pbpaste' +elif command_exists xclip; then + copy_cmd='xclip -in -selection clipboard' paste_cmd='xclip -out -selection clipboard' +elif command_exists xsel; then + copy_cmd='xsel --clipboard --input' paste_cmd='xsel --clipboard --output' +elif command_exists termux-clipboard-set && command_exists termux-clipboard-get; then + copy_cmd='termux-clipboard-set' paste_cmd='termux-clipboard-get' +else + error_msg='Platform $OSTYPE is not supported' + copy_cmd='print >&2 "clipcopy: '"$error_msg"'"; return 1' + paste_cmd='print >&2 "clippaste: '"$error_msg"'"; return 1' + unset error_msg +fi +eval "clipcopy(){$copy_cmd;};clippaste(){$paste_cmd;}" +unset copy_cmd paste_cmd diff --git a/zsh/options.zsh b/zsh/options.zsh new file mode 100644 index 0000000..3679194 --- /dev/null +++ b/zsh/options.zsh @@ -0,0 +1,60 @@ +# http://zsh.sourceforge.net/Doc/Release/Options.html#Description-of-Options-1 +# http://zsh.sourceforge.net/Doc/Release/Parameters.html#Parameters-Used-By-The-Shell + +# remove some characters from the standard WORDCHARS +WORDCHARS="${WORDCHARS//[\/=]}" +# disable Ctrl+S and Ctrl+Q (https://unix.stackexchange.com/questions/137842/what-is-the-point-of-ctrl-s) +setopt no_flow_control + +setopt correct_all + +# recognize comments in the prompt +setopt interactive_comments + +setopt extended_glob + +# enable support for multiple redirections in one command +setopt multi_os +# disallow redirection to file (i.e. `>`) if the file already exists, this can +# be overriden by using `>!` or `>|` redirection operators +setopt no_clobber +# command to assume when redirection is used without a command +READNULLCMD=cat + +setopt long_list_jobs + +# if the first word in the command is a directory name, `cd` into it +setopt auto_cd +# automatically push directories onto the stack when doing a `cd`, but remove +# older duplicates (this works like history of a single tab in a web browser) +setopt auto_pushd pushd_ignore_dups + +# do not autoselect the first completion entry +setopt no_menu_complete +# if the cursor is inside a word, use part from the beginning to the cursor as +# prefix and from the cursor to the end as suffix when searching for +# completions (the default behavior is to use the whole word as a substring) +setopt complete_in_word +# setopt always_to_end # does this option affect anything? + +# strangely enough, Zsh doesn't save command history by default +HISTFILE="${HISTFILE:-$HOME/.zsh_history}" +# max number of entries stored in memory +HISTSIZE=50000 +# max number of entries in the HISTFILE +SAVEHIST=10000 +# record timestamps in the history +setopt extended_history +# delete duplicates first when HISTFILE size exceeds HISTSIZE +setopt hist_expire_dups_first +# ignore duplicated history items +setopt hist_ignore_dups +# ignore commands that start with space +setopt hist_ignore_space +# don't run commands with history expansions immediately, instead allow the +# user to preview the expansions and edit the command after expansions in ZLE +setopt hist_verify +# immediately write HISTFILE to disk when a new command is appended +setopt inc_append_history +# synchronize history between active sessions +setopt share_history diff --git a/zsh/path.zsh b/zsh/path.zsh index 0f7de56..f3b0dfd 100644 --- a/zsh/path.zsh +++ b/zsh/path.zsh @@ -40,22 +40,25 @@ if is_macos; then fi # add Go binaries -export GOPATH="$HOME/.go" +export GOPATH=~/.go path=("$GOPATH/bin" "${path[@]}") -# add user binaries -path=(~/.local/bin "${path[@]}") - -# add my binaries and completions -path=("$ZSH_DOTFILES/../scripts" "${path[@]}") -fpath=("$ZSH_DOTFILES/completions" "${fpath[@]}") - -# check for Rust installed via rustup -rustc=~/.cargo/bin/rustc -if [[ -f "$rustc" && -x "$rustc" ]] && rust_sysroot="$("$rustc" --print sysroot)"; then - # add paths of the default Rust toolchain - path=(~/.cargo/bin "${path[@]}") +# Rust +path=(~/.cargo/bin "${path[@]}") +# check if the Rust toolchain was installed via rustup +if rustup_home="$(rustup show home 2> /dev/null)" && + rust_sysroot="$(rustc --print sysroot 2> /dev/null)" && + [[ -d "$rustup_home" && -d "$rust_sysroot" && "$rust_sysroot" == "$rustup_home"/* ]] +then + # add paths of the selected Rust toolchain fpath=("$rust_sysroot/share/zsh/site-functions" "${fpath[@]}") manpath=("$rust_sysroot/share/man" "${manpath[@]}") fi -unset rustc rust_sysroot +unset rustup_home rust_sysroot + +# add my binaries and completions +path=("${ZSH_DOTFILES:h}/scripts" "${path[@]}") +fpath=("$ZSH_DOTFILES/completions" "${fpath[@]}") + +# add user binaries +path=(~/.local/bin "${path[@]}") diff --git a/zsh/plugins.zsh b/zsh/plugins.zsh index cf9adc7..f69dd75 100644 --- a/zsh/plugins.zsh +++ b/zsh/plugins.zsh @@ -12,31 +12,30 @@ plugin completions 'zsh-users/zsh-completions' # compinit {{{ # note that completion system must be initialized after zsh-completions and # before oh-my-zsh - run_compinit() { - autoload -U compinit + autoload -U compinit - local match run_compdump=1 - # glob qualifiers description: - # N turn on NULL_GLOB for this expansion - # . match only plain files - # m-1 check if the file was modified today - # see "Filename Generation" in zshexpn(1) - for match in $HOME/.zcompdump(N.m-1); do - run_compdump= - break - done + run_compdump=1 + # glob qualifiers description: + # N turn on NULL_GLOB for this expansion + # . match only plain files + # m-1 check if the file was modified today + # see "Filename Generation" in zshexpn(1) + for match in $HOME/.zcompdump(N.m-1); do + run_compdump=0 + break + done; unset match - if [[ -n "$run_compdump" ]]; then - # -D flag turns off compdump loading - compinit -D - compdump - else - # -C flag disables some checks performed by compinit - they are not needed - # because we already have a fresh compdump - compinit -C - fi - } - run_compinit + if (( $run_compdump )); then + echo "$0: rebuilding zsh completion dump" + # -D flag turns off compdump loading + compinit -D + compdump + else + # -C flag disables some checks performed by compinit - they are not needed + # because we already have a fresh compdump + compinit -C + fi + unset run_compdump # }}} # Oh-My-Zsh {{{ @@ -64,11 +63,12 @@ plugin completions 'zsh-users/zsh-completions' # wasting time DISABLE_LS_COLORS=true + omz_features=(key-bindings termsupport) omz_plugins=(git extract fasd) plugin oh-my-zsh 'robbyrussell/oh-my-zsh' \ - load='lib/*.zsh' load='plugins/'${^omz_plugins}'/*.plugin.zsh' \ - ignore='lib/(compfix|diagnostics).zsh' \ + load='lib/'${^omz_features}'.zsh' \ + load='plugins/'${^omz_plugins}'/*.plugin.zsh' \ before_load='ZSH="$plugin_dir"' \ after_load='plugin-cfg-path fpath prepend completions functions' \ after_load='plugin-cfg-path fpath prepend plugins/'${^omz_plugins} @@ -83,9 +83,8 @@ plugin fzf 'junegunn/fzf' build='./install --bin' \ plugin alias-tips 'djui/alias-tips' -plugin ssh 'zpm-zsh/ssh' - FAST_WORK_DIR="$ZSH_CACHE_DIR" if [[ "$TERM" != "linux" ]]; then plugin fast-syntax-highlighting 'zdharma/fast-syntax-highlighting' + set-my-syntax-theme() { fast-theme "$ZSH_DOTFILES/my-syntax-theme.ini" "$@"; } fi diff --git a/zsh/prompt.zsh b/zsh/prompt.zsh index 39d7182..d0d7a99 100644 --- a/zsh/prompt.zsh +++ b/zsh/prompt.zsh @@ -3,7 +3,7 @@ # Escapes `%` in all arguments by replacing it with `%%`. Escaping is needed so # that untrusted input (e.g. git branch names) doesn't affect prompt rendering. prompt_escape() { - echo "${@//'%'/%%}" + print -n "${@//\%/%%}" } prompt_preexec_hook() { @@ -65,7 +65,7 @@ prompt_vcs_info() { # enable normal prompt expansion sequences which begin with a `%`. # promptsubst # enable parameter/command/arithmetic expansion/substitution in the prompt. -setopt nopromptbang promptcr promptsp promptpercent promptsubst +setopt no_prompt_bang prompt_cr prompt_sp prompt_percent prompt_subst zmodload zsh/datetime autoload -Uz add-zsh-hook diff --git a/zsh/zle.zsh b/zsh/zle.zsh index 3e16c46..e43135f 100644 --- a/zsh/zle.zsh +++ b/zsh/zle.zsh @@ -1,9 +1,13 @@ #!/usr/bin/env zsh +# http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html +# http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#Zle-Builtins +# http://zsh.sourceforge.net/Doc/Release/Zsh-Line-Editor.html#Standard-Widgets + # _fzf_history_widget {{{ # taken from https://github.com/junegunn/fzf/blob/master/shell/key-bindings.zsh _fzf_history_widget() { - setopt localoptions pipefail + setopt local_options pipe_fail local selected selected=( $(fc -rl 1 | @@ -127,3 +131,20 @@ zle -N _palette_widget bindkey "^[P" _palette_widget # }}} + +# expand-or-complete-with-dots {{{ + expand-or-complete-with-dots() { + local wrap_ctrl_supported + if (( ${+terminfo[rmam]} && ${+terminfo[smam]} )); then + wrap_ctrl_supported=1 + fi + # toggle line-wrapping off and back on again + if [[ -n "$wrap_ctrl_supported" ]]; then echoti rmam; fi + print -Pn "%F{red}...%f" + if [[ -n "$wrap_ctrl_supported" ]]; then echoti smam; fi + zle expand-or-complete + zle redisplay + } + zle -N expand-or-complete-with-dots + bindkey "^I" expand-or-complete-with-dots +# }}} diff --git a/zsh/zplg.zsh b/zsh/zplg.zsh index d56b2b8..7575607 100644 --- a/zsh/zplg.zsh +++ b/zsh/zplg.zsh @@ -1,3 +1,5 @@ +#!/usr/bin/env zsh + # This... is my DIY plugin manager for Zsh. "Why did I reinvent the wheel yet # again and created my own plugin manager?" you might ask. Well, some of them # are too slow (antigen, zplug), some are too complicated (antigen-hs, zplugin) @@ -22,16 +24,12 @@ # means "put all elements of the array in separate quotes". # 2. I often use the following snippet to exit functions on errors: # eval "$some_user_command_that_might_fail" || return "$?" -# I do this instead of `setopt localoptions errexit` because some plugins +# I do this instead of `setopt local_options err_exit` because some plugins # may not be compatitable with ERREXIT. _ZPLG_SCRIPT_PATH="${(%):-%N}" -# load dependencies {{{ - autoload -Uz colors && colors -# }}} - # $ZPLG_HOME is a directory where all your plugins are downloaded, it also # might contain in the future some kind of state/lock/database files. It is @@ -118,7 +116,7 @@ _zplg_run_commands() { # inside a function which reverts NULL_GLOB to its previous value as soon as # the function returns. _zplg_expand_pattern() { - setopt localoptions nullglob + setopt local_options null_glob local pattern="$1" out_var_name="$2" # ${~var_name} turns on globbing for this expansion, note lack of quotes: as # it turns out glob expansions are automatically quoted by design, and when diff --git a/zsh/zshrc b/zsh/zshrc index 37866e9..de93265 100755 --- a/zsh/zshrc +++ b/zsh/zshrc @@ -2,9 +2,10 @@ ZSH_DOTFILES="${0:h}" -for script in functions path env plugins aliases zstyle zle prompt colorscheme; do +autoload -U colors && colors + +for script in functions options path env aliases plugins completion zle prompt colorscheme; do source "$ZSH_DOTFILES/$script.zsh" - source_if_exists "$ZSH_DOTFILES/custom/$script.zsh" done # add colon after MANPATH so that it doesn't overwrite system MANPATH @@ -12,6 +13,4 @@ MANPATH="$MANPATH:" command_exists rbenv && eval "$(rbenv init -)" -setopt noclobber - welcome diff --git a/zsh/zstyle.zsh b/zsh/zstyle.zsh deleted file mode 100644 index 5d905d9..0000000 --- a/zsh/zstyle.zsh +++ /dev/null @@ -1,3 +0,0 @@ -if [[ -n "$LS_COLORS" ]]; then - zstyle ':completion:*' list-colors "${(@s.:.)LS_COLORS}" -fi