From 98f0b2ec667cd664b1d52bc7455e81d03faf2cf5 Mon Sep 17 00:00:00 2001 From: Jim Myhrberg Date: Sun, 19 May 2024 00:08:04 +0100 Subject: [PATCH] feat(shell): major refactor around history and completion setup Biggest change is embracing fzf for all shell completion. This includes using tmux popup windows to effectively render completions as floating popups with fuzzy matching via fzf. Shell history has also been improved, with appending each command to the history file one by one, rather than only at end of a shell session when the shell exists. This should ensure I don't have any more lost commands cause the shell didn't exit cleanly. --- config/mise/config.toml | 2 + zsh/fzf.zsh | 18 -- zsh/ruby.zsh | 7 + ...-funcs-interactive.zsh => zshrc.funcs.zsh} | 24 +-- zshrc | 158 +++++++++++++++--- 5 files changed, 155 insertions(+), 54 deletions(-) delete mode 100644 zsh/fzf.zsh rename zsh/{util-funcs-interactive.zsh => zshrc.funcs.zsh} (92%) diff --git a/config/mise/config.toml b/config/mise/config.toml index 8551225..da2aa8e 100644 --- a/config/mise/config.toml +++ b/config/mise/config.toml @@ -25,6 +25,7 @@ show_tools = false "cargo:hwatch" = "latest" "cargo:jwt-cli" = "latest" "cargo:kubectl-watch" = "latest" +"cargo:paper-terminal" = "latest" "cargo:sccache" = "latest" "cargo:tlrc" = "latest" "cargo:ubi" = "latest" @@ -93,6 +94,7 @@ ruby = "latest" shellcheck = "latest" shfmt = "latest" starship = "latest" +talosctl = "latest" terraform = "latest" tflint = "latest" usage = "latest" diff --git a/zsh/fzf.zsh b/zsh/fzf.zsh deleted file mode 100644 index b51ac49..0000000 --- a/zsh/fzf.zsh +++ /dev/null @@ -1,18 +0,0 @@ -# -# fzf -# - -export FZF_CTRL_T_OPTS="--preview='less {}'" -export FZF_DEFAULT_OPTS="--bind=ctrl-k:kill-line --border=none --tabstop=4" -export FZF_TMUX=0 -export FZF_TMUX_HEIGHT=100% - -# Install fzf binary from latest GitHub Release. -zinit light-mode wait lucid from'gh-r' as'program' pick'fzf' \ - for @junegunn/fzf - -# Install fzf-tmux command and zsh plugins from default branch on GitHub. -zinit light-mode wait lucid from'gh' as'program' pick'bin/fzf-tmux' \ - multisrc'shell/{completion,key-bindings}.zsh' \ - id-as'junegunn/fzf-extras' \ - for @junegunn/fzf diff --git a/zsh/ruby.zsh b/zsh/ruby.zsh index da0e9fd..74f95cf 100755 --- a/zsh/ruby.zsh +++ b/zsh/ruby.zsh @@ -2,6 +2,13 @@ # Ruby environment setup. # +# ============================================================================== +# bundler +# ============================================================================== + +# Enable Ruby Bundler plugin from oh-my-zsh. +zinit for @OMZ::plugins/bundler + # ============================================================================== # aliases # ============================================================================== diff --git a/zsh/util-funcs-interactive.zsh b/zsh/zshrc.funcs.zsh similarity index 92% rename from zsh/util-funcs-interactive.zsh rename to zsh/zshrc.funcs.zsh index 1f0633c..5e39128 100644 --- a/zsh/util-funcs-interactive.zsh +++ b/zsh/zshrc.funcs.zsh @@ -1,5 +1,5 @@ # -# Interactive Utility Functions +# zshrc helper functions # # Helpers designed for use during setup of interactive shell environments # (~/.zshrc ). @@ -36,32 +36,32 @@ setup-completions() { local cmd="$1" local source="$2" - local setup_cmd="$3" - shift 3 + shift 2 + local script="$@" local target_dir="${ZSH_COMPLETIONS:-$HOME/.zsh/completions}" local target_file="${target_dir}/_${cmd}" + if [[ -z "$cmd" || -z "$source" || -z "$script" ]]; then + echo "setup-completions: Missing required arguments." >&2 + return 1 + fi + if [[ -z "$(command -v "$cmd")" ]]; then echo "setup-completions: Command not found: $cmd" >&2 return 1 fi - if [[ -z "$(command -v "$setup_cmd")" ]]; then - echo "setup-completions: Command not found: $setup_cmd" >&2 - return 1 - fi - - if [[ -z "$cmd" || -z "$source" || -z "$setup_cmd" ]]; then - echo "setup-completions: Missing required arguments." >&2 + if [[ -z "$source" ]]; then + echo "setup-completions: Source file not found: $source" >&2 return 1 fi # Check if the target completion file needs to be updated if [[ ! -f "$target_file" || "$source" -nt "$target_file" ]]; then - echo "setup-completions: Setting up completion for $cmd --> $target_file" >&2 + echo "setup-completions: Setting up completion for $cmd: $script" >&2 mkdir -p "$target_dir" - "$setup_cmd" "$@" >| "$target_file" + eval "$script" >| "$target_file" chmod +x "$target_file" # Only run compinit if not already loaded diff --git a/zshrc b/zshrc index afda522..a273e08 100644 --- a/zshrc +++ b/zshrc @@ -32,45 +32,156 @@ fi # Load Zinit source "${ZINIT[BIN_DIR]}/zinit.zsh" -# Enable interactive selection of completions. -zinit for @OMZ::lib/completion.zsh +# ============================================================================== +# History +# ============================================================================== # Set various sane defaults for ZSH history management. zinit for @OMZ::lib/history.zsh -# Enable Ruby Bundler plugin from oh-my-zsh. -zinit for @OMZ::plugins/bundler +# Map history search to ctrl-p and ctrl-n. +# bindkey '^p' history-beginning-search-backward +# bindkey '^n' history-beginning-search-forward -zinit light-mode wait lucid \ - atinit"ZINIT[COMPINIT_OPTS]=-C; zicompinit; zicdreplay" \ - for @zdharma-continuum/fast-syntax-highlighting +## History file configuration +HISTFILE="$HOME/.zsh_history" +HISTSIZE=100000 +SAVEHIST=50000 -zinit light-mode wait lucid blockf \ - for @zsh-users/zsh-completions - -zinit light-mode wait lucid atload"!_zsh_autosuggest_start" \ - for @zsh-users/zsh-autosuggestions +## History command configuration +setopt append_history # append history to HISTFILE +setopt extended_history # record timestamp of command in HISTFILE +setopt hist_expire_dups_first # delete duplicates first when HISTFILE size exceeds HISTSIZE +setopt hist_find_no_dups # do not display a duplicate history entry +setopt hist_ignore_dups # ignore duplicated commands history list +setopt hist_ignore_space # ignore commands that start with space +setopt hist_reduce_blanks # remove superfluous blanks before adding to history +setopt hist_verify # show command with history expansion to user before running it +setopt inc_append_history_time # add timestamp to HISTFILE in order of execution +setopt share_history # share command history data # ============================================================================== # Completion # ============================================================================== -# Group completions by type under group headings -zstyle ':completion:*' group-name '' -zstyle ':completion:*:descriptions' format '%B%d%b' - -# Improve selection of Makefile completions - from: -# https://github.com/zsh-users/zsh-completions/issues/541#issuecomment-384223016 -zstyle ':completion:*:make:*:targets' call-command true -zstyle ':completion:*:make:*' tag-order targets - if [ -d "$ZSH_COMPLETIONS" ]; then fpath=("$ZSH_COMPLETIONS" $fpath); fi if [ -d "$DOTZSH_SITEFUNS" ]; then fpath=("$DOTZSH_SITEFUNS" $fpath); fi if [ -d "$BREW_SITEFUNS" ]; then fpath=("$BREW_SITEFUNS" $fpath); fi +# Enable interactive selection of completions. +zinit for @OMZ::lib/completion.zsh + +# Add generic cross platform clipcopy and clippaste commands to copy and paste +# from the system clipboard. +zinit for @OMZ::lib/clipboard.zsh + +# Install fzf-tab if fzf is available. +if command-exists fzf; then + zinit light-mode wait lucid for @Aloxaf/fzf-tab + zinit light-mode wait lucid for @Freed-Wu/fzf-tab-source +fi + +zinit light-mode wait lucid blockf for @zsh-users/zsh-completions + +zinit light-mode wait lucid atload"!_zsh_autosuggest_start" \ + for @zsh-users/zsh-autosuggestions + +# Map ctrl+x h to show completion context info. +bindkey '^Xh' _complete_help + +# Group completions by type under group headings +zstyle ':completion:*' format '%B[%d]%b' +zstyle ':completion:*:descriptions' format '[%d]' +zstyle ':completion:*' group-name '' +zstyle ':completion:*' list-colors ${(s.:.)LS_COLORS} +zstyle ':completion:*' list-grouped true +zstyle ':autocomplete:*' groups 'always' + +# Case-insensitive completion. +zstyle ':completion:*' matcher-list 'm:{[:lower:][:upper:]-_}={[:upper:][:lower:]_-}' 'r:|=*' 'l:|=* r:|=*' + +# git +zstyle ':completion:*:*:git:*:branches' group-name 'local branches' +zstyle ':completion:*:*:git:*:remote-branches' group-name 'remote branches' +zstyle ':completion:*:*:git:*:tags' group-name 'tags' +zstyle ':completion:*:git-checkout:*' sort false + +# Improve selection of Makefile completions - from: +# https://github.com/zsh-users/zsh-completions/issues/541#issuecomment-384223016 +zstyle ':completion:*:make:*' tag-order targets +zstyle ':completion:*:make:*:targets' call-command true + autoload -Uz compinit compinit +# Setup fzf related stuff if it is available. +if command-exists fzf; then + export FZF_DEFAULT_OPTS=" + --bind=ctrl-k:kill-line + --bind=ctrl-v:half-page-down + --bind=alt-v:half-page-up + --tabstop=4 + --highlight-line" + + export FZF_CTRL_T_OPTS=" + --walker-skip .git,node_modules,target + --bind 'ctrl-/:change-preview-window(down|hidden|)'" + + # TODO: replace pbcopy with something that's cross-platform. + export FZF_CTRL_R_OPTS=" + --border=none + --preview 'echo {}' --preview-window up:3:hidden:wrap + --bind 'ctrl-/:toggle-preview' + --bind 'ctrl-y:execute-silent(echo -n {2..} | pbcopy)+abort'" + + export FZF_ALT_C_OPTS=" + --walker-skip .git,node_modules,target + --preview 'tree -C {}'" + + export FZF_TMUX=0 + export FZF_TMUX_HEIGHT="100%" + + cached-eval "$(command -v fzf)" fzf --zsh + + zstyle ':completion:*' menu no + zstyle ':completion:*' special-dirs true + + zstyle ':fzf-tab:*' fzf-bindings 'ctrl-v:half-page-down' 'alt-v:half-page-up' + zstyle ':fzf-tab:*' fzf-flags '--highlight-line' '--tabstop=4' + zstyle ':fzf-tab:*' switch-group '<' '>' + + # Use fzf-tab's tmux popup for tab completion. + zstyle ':fzf-tab:*' fzf-command ftb-tmux-popup + zstyle ':fzf-tab:*' popup-min-size 80 10 + zstyle ':fzf-tab:*' popup-pad 0 0 + + if command-exists eza; then + fzf_dir_preview='eza -1 --color=always --icons $realpath' + zstyle ':fzf-tab:complete:eza:*' fzf-preview "$fzf_dir_preview" + else + fzf_dir_preview='ls -1 --color=always $realpath' + fi + zstyle ':fzf-tab:complete:cd:*' fzf-preview "$fzf_dir_preview" + zstyle ':fzf-tab:complete:ls:*' fzf-preview "$fzf_dir_preview" + + if command-exists bat; then + fzf_bat_preview='bat --color=always -n -r :500' + FZF_CTRL_T_OPTS="$FZF_CTRL_T_OPTS --preview '$fzf_bat_preview {}'" + zstyle ':fzf-tab:complete:bat:*' fzf-preview "$fzf_bat_preview \$realpath" + zstyle ':fzf-tab:complete:cat:*' fzf-preview "$fzf_bat_preview \$realpath" + else + FZF_CTRL_T_OPTS="$FZF_CTRL_T_OPTS --preview 'less {}'" + fi +fi + +# ============================================================================== +# Visuals +# ============================================================================== + +zinit light-mode wait lucid \ + atinit"ZINIT[COMPINIT_OPTS]=-C; zicompinit; zicdreplay" \ + for @zdharma-continuum/fast-syntax-highlighting + # ============================================================================== # Edit command line # ============================================================================== @@ -80,10 +191,10 @@ zle -N edit-command-line bindkey "^X^E" edit-command-line # ============================================================================== -# Helpers +# Helper Functions # ============================================================================== -source "$DOTZSH/util-funcs-interactive.zsh" +source "$DOTZSH/zshrc.funcs.zsh" # ============================================================================== # Private Dotfiles @@ -151,7 +262,6 @@ if [[ "$OSTYPE" == "linux"* ]]; then source "$DOTZSH/linux.zsh"; fi source "$DOTZSH/1password.zsh" source "$DOTZSH/copilot.zsh" source "$DOTZSH/emacs.zsh" -source "$DOTZSH/fzf.zsh" source "$DOTZSH/less.zsh" source "$DOTZSH/mise.zsh" source "$DOTZSH/neovim.zsh"