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.
This commit is contained in:
2024-05-19 00:08:04 +01:00
parent 3a6e6a5256
commit 98f0b2ec66
5 changed files with 155 additions and 54 deletions

140
zsh/zshrc.funcs.zsh Normal file
View File

@@ -0,0 +1,140 @@
#
# zshrc helper functions
#
# Helpers designed for use during setup of interactive shell environments
# (~/.zshrc ).
#
# setup-completions is a helper function to set up shell completions for a given
# command. It generates Zsh completion scripts and places them in the specified
# completions directory. If the completion file already exists, it checks if the
# source file has been updated and regenerates the completions if necessary.
#
# Arguments:
#
# $1 - cmd: The name of the command for which completions are being
# set up.
# $2 - source: The source file used to determine if completions need to
# be re-generated. For example, the binary file of the
# command (e.g., rustup).
# $@ - args: The command to run to generate the completions. This
# should produce Zsh completion scripts.
#
# Example usage:
#
# setup-completions rustup "$(command -v rustup)" rustup completions zsh
#
# This example sets up completions for the 'rustup' command by running
# 'rustup completions zsh', and places the generated completion script in the
# appropriate completions directory. If the source file is newer than the target
# completion file, the command is re-executed and the completion script is
# updated.
#
# The completions are placed in the directory specified by the ZSH_COMPLETIONS
# environment variable. If ZSH_COMPLETIONS is not set, the completions are
# placed in $HOME/.zsh/completions by default.
setup-completions() {
local cmd="$1"
local source="$2"
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 "$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: $script" >&2
mkdir -p "$target_dir"
eval "$script" >| "$target_file"
chmod +x "$target_file"
# Only run compinit if not already loaded
if ! (whence -w compinit &> /dev/null); then
autoload -U compinit && compinit
fi
fi
}
# Convert a bash/zsh alias to a function. It prints the unalias command and the
# function definition, meaning the output needs to be evaluated to take effect.
#
# Arguments:
# $1: The alias to convert. Should be a single line like "alias ll='ls -alF'"
# or "ll='ls -alF'".
#
# Example:
# alias brew="op plugin run -- brew"
# convert_alias_to_function "$(alias brew)"
#
# This will print:
# unalias brew
# brew() {
# op plugin run -- brew "$@"
# }
convert-alias-source-to-function-source() {
local line="$1"
# Remove Bash's "alias " prefix if present.
line=${line#alias }
# Extract the alias name and the raw command.
local alias_name="$(echo "$line" | cut -d'=' -f1)"
local raw_command="$(echo "$line" | cut -d'=' -f2)"
# Evaluate the raw command to ensure we get the correct command, with any
# quotes or shell escape characters handled correctly.
local command
eval "command=$raw_command"
# Abort if the command is empty.
if [ -z "$command" ]; then
echo "Error: Empty command for alias $alias_name" >&2
return 1
fi
# Print the unalias command and the function definition.
echo -e "unalias ${alias_name}"
echo -e "${alias_name}() {\n ${command} \"\$@\"\n}"
}
# Convert a bash/zsh alias to a function. It replaces the alias with a function
# definition in the current shell that has the same behavior as the alias.
#
# Arguments:
# $1: The alias to convert. Should be the name of the alias.
#
# Example:
# alias brew="op plugin run -- brew"
# convert-alias-to-function brew
#
# This will replace the alias "brew" with a function that has the same behavior.
convert-alias-to-function() {
local alias_name="$1"
# Get the alias source.
local alias_source="$(alias "$alias_name")"
# Abort if the alias does not exist.
if [ -z "$alias_source" ]; then
echo "Error: Alias $alias_name does not exist" >&2
return 1
fi
eval "$(convert-alias-source-to-function-source "$alias_source")"
}