inital commit
@@ -0,0 +1,24 @@
|
||||
._*
|
||||
.vscode/
|
||||
*.zwc
|
||||
*.compile
|
||||
*.swp
|
||||
.netrwhist
|
||||
.jupyterlab-workspace
|
||||
*workspaces*
|
||||
.history/Users/ngorse/.dotfiles/
|
||||
zsh/plugins/*
|
||||
00-homebrew/Brewfile.lock.json
|
||||
*.bak
|
||||
*.zip
|
||||
*/archive/*
|
||||
*.old
|
||||
*.old*
|
||||
zsh/.zcomp*
|
||||
*/*.compile
|
||||
zsh/.zsh_sessions*
|
||||
zsh/.zsh_history
|
||||
antidote/zsh_plugins.compile
|
||||
.claude/
|
||||
00-homebrew
|
||||
:
|
||||
@@ -0,0 +1,40 @@
|
||||
_create_default() {
|
||||
cat <<< "# .zsh_plugins.txt
|
||||
zsh-users/zsh-syntax-highlighting
|
||||
zsh-users/zsh-autosuggestions
|
||||
zsh-users/zsh-history-substring-search
|
||||
|
||||
mattmc3/zephyr path:plugins/color
|
||||
mattmc3/zephyr path:plugins/helper
|
||||
getantidote/use-omz
|
||||
joshskidmore/zsh-fzf-history-search
|
||||
zsh-hooks/zsh-hooks
|
||||
" > ${1}
|
||||
|
||||
}
|
||||
|
||||
if [[ ! -d $HOME/.antidote ]]; then
|
||||
git clone --depth 1 https://github.com/mattmc3/antidote.git $HOME/.antidote
|
||||
fi
|
||||
|
||||
|
||||
# Set the root name of the plugins files (.txt and .zsh) antidote will use.
|
||||
zsh_plugins=${DOTFILES}/antidote/zsh_plugins
|
||||
|
||||
# Ensure the .zsh_plugins.txt file exists so you can add plugins.
|
||||
[[ -f ${zsh_plugins}.txt ]] || _create_default ${zsh_plugins}.txt
|
||||
|
||||
# Lazy-load antidote from its functions directory.
|
||||
fpath=($HOME/.antidote/functions $fpath)
|
||||
autoload -Uz antidote
|
||||
|
||||
zstyle ':antidote:bundle' use-friendly-names 'yes'
|
||||
zstyle ':antidote:static' file "${DOTFILES}/antidote/zsh_plugins"
|
||||
zstyle ':antidote:static' zcompile 'yes'
|
||||
if [[ ! ${zsh_plugins} -nt ${zsh_plugins}.txt ]]; then
|
||||
echo "${$(antidote bundle <${zsh_plugins}.txt)//source/call_file}" >|${zsh_plugins}
|
||||
#antidote bundle <${zsh_plugins}.txt >|${zsh_plugins}.compile
|
||||
fi
|
||||
call_file ${zsh_plugins} "antidote_plugin"
|
||||
ZSH=$(antidote path ohmyzsh/ohmyzsh)
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
function {
|
||||
0=${(%):-%x}
|
||||
local staticfile=${0:A}
|
||||
[[ -e ${staticfile} ]] || return 1
|
||||
if [[ ! -s ${staticfile}.zwc || ${staticfile} -nt ${staticfile}.zwc ]]; then
|
||||
builtin autoload -Uz zrecompile
|
||||
zrecompile -pq ${staticfile}
|
||||
fi
|
||||
}
|
||||
fpath+=( "$HOME/Library/Caches/antidote/zsh-users/zsh-syntax-highlighting" )
|
||||
call_file "$HOME/Library/Caches/antidote/zsh-users/zsh-syntax-highlighting/zsh-syntax-highlighting.plugin.zsh"
|
||||
fpath+=( "$HOME/Library/Caches/antidote/zsh-users/zsh-autosuggestions" )
|
||||
call_file "$HOME/Library/Caches/antidote/zsh-users/zsh-autosuggestions/zsh-autosuggestions.plugin.zsh"
|
||||
fpath+=( "$HOME/Library/Caches/antidote/zsh-users/zsh-history-substring-search" )
|
||||
call_file "$HOME/Library/Caches/antidote/zsh-users/zsh-history-substring-search/zsh-history-substring-search.plugin.zsh"
|
||||
fpath+=( "$HOME/Library/Caches/antidote/mattmc3/zephyr/plugins/color" )
|
||||
call_file "$HOME/Library/Caches/antidote/mattmc3/zephyr/plugins/color/color.plugin.zsh"
|
||||
fpath+=( "$HOME/Library/Caches/antidote/mattmc3/zephyr/plugins/helper" )
|
||||
call_file "$HOME/Library/Caches/antidote/mattmc3/zephyr/plugins/helper/helper.plugin.zsh"
|
||||
if is-macos; then
|
||||
fpath+=( "$HOME/Library/Caches/antidote/mattmc3/zephyr/plugins/macos" )
|
||||
call_file "$HOME/Library/Caches/antidote/mattmc3/zephyr/plugins/macos/macos.plugin.zsh"
|
||||
fi
|
||||
fpath+=( "$HOME/Library/Caches/antidote/getantidote/use-omz" )
|
||||
call_file "$HOME/Library/Caches/antidote/getantidote/use-omz/use-omz.plugin.zsh"
|
||||
if ! (( $+functions[zsh-defer] )); then
|
||||
fpath+=( "$HOME/Library/Caches/antidote/romkatv/zsh-defer" )
|
||||
call_file "$HOME/Library/Caches/antidote/romkatv/zsh-defer/zsh-defer.plugin.zsh"
|
||||
fi
|
||||
fpath+=( "$HOME/Library/Caches/antidote/ohmyzsh/ohmyzsh/plugins/sudo" )
|
||||
zsh-defer call_file "$HOME/Library/Caches/antidote/ohmyzsh/ohmyzsh/plugins/sudo/sudo.plugin.zsh"
|
||||
fpath+=( "$HOME/Library/Caches/antidote/ohmyzsh/ohmyzsh/plugins/mise" )
|
||||
zsh-defer call_file "$HOME/Library/Caches/antidote/ohmyzsh/ohmyzsh/plugins/mise/mise.plugin.zsh"
|
||||
fpath+=( "$HOME/Library/Caches/antidote/ohmyzsh/ohmyzsh/plugins/pip" )
|
||||
zsh-defer call_file "$HOME/Library/Caches/antidote/ohmyzsh/ohmyzsh/plugins/pip/pip.plugin.zsh"
|
||||
fpath+=( "$HOME/Library/Caches/antidote/ohmyzsh/ohmyzsh/custom/plugins/you-should-use" )
|
||||
zsh-defer call_file "$HOME/Library/Caches/antidote/ohmyzsh/ohmyzsh/custom/plugins/you-should-use/you-should-use.plugin.zsh"
|
||||
fpath+=( "$HOME/Library/Caches/antidote/ohmyzsh/ohmyzsh/plugins/aliases" )
|
||||
zsh-defer call_file "$HOME/Library/Caches/antidote/ohmyzsh/ohmyzsh/plugins/aliases/aliases.plugin.zsh"
|
||||
fpath+=( "$HOME/Library/Caches/antidote/joshskidmore/zsh-fzf-history-search" )
|
||||
call_file "$HOME/Library/Caches/antidote/joshskidmore/zsh-fzf-history-search/zsh-fzf-history-search.plugin.zsh"
|
||||
fpath+=( "$HOME/Library/Caches/antidote/zsh-hooks/zsh-hooks" )
|
||||
call_file "$HOME/Library/Caches/antidote/zsh-hooks/zsh-hooks/zsh-hooks.plugin.zsh"
|
||||
fpath+=( "$HOME/Library/Caches/antidote/mattmc3/zfunctions" )
|
||||
call_file "$HOME/Library/Caches/antidote/mattmc3/zfunctions/zfunctions.plugin.zsh"
|
||||
fpath+=( "$HOME/Library/Caches/antidote/zdharma-continuum/fast-syntax-highlighting" )
|
||||
zsh-defer call_file "$HOME/Library/Caches/antidote/zdharma-continuum/fast-syntax-highlighting/fast-syntax-highlighting.plugin.zsh"
|
||||
@@ -0,0 +1,31 @@
|
||||
# .zsh_plugins.txt
|
||||
zsh-users/zsh-syntax-highlighting
|
||||
zsh-users/zsh-autosuggestions
|
||||
zsh-users/zsh-history-substring-search
|
||||
|
||||
|
||||
mattmc3/zephyr path:plugins/color
|
||||
mattmc3/zephyr path:plugins/helper
|
||||
mattmc3/zephyr path:plugins/macos conditional:is-macos
|
||||
|
||||
|
||||
# Oh-my-zsh dependency management
|
||||
getantidote/use-omz
|
||||
#ohmyzsh/ohmyzsh path:lib
|
||||
#ohmyzsh/ohmyzsh path:plugins/macos conditional:is-macos
|
||||
ohmyzsh/ohmyzsh path:plugins/sudo kind:defer
|
||||
#ohmyzsh/ohmyzsh path:plugins/asdf kind:defer
|
||||
#ohmyzsh/ohmyzsh path:custom/plugins/fzf-tab kind:defer
|
||||
ohmyzsh/ohmyzsh path:plugins/mise kind:defer
|
||||
ohmyzsh/ohmyzsh path:plugins/pip kind:defer
|
||||
#ohmyzsh/ohmyzsh path:plugins/golang kind:defer
|
||||
#ohmyzsh/ohmyzsh path:plugins/git kind:defer
|
||||
ohmyzsh/ohmyzsh path:custom/plugins/you-should-use kind:defer
|
||||
ohmyzsh/ohmyzsh path:plugins/aliases kind:defer
|
||||
joshskidmore/zsh-fzf-history-search
|
||||
zsh-hooks/zsh-hooks
|
||||
|
||||
mattmc3/zfunctions
|
||||
zdharma-continuum/fast-syntax-highlighting kind:defer
|
||||
|
||||
#romkatv/zsh-bench kind:path
|
||||
@@ -0,0 +1,2 @@
|
||||
|
||||
.autoversion
|
||||
@@ -0,0 +1,8 @@
|
||||
#!/bin/zsh
|
||||
|
||||
if [[ -n $VIRTUAL_ENV && ! -f "./$VENV_FILE_EXTENSION" ]]; then
|
||||
if [[ ! -e $(JUPYTER_PREFER_ENV_PATH=0 jupyter --data-dir)"/kernels/"$(basename "${VIRTUAL_ENV//%\/.venv/}") ]]; then
|
||||
[[ `$VIRTUAL_ENV/bin/python -m ipykernel -V &> /dev/null` ]] || $VIRTUAL_ENV/bin/python -m pip install ipykernel
|
||||
$VIRTUAL_ENV/bin/python -m ipykernel install --user --name=$(basename "${VIRTUAL_ENV//%\/.venv/}")
|
||||
fi
|
||||
fi
|
||||
@@ -0,0 +1,65 @@
|
||||
#autoload
|
||||
|
||||
bd () {
|
||||
(($#<1)) && {
|
||||
print -- "usage: $0 <name-of-any-parent-directory>"
|
||||
print -- " $0 <number-of-folders>"
|
||||
return 1
|
||||
} >&2
|
||||
# example:
|
||||
# $PWD == /home/arash/abc ==> $num_folders_we_are_in == 3
|
||||
local num_folders_we_are_in=${#${(ps:/:)${PWD}}}
|
||||
local dest="./"
|
||||
|
||||
# First try to find a folder with matching name (could potentially be a number)
|
||||
# Get parents (in reverse order)
|
||||
local parents
|
||||
local i
|
||||
for i in {$num_folders_we_are_in..2}
|
||||
do
|
||||
parents=($parents "$(echo $PWD | cut -d'/' -f$i)")
|
||||
done
|
||||
parents=($parents "/")
|
||||
# Build dest and 'cd' to it
|
||||
local parent
|
||||
foreach parent (${parents})
|
||||
do
|
||||
dest+="../"
|
||||
if [[ $1 == $parent ]]
|
||||
then
|
||||
cd $dest
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
# If the user provided an integer, go up as many times as asked
|
||||
dest="./"
|
||||
if [[ "$1" = <-> ]]
|
||||
then
|
||||
if [[ $1 -gt $num_folders_we_are_in ]]
|
||||
then
|
||||
print -- "bd: Error: Can not go up $1 times (not enough parent directories)"
|
||||
return 1
|
||||
fi
|
||||
for i in {1..$1}
|
||||
do
|
||||
dest+="../"
|
||||
done
|
||||
cd $dest
|
||||
return 0
|
||||
fi
|
||||
|
||||
# If the above methods fail
|
||||
print -- "bd: Error: No parent directory named '$1'"
|
||||
return 1
|
||||
}
|
||||
_bd () {
|
||||
# Get parents (in reverse order)
|
||||
local num_folders_we_are_in=${#${(ps:/:)${PWD}}}
|
||||
local i
|
||||
for i in {$num_folders_we_are_in..2}
|
||||
do
|
||||
reply=($reply "`echo $PWD | cut -d'/' -f$i`")
|
||||
done
|
||||
reply=($reply "/")
|
||||
}
|
||||
@@ -0,0 +1,72 @@
|
||||
#autoload
|
||||
|
||||
biggest() {
|
||||
emulate -L zsh
|
||||
setopt pipe_fail
|
||||
|
||||
local count=20
|
||||
local pattern='*'
|
||||
|
||||
# Option parsing
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
-n|--number)
|
||||
count="$2"
|
||||
shift 2
|
||||
;;
|
||||
-p|--pattern)
|
||||
pattern="$2"
|
||||
shift 2
|
||||
;;
|
||||
-h|--help)
|
||||
cat <<'EOF'
|
||||
Usage: biggest [-n COUNT | --number COUNT] [-p PATTERN | --pattern PATTERN]
|
||||
|
||||
-n, --number Number of entries to show (default: 20)
|
||||
-p, --pattern Glob-style pattern to match names (default: *)
|
||||
-h, --help Show this help and exit
|
||||
|
||||
The command scans up to three directory levels below the current
|
||||
directory, includes hidden files and directories, and prints the
|
||||
largest files/directories sorted by size. Sizes are sorted using
|
||||
du -sk (numeric) and displayed using du -sh (human-readable).
|
||||
EOF
|
||||
return 0
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
-*)
|
||||
print -u2 "biggest: unknown option: $1"
|
||||
return 1
|
||||
;;
|
||||
*)
|
||||
# Ignore unexpected positional args for now
|
||||
break
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# Validate count
|
||||
if ! [[ "$count" == <-> ]]; then
|
||||
print -u2 "biggest: COUNT must be a positive integer"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Core logic: depth 1–3, files and dirs, include hidden, suppress errors
|
||||
find . -mindepth 1 -maxdepth 3 -name "$pattern" -print0 2>/dev/null \
|
||||
| xargs -0 -n 100 du -sk 2>/dev/null \
|
||||
| sort -nr \
|
||||
| head -n "$count" \
|
||||
| cut -f2- \
|
||||
| tr '\n' '\0' \
|
||||
| xargs -0 -I{} du -sh "{}" 2>/dev/null \
|
||||
| awk '{
|
||||
size=$1
|
||||
$1=""
|
||||
sub(/^ /,"")
|
||||
# green size, blue path
|
||||
printf "\033[1;32m%-8s\033[0m \033[1;34m%s\033[0m\n", size, $0
|
||||
}'
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
# ===============================================================
|
||||
# call_file — Timed JSON logger for Zsh config execution
|
||||
# ===============================================================
|
||||
# Source this file early (e.g. in ~/.zshenv). It defines a function:
|
||||
# call_file /path/to/script.zsh [label]
|
||||
# Logs NDJSON lines to $outfile (default: ~/zshrc-log.json)
|
||||
|
||||
function call_file() {
|
||||
# No set -u here; be tolerant during early boot
|
||||
local filepath="$1"
|
||||
local label="${2:-}"
|
||||
local resolved_path exit_code=0 duration_ms start_time end_time LOGFILE json_entry
|
||||
|
||||
[[ -z "$filepath" ]] && return 0
|
||||
|
||||
LOGFILE="${outfile:-$HOME/zshrc-log.json}"
|
||||
: "${CALL_FILE_DEBUG:=0}"
|
||||
|
||||
# Resolve path (follow symlinks if present), fallback safely on macOS
|
||||
if [[ -L "$filepath" ]]; then
|
||||
if command -v readlink >/dev/null 2>&1 && readlink -f / >/dev/null 2>&1; then
|
||||
resolved_path="$(readlink -f "$filepath" 2>/dev/null || echo "$filepath")"
|
||||
elif command -v realpath >/dev/null 2>&1; then
|
||||
resolved_path="$(realpath "$filepath" 2>/dev/null || echo "$filepath")"
|
||||
else
|
||||
resolved_path="$filepath"
|
||||
fi
|
||||
else
|
||||
resolved_path="$filepath"
|
||||
fi
|
||||
|
||||
# Derive label if not provided: config.zsh -> parent dir name, else file stem
|
||||
if [[ -z "$label" ]]; then
|
||||
local fname="${resolved_path:t}"
|
||||
local dname="${resolved_path:h:t}"
|
||||
if [[ "$fname" == "config.zsh" ]]; then
|
||||
label="$dname"
|
||||
else
|
||||
label="${fname:r}"
|
||||
fi
|
||||
fi
|
||||
|
||||
# Guard if file missing
|
||||
[[ ! -r "$resolved_path" ]] && {
|
||||
print -u2 "call_file: missing or unreadable: $resolved_path"
|
||||
return 1
|
||||
}
|
||||
|
||||
# Timestamps (μs precision). Fall back if EPOCHREALTIME is unavailable.
|
||||
start_time="${EPOCHREALTIME:-$(perl -MTime::HiRes=time -e 'printf "%.6f\n", time()')}"
|
||||
[[ "$CALL_FILE_DEBUG" != "0" ]] && print -- "▶️ Starting $resolved_path (label: $label)"
|
||||
|
||||
{
|
||||
# source quietly; the config can print if it wants—logs stay clean
|
||||
source "$resolved_path"
|
||||
} >/dev/null 2>&1 || exit_code=$?
|
||||
|
||||
end_time="${EPOCHREALTIME:-$(perl -MTime::HiRes=time -e 'printf "%.6f\n", time()')}"
|
||||
# Floating math via awk to avoid zsh int math pitfalls
|
||||
duration_ms=$(awk -v s="$start_time" -v e="$end_time" 'BEGIN {printf "%.5f", (e - s) * 1000}')
|
||||
|
||||
[[ "$CALL_FILE_DEBUG" != "0" ]] && print -- "⏱ Finished $resolved_path in ${duration_ms}ms (exit:${exit_code})"
|
||||
|
||||
# Ensure logfile dir exists
|
||||
mkdir -p -- "$(dirname -- "$LOGFILE")" 2>/dev/null
|
||||
|
||||
# NDJSON entry (numbers are real numbers via --argjson)
|
||||
json_entry=$(jq -nc \
|
||||
--arg file "$resolved_path" \
|
||||
--arg label "$label" \
|
||||
--argjson start "$(printf "%.5f" "$start_time")" \
|
||||
--argjson end "$(printf "%.5f" "$end_time")" \
|
||||
--argjson duration "$(printf "%.5f" "$duration_ms")" \
|
||||
--argjson code "$exit_code" \
|
||||
'{file:$file, label:$label, start:$start, end:$end, duration_ms:$duration, exit_code:$code}')
|
||||
|
||||
# Append (don’t fail if file doesn’t exist yet)
|
||||
if [[ -w "$LOGFILE" || ! -e "$LOGFILE" ]]; then
|
||||
print -- "$json_entry" >>"$LOGFILE"
|
||||
fi
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
#!/usr/bin/env zsh
|
||||
NEWPATH=()
|
||||
DELPATH=()
|
||||
new_path() {
|
||||
for el in ${${(R)${(us/:/)PATH}:#* *}[@]}; do
|
||||
if [[ -d $el ]]; then
|
||||
NEWPATH+=$el
|
||||
else
|
||||
DELPATH+=$el
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
new_path
|
||||
# echo ${(F)DELPATH}
|
||||
echo ${(j/:/)NEWPATH}
|
||||
@@ -0,0 +1,159 @@
|
||||
#autoload
|
||||
# compinit_doctor — inspect, audit, diff, and (optionally) rebuild zsh completion state
|
||||
# Usage: compinit_doctor [--status] [--audit] [--rebuild] [--snap FILE] [--diff FILE] [--dump PATH] [--json] [--help]
|
||||
|
||||
compinit_doctor() {
|
||||
emulate -L zsh
|
||||
setopt extended_glob
|
||||
zmodload -F zsh/datetime 2>/dev/null || true
|
||||
|
||||
# -------- defaults --------
|
||||
local dump="${ZSH_COMPDUMP:-${XDG_CACHE_HOME:-$HOME/.cache}/zcompdump}"
|
||||
local action=status json=0 snap_file="" diff_file=""
|
||||
|
||||
# -------- arg parse --------
|
||||
while (( $# > 0 )); do
|
||||
case "$1" in
|
||||
--help|-h) action=help;;
|
||||
--status|-s) action=status;;
|
||||
--audit|-a) action=audit;;
|
||||
--rebuild|-r) action=rebuild;;
|
||||
--snap|-S) shift; snap_file="${1:-}"; [[ -z $snap_file ]] && { print -ru2 "compinit_doctor: --snap requires a file"; return 2; }; action=snap;;
|
||||
--diff|-d) shift; diff_file="${1:-}"; [[ -z $diff_file ]] && { print -ru2 "compinit_doctor: --diff requires a file"; return 2; }; action=diff;;
|
||||
--dump=*|-D=*) dump="${1#*=}";;
|
||||
--dump|-D) shift; dump="${1:-$dump}";;
|
||||
--json|-j) json=1;;
|
||||
*) print -ru2 "compinit_doctor: unknown option: $1"; return 2;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
# -------- helpers --------
|
||||
__mtime() {
|
||||
[[ -e $1 ]] && stat -c %Y -- "$1" 2>/dev/null || return 1
|
||||
}
|
||||
__age_human() {
|
||||
local mt now delta
|
||||
mt="$(__mtime "$1")" || { print -r -- "—"; return 0; }
|
||||
now="${EPOCHSECONDS:-$(date +%s)}"
|
||||
delta=$(( now - mt )); (( delta < 0 )) && delta=0
|
||||
printf "%dd %02dh %02dm" $((delta/86400)) $(((delta%86400)/3600)) $(((delta%3600)/60))
|
||||
}
|
||||
__exists() { [[ -e $1 ]] && print yes || print no; }
|
||||
__count_comps() { (( ${+_comps} )) && print ${#_comps} || print 0; }
|
||||
|
||||
__print_status_text() {
|
||||
local dump_mtime dump_age compiled="${dump}.zwc"
|
||||
dump_mtime="$(__mtime "$dump" 2>/dev/null)" || dump_mtime="—"
|
||||
dump_age="$(__age_human "$dump")"
|
||||
|
||||
print -r -- "compinit status:"
|
||||
print -r -- " dump path : $dump"
|
||||
print -r -- " dump exists : $(__exists "$dump")"
|
||||
print -r -- " dump age : $dump_age"
|
||||
print -r -- " compiled exists : $(__exists "$compiled")"
|
||||
print -r -- " \$fpath entries : ${#fpath}"
|
||||
print -r -- " \$fpath missing : $(
|
||||
local -a miss d
|
||||
for d in $fpath; do [[ -d $d ]] || miss+=("$d"); done
|
||||
print ${#miss}
|
||||
)"
|
||||
print -r -- " completion funcs : $(__count_comps)"
|
||||
print -r -- " compinit present : $((($+functions[compinit] || $+commands[compinit])) && print "yes" || print "no")"
|
||||
}
|
||||
|
||||
__print_status_json() {
|
||||
local -a missing=(); local d
|
||||
for d in $fpath; do [[ -d $d ]] || missing+=("$d"); done
|
||||
local compinit_present=false
|
||||
if (( $+functions[compinit] || $+commands[compinit] )); then compinit_present=true; fi
|
||||
|
||||
printf '{"dump":"%s","dump_exists":%s,"dump_age":"%s","compiled_exists":%s,' \
|
||||
"$dump" "$([[ -e $dump ]] && print true || print false)" "$(__age_human "$dump")" "$([[ -e ${dump}.zwc ]] && print true || print false)"
|
||||
printf '"fpath_count":%d,"fpath_missing_count":%d,"completion_funcs":%d,"compinit_present":%s}\n' \
|
||||
"${#fpath}" "${#missing}" "$(__count_comps)" "$compinit_present"
|
||||
}
|
||||
|
||||
__do_audit() {
|
||||
if ! whence -w compaudit >/dev/null 2>&1; then
|
||||
print -ru2 "compinit_doctor: compaudit not available"; return 3
|
||||
fi
|
||||
local -a insecure
|
||||
insecure=(${(f)"$(compaudit 2>/dev/null)"})
|
||||
if (( ${#insecure} )); then
|
||||
print -r -- "Insecure completion dirs/files:"
|
||||
printf " %s\n" "${insecure[@]}"
|
||||
print -r -- "\nSuggested fix (review before running):"
|
||||
print -r -- " chmod -R go-w -- ${insecure:q}"
|
||||
else
|
||||
print -r -- "No insecure completion paths found (compaudit)."
|
||||
fi
|
||||
}
|
||||
|
||||
__do_rebuild() {
|
||||
local t0 t1 dt
|
||||
t0="${EPOCHREALTIME:-$(perl -MTime::HiRes=time -e 'printf \"%.6f\", time()')}"
|
||||
autoload -U compinit
|
||||
compinit -i -d "$dump" || { print -ru2 "compinit_doctor: compinit failed"; return 4; }
|
||||
{ zcompile "$dump" 2>/dev/null || true; } &
|
||||
t1="${EPOCHREALTIME:-$(perl -MTime::HiRes=time -e 'printf \"%.6f\", time()')}"
|
||||
dt=$(awk -v s="$t0" -v e="$t1" 'BEGIN{printf "%.3f",(e-s)*1000}')
|
||||
printf 'Rebuilt dump at: %s (compinit %.3fms)\n' "$dump" "$dt"
|
||||
}
|
||||
|
||||
__snap() {
|
||||
local file="$1"
|
||||
[[ -n $file ]] || { print -ru2 "compinit_doctor: snapshot file required"; return 2; }
|
||||
mkdir -p -- "${file:h}" 2>/dev/null
|
||||
{
|
||||
print "# compinit_doctor snapshot"
|
||||
print "# date: $(date +'%Y-%m-%d %H:%M:%S %z')"
|
||||
printf "%s\n" "${(o)fpath[@]}"
|
||||
} >| "$file"
|
||||
print -r -- "Snapshot written: $file"
|
||||
}
|
||||
|
||||
__diff() {
|
||||
local file="$1"
|
||||
[[ -r $file ]] || { print -ru2 "compinit_doctor: cannot read snapshot: $file"; return 2; }
|
||||
local -a snap cur added removed
|
||||
snap=(${(f)"$(<"$file")"})
|
||||
snap=(${snap:#\#*})
|
||||
cur=(${(o)fpath})
|
||||
local -Ua _S _C
|
||||
_S=("${snap[@]}"); _C=("${cur[@]}")
|
||||
local s c
|
||||
for s in "${_S[@]}"; do [[ -z ${_C[(r)$s]} ]] && removed+=("$s"); done
|
||||
for c in "${_C[@]}"; do [[ -z ${_S[(r)$c]} ]] && added+=("$c"); done
|
||||
print -r -- "fpath diff vs $file:"
|
||||
print -r -- " added (${#added}):"
|
||||
printf " + %s\n" "${added[@]}"
|
||||
print -r -- " removed(${#removed}):"
|
||||
printf " - %s\n" "${removed[@]}"
|
||||
}
|
||||
|
||||
# -------- dispatch --------
|
||||
case "$action" in
|
||||
help)
|
||||
cat <<'EOF'
|
||||
compinit_doctor — inspect, audit, diff, and rebuild zsh completion state
|
||||
|
||||
--status|-s Show current compinit/zcompdump status (default)
|
||||
--audit |-a Run compaudit and list insecure paths
|
||||
--rebuild|-r Force compinit -i -d <dump> and background zcompile
|
||||
--snap |-S FILE Snapshot current $fpath (sorted) to FILE
|
||||
--diff |-d FILE Diff current $fpath against FILE snapshot
|
||||
--dump |-D PATH Override zcompdump path (default: $XDG_CACHE_HOME/zcompdump)
|
||||
--json |-j Emit status as a single JSON object
|
||||
--help |-h Show this help
|
||||
EOF
|
||||
;;
|
||||
audit) __do_audit;;
|
||||
rebuild) __do_rebuild;;
|
||||
snap) __snap "$snap_file";;
|
||||
diff) __diff "$diff_file";;
|
||||
status)
|
||||
(( json )) && __print_status_json || __print_status_text
|
||||
;;
|
||||
esac
|
||||
}
|
||||
@@ -0,0 +1,133 @@
|
||||
#autoload
|
||||
|
||||
# Helper: backup a file via a dummy backup script/command
|
||||
confe_backup_file() {
|
||||
emulate -L zsh
|
||||
|
||||
local file=${1:A}
|
||||
if [[ -z $file ]]; then
|
||||
print -u2 "confe: no file passed to confe_backup_file"
|
||||
return 1
|
||||
fi
|
||||
if [[ ! -e $file ]]; then
|
||||
print -u2 "confe: cannot backup, file does not exist: $file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# You can override this with: CONFE_BACKUP_CMD=your-script
|
||||
local backup_cmd=${CONFE_BACKUP_CMD:-echo}
|
||||
|
||||
# Dummy backup – replace with your real backup script
|
||||
"$backup_cmd" "$file"
|
||||
}
|
||||
|
||||
# Helper: stage a file in its git repository (if there is one)
|
||||
confe_stage_file() {
|
||||
emulate -L zsh
|
||||
|
||||
local file=${1:A}
|
||||
if [[ -z $file ]]; then
|
||||
print -u2 "confe: no file passed to confe_stage_file"
|
||||
return 1
|
||||
fi
|
||||
if [[ ! -e $file ]]; then
|
||||
print -u2 "confe: cannot stage, file does not exist: $file"
|
||||
return 1
|
||||
fi
|
||||
|
||||
local dir=${file:h}
|
||||
|
||||
if git -C "$dir" rev-parse --is-inside-work-tree &>/dev/null; then
|
||||
git -C "$dir" add "$file"
|
||||
else
|
||||
print -u2 "confe: $file is not inside a git repository"
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
confe() {
|
||||
emulate -L zsh
|
||||
|
||||
# Main executable for editing – define here.
|
||||
# You can also override with CONFE_EDIT_CMD in your env.
|
||||
local edit_cmd=${CONFE_EDIT_CMD:-${EDITOR:-vim}}
|
||||
[[ -z $edit_cmd ]] && edit_cmd=nvim
|
||||
|
||||
local do_backup=false
|
||||
local do_staged=false
|
||||
local -a positional
|
||||
|
||||
# Option parsing: --backup/-b, --staged/-s
|
||||
while (( $# > 0 )); do
|
||||
case "$1" in
|
||||
--backup|-b)
|
||||
do_backup=true
|
||||
;;
|
||||
--staged|-s)
|
||||
do_staged=true
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
positional+=("$@")
|
||||
break
|
||||
;;
|
||||
--*)
|
||||
print -u2 "confe: unknown option: $1"
|
||||
return 1
|
||||
;;
|
||||
-*)
|
||||
print -u2 "confe: unknown short option: $1"
|
||||
return 1
|
||||
;;
|
||||
*)
|
||||
positional+=("$1")
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
|
||||
set -- "${positional[@]}"
|
||||
|
||||
local topic=$1
|
||||
if [[ -z $topic ]]; then
|
||||
print -u2 "usage: confe [--backup|-b] [--staged|-s] (alias|keybindings|options|path|packages|ssh|brewfile)"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Associative array: topic → file path
|
||||
local -A CONFE_FILES=(
|
||||
alias "$ZDOTDIR/alias.zsh"
|
||||
keybindings "$ZDOTDIR/keybindings.zsh"
|
||||
options "$ZDOTDIR/options.zsh"
|
||||
path "$ZDOTDIR/path.zsh"
|
||||
packages "$DOTFILES/antidote/zsh_plugins.txt"
|
||||
ssh "$HOME/.ssh/config"
|
||||
brewfile "$DOTFILES/00-homebrew/Brewfile"
|
||||
)
|
||||
|
||||
local file=${CONFE_FILES[$topic]}
|
||||
|
||||
if [[ -z $file ]]; then
|
||||
print -u2 "confe: unknown topic: $topic"
|
||||
print -u2 "valid topics: ${(j:, :)${(k)CONFE_FILES}}"
|
||||
return 1
|
||||
fi
|
||||
|
||||
# Run backup first if requested
|
||||
if $do_backup; then
|
||||
confe_backup_file "$file" || return $?
|
||||
fi
|
||||
|
||||
# Main dummy command – you replace by setting edit_cmd / CONFE_EDIT_CMD
|
||||
eval "$edit_cmd" $file(:P) || return $?
|
||||
if [[ "${file}(:e)" == "zsh" ]]; then
|
||||
source $file(:P)
|
||||
fi
|
||||
|
||||
# Stage changes if requested
|
||||
if $do_staged; then
|
||||
confe_stage_file "$file" || return $?
|
||||
fi
|
||||
}
|
||||
|
||||
confe ${1:-ssh}
|
||||
@@ -0,0 +1,62 @@
|
||||
#autoload
|
||||
#
|
||||
copy_hosts() {
|
||||
local -a cp_hosts_default=( infra-dns-1 swarm-mgr-1 swarm-mgr-2 swarm-mgr-3 proxmox-01 )
|
||||
local -a cp_hosts_new=()
|
||||
local -a cp_hosts=()
|
||||
local -a cp_sources=()
|
||||
local saw_hosts=0
|
||||
|
||||
while (( $# > 0 )); do
|
||||
case "$1" in
|
||||
-h|--hosts)
|
||||
saw_hosts=1
|
||||
shift
|
||||
|
||||
# Collect host names until:
|
||||
# - we hit "--"
|
||||
# - or only one argument remains (treat that as a source)
|
||||
while (( $# > 1 )) && [[ "$1" != "--" ]]; do
|
||||
cp_hosts_new+=("$1")
|
||||
shift
|
||||
done
|
||||
|
||||
# Keep only hosts that exist in cp_hosts_default
|
||||
cp_hosts=( ${cp_hosts_default:*cp_hosts_new} )
|
||||
|
||||
# Skip the separator if present
|
||||
if (( $# > 0 )) && [[ "$1" == "--" ]]; then
|
||||
shift
|
||||
fi
|
||||
;;
|
||||
|
||||
--)
|
||||
shift
|
||||
cp_sources+=( "$@" )
|
||||
break
|
||||
;;
|
||||
|
||||
*)
|
||||
cp_sources+=("$1")
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# No -h/--hosts provided: use defaults
|
||||
if (( ! saw_hosts )); then
|
||||
cp_hosts=( "${cp_hosts_default[@]}" )
|
||||
fi
|
||||
|
||||
# Nothing valid matched from -h/--hosts: fall back to defaults
|
||||
if (( saw_hosts )) && (( ${#cp_hosts[@]} == 0 )); then
|
||||
cp_hosts=( "${cp_hosts_default[@]}" )
|
||||
fi
|
||||
|
||||
for dotdir in "${cp_sources[@]}"; do
|
||||
[[ -e "${DOTFILES}/${dotdir}" ]] || continue
|
||||
for dothost in "${cp_hosts[@]}"; do
|
||||
dotpush "$dothost" "$dotdir"
|
||||
done
|
||||
done
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
#autoload
|
||||
dot () {
|
||||
_dotfiles="${DOTFILES:-$HOME/.dotfiles}"
|
||||
if [ $# -eq 1 ]; then
|
||||
if [[ -d "${_dotfiles}/$1" ]]; then
|
||||
cd "${_dotfiles}/$1"
|
||||
else
|
||||
echo "no dir $1 in ${_dotfiles}"
|
||||
fi
|
||||
else
|
||||
cd "${_dotfiles}"
|
||||
fi
|
||||
if command -v eza >/dev/null 2>&1; then
|
||||
eza --grid --git-ignore --all --header --grid --no-permissions --no-filesize --no-time --no-user --icons --git --hyperlink
|
||||
elif command -v colorls >/dev/null 2>&1; then
|
||||
colorls -A --sd --hyperlink --gs
|
||||
else
|
||||
ls -a --color=auto
|
||||
fi
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
#!/usr/bin/env zsh
|
||||
# ===============================================================
|
||||
# dotfiles_doctor — Zsh Config Health & Timeline Analyzer
|
||||
# ===============================================================
|
||||
|
||||
# Be interactive-safe: never kill the session on errors here
|
||||
unsetopt errexit 2>/dev/null || true
|
||||
unsetopt nounset 2>/dev/null || true
|
||||
unsetopt pipefail 2>/dev/null || true
|
||||
setopt NO_NOMATCH
|
||||
|
||||
LOGFILE="$HOME/zshrc-log.json"
|
||||
SHOW_TIMELINE=0
|
||||
SLOW_THRESHOLD=${SLOW_THRESHOLD:-200}
|
||||
WARN_THRESHOLD=${WARN_THRESHOLD:-500}
|
||||
|
||||
# --- Argument parsing (flags only; NDJSON log stays default unless overridden) ---
|
||||
for arg in "$@"; do
|
||||
case "$arg" in
|
||||
--timeline) SHOW_TIMELINE=1 ;;
|
||||
--logfile=*) LOGFILE="${arg#*=}" ;;
|
||||
esac
|
||||
done
|
||||
|
||||
# --- Pre-flight checks (non-fatal for interactive) ---
|
||||
if ! command -v jq >/dev/null 2>&1; then
|
||||
print -P "%F{red}❌ jq is required (brew install jq)%f"
|
||||
return 1 2>/dev/null || exit 1
|
||||
fi
|
||||
if [[ ! -f "$LOGFILE" ]]; then
|
||||
print -P "%F{red}❌ No log file found:%f $LOGFILE"
|
||||
return 1 2>/dev/null || exit 1
|
||||
fi
|
||||
|
||||
print
|
||||
print "🩺 Dotfiles Doctor — Startup Diagnostic"
|
||||
print "📄 Log: $LOGFILE"
|
||||
print "⚙️ Slow threshold: ${SLOW_THRESHOLD}ms | Warn threshold: ${WARN_THRESHOLD}ms"
|
||||
print "---------------------------------------------------------------"
|
||||
|
||||
# ---- Individual file timings (slurp NDJSON -> array; then iterate) ----
|
||||
jq -sr '.[] | [.label, (.duration_ms // 0), (.exit_code // 0), .file] | @tsv' "$LOGFILE" |
|
||||
while IFS=$'\t' read -r label duration exit_code file; do
|
||||
color="" reset=$'\033[0m' emoji="✅"
|
||||
if (( exit_code != 0 )); then color=$'\033[31m'; emoji="💥"
|
||||
elif (( ${duration%.*} > WARN_THRESHOLD )); then color=$'\033[31m'; emoji="🔥"
|
||||
elif (( ${duration%.*} > SLOW_THRESHOLD )); then color=$'\033[33m'; emoji="⚠️ "
|
||||
else color=$'\033[32m'; fi
|
||||
printf "%b%-22s %8.3fms (exit:%3d) %s%b\n" \
|
||||
"$color$emoji " "$label" "$duration" "$exit_code" "$file" "$reset"
|
||||
done
|
||||
|
||||
print "---------------------------------------------------------------"
|
||||
|
||||
# ---- Aggregate summary (right-justified, 3 decimals) ----
|
||||
echo "📊 Aggregate timings:"
|
||||
jq -sr '
|
||||
group_by(.label)
|
||||
| map({
|
||||
label: .[0].label,
|
||||
count: length,
|
||||
total: (map(.duration_ms // 0) | add),
|
||||
avg: ((map(.duration_ms // 0) | add) / length)
|
||||
})
|
||||
| sort_by(.label)
|
||||
| .[]
|
||||
| [.label, (.count|tostring), (.total|tostring), (.avg|tostring)]
|
||||
| @tsv
|
||||
' "$LOGFILE" |
|
||||
awk -F '\t' '
|
||||
BEGIN {
|
||||
printf "%-26s %7s %12s %10s\n", "label", "count", "total_ms", "avg_ms"
|
||||
printf "%-26s %7s %12s %10s\n", "-----", "-----", "--------", "------"
|
||||
}
|
||||
{
|
||||
label=$1; count=$2+0; total=$3+0; avg=$4+0
|
||||
printf "%-26s %7d %12.3f %10.3f\n", label, count, total, avg
|
||||
}'
|
||||
|
||||
# ---- Totals ----
|
||||
sum_total=$(jq -sr '[.[].duration_ms // 0] | add' "$LOGFILE")
|
||||
# Session elapsed = (max end - min start) * 1000 (if present)
|
||||
session_total_ms=$(jq -sr '
|
||||
( [.[].start] | map(select(. != null)) ) as $s
|
||||
| ( [.[].end] | map(select(. != null)) ) as $e
|
||||
| if ($s|length)>0 and ($e|length)>0
|
||||
then (( ($e|max) - ($s|min) ) * 1000)
|
||||
else 0
|
||||
end
|
||||
' "$LOGFILE")
|
||||
|
||||
fails=$(jq -sr '[.[].exit_code // 0] | map(select(. != 0)) | length' "$LOGFILE")
|
||||
slows=$(jq -sr "[.[].duration_ms // 0] | map(select(. > $SLOW_THRESHOLD)) | length" "$LOGFILE")
|
||||
|
||||
printf "\n🕒 Total (sum of durations): %.5fms\n" "$sum_total"
|
||||
printf "🕒 Session elapsed (first→last): %.5fms\n" "$session_total_ms"
|
||||
print "💀 Failed modules: $fails"
|
||||
print "🐢 Slow modules: $slows"
|
||||
print
|
||||
|
||||
if (( fails > 0 )); then
|
||||
print "❗ One or more modules failed to load. Check 💥 entries."
|
||||
elif (( slows > 0 )); then
|
||||
print "⚠️ Some modules exceeded ${SLOW_THRESHOLD}ms."
|
||||
else
|
||||
print "✅ All modules healthy and fast."
|
||||
fi
|
||||
|
||||
# ---- Timeline (optional; format times in shell, not jq) ----
|
||||
if (( SHOW_TIMELINE )); then
|
||||
print
|
||||
print "🕰️ Load Timeline (start→end, μs precision)"
|
||||
print "---------------------------------------------------------------"
|
||||
|
||||
jq -sr '.[] | select(.start != null and .end != null)
|
||||
| [.label, (.start|tostring), (.end|tostring), (.duration_ms // 0), (.exit_code // 0), .file]
|
||||
| @tsv' "$LOGFILE" |
|
||||
while IFS=$'\t' read -r label start end duration exit_code file; do
|
||||
# hh:mm:ss.fffff formatting in shell
|
||||
s_sec="${start%.*}"; s_frac="${start#*.}"; s_frac="${s_frac%%[^0-9]*}"; s_frac="${s_frac:0:5}"
|
||||
e_sec="${end%.*}" ; e_frac="${end#*.}" ; e_frac="${e_frac%%[^0-9]*}" ; e_frac="${e_frac:0:5}"
|
||||
s_hms="$(date -r "$s_sec" +%H:%M:%S 2>/dev/null).$(printf "%05d" ${s_frac:-0})"
|
||||
e_hms="$(date -r "$e_sec" +%H:%M:%S 2>/dev/null).$(printf "%05d" ${e_frac:-0})"
|
||||
printf "%-22s %s → %s %8.3fms (exit:%3d) %s\n" \
|
||||
"$label" "$s_hms" "$e_hms" "$duration" "$exit_code" "$file"
|
||||
done
|
||||
print "---------------------------------------------------------------"
|
||||
fi
|
||||
@@ -0,0 +1,14 @@
|
||||
#autoload
|
||||
|
||||
dotpush() {
|
||||
local host="$1"
|
||||
rsync -azc \
|
||||
--update \
|
||||
--backup --suffix='.bak' \
|
||||
--filter=':- .gitignore' \
|
||||
--exclude='.git/' \
|
||||
-e ssh \
|
||||
"$DOTFILES/$2" \
|
||||
"$host:~/.dotfiles/"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Usage: extract <file>
|
||||
# Description: extracts archived files / mounts disk images
|
||||
# Note: .dmg/hdiutil is macOS-specific.
|
||||
#
|
||||
# credit: http://nparikh.org/notes/zshrc.txt
|
||||
extract () {
|
||||
if [ -f $1 ]; then
|
||||
case $1 in
|
||||
*.tar.bz2) tar -jxvf $1 || echo $1 ;;
|
||||
*.tar.gz) tar -zxvf $1 ;;
|
||||
*.bz2) bunzip2 $1 ;;
|
||||
*.dmg) hdiutil mount $1 ;;
|
||||
*.gz) gunzip $1 ;;
|
||||
*.tar) tar -xvf $1 ;;
|
||||
*.tbz2) tar -jxvf $1 ;;
|
||||
*.tgz) tar -zxvf $1 ;;
|
||||
*.zip) unzip $1 ;;
|
||||
*.ZIP) unzip $1 ;;
|
||||
*.pax) cat $1 | pax -r ;;
|
||||
*.pax.Z) uncompress $1 --stdout | pax -r ;;
|
||||
*.rar) unrar x $1 ;;
|
||||
*.Z) uncompress $1 ;;
|
||||
*) echo "'$1' cannot be extracted/mounted via extract()" ;;
|
||||
esac
|
||||
else
|
||||
echo "'$1' is not a valid file"
|
||||
fi
|
||||
}
|
||||
@@ -0,0 +1,89 @@
|
||||
#autoload
|
||||
|
||||
find_dupes() {
|
||||
local maxdepth=3 regex='.*' ignore_case=false
|
||||
local tmpfile grep_flags="-E"
|
||||
|
||||
# ----------------------------
|
||||
# Parse args (all long opts must start with --)
|
||||
# ----------------------------
|
||||
while [[ $# -gt 0 ]]; do
|
||||
case "$1" in
|
||||
--maxdepth|-d)
|
||||
maxdepth="$2"
|
||||
shift 2
|
||||
;;
|
||||
--regex|-r)
|
||||
regex="$2"
|
||||
shift 2
|
||||
;;
|
||||
--ignore-case|-i)
|
||||
ignore_case=true
|
||||
shift
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
break
|
||||
;;
|
||||
*)
|
||||
echo "find_dupes: unknown option '$1'" >&2
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# grep flags
|
||||
$ignore_case && grep_flags="$grep_flags -i"
|
||||
|
||||
tmpfile=$(mktemp /tmp/find_dupes.XXXXXX)
|
||||
|
||||
# ----------------------------
|
||||
# Scan files
|
||||
# ----------------------------
|
||||
find . -type f -maxdepth "$maxdepth" ! -path '*/.git/*' \
|
||||
\( -name "*.zsh" -o -name "*.sh" -o -name "*.zlogin" -o -name "*.symlink" -o ! -name "*.*" \) \
|
||||
-print0 | while IFS= read -r -d '' file; do
|
||||
|
||||
grep $grep_flags "$regex" "$file" 2>/dev/null | \
|
||||
awk '
|
||||
{
|
||||
gsub(/\r/, "", $0)
|
||||
gsub(/\302\240/, "", $0)
|
||||
line = $0
|
||||
}
|
||||
|
||||
/^[[:space:]]*#/ { next }
|
||||
|
||||
{
|
||||
sub(/^[[:space:]]+/, "", line)
|
||||
sub(/[[:space:]]+$/, "", line)
|
||||
|
||||
if (length(line) == 0) next
|
||||
if (length(line) < 5) next
|
||||
|
||||
print line "\t" FILENAME
|
||||
}
|
||||
' FILENAME="$file" >> "$tmpfile"
|
||||
done
|
||||
|
||||
# ----------------------------
|
||||
# Show duplicates
|
||||
# ----------------------------
|
||||
sort "$tmpfile" | awk -F'\t' '
|
||||
{
|
||||
line=$1; file=$2
|
||||
count[line]++
|
||||
files[line]=files[line] file "\n"
|
||||
}
|
||||
END {
|
||||
for (l in count)
|
||||
if (count[l] > 1) {
|
||||
print "==== Duplicate line: \"" l "\" ===="
|
||||
printf "%s", files[l]
|
||||
print ""
|
||||
}
|
||||
}
|
||||
'
|
||||
|
||||
rm -f "$tmpfile"
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/zsh
|
||||
function echo_exists() {
|
||||
if [[ -e "${1:r}.py" ]]; then
|
||||
echo "${1:r}.py"
|
||||
# cat "${1:r}.py"
|
||||
elif [[ -e "${1:h}" ]]; then
|
||||
echo "${1:h}"
|
||||
else
|
||||
echo "${1} - no"
|
||||
fi
|
||||
}
|
||||
|
||||
typeset -a SEARCH_DIR=("${(@f)"$(<$DOTFILES/bin/jupconf.txt)"}")
|
||||
for _dir in "${SEARCH_DIR[@]}"; do
|
||||
echo_exists $_dir
|
||||
done
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,58 @@
|
||||
#!/usr/local/bin/zsh
|
||||
|
||||
function prefix() { PYENV_VERSION=3.9.5 pyenv exec "$@"; }
|
||||
function suffix_grep() { prefix "$@" | grep ${JUP_PORT:-9888}; }
|
||||
function suffix_null() { $@ >/dev/null 2>&1; }
|
||||
|
||||
function jupyter_start() {
|
||||
prefix nohup jupyter lab --no-browser --ServerApp.disable_check_xsrf=True --port ${JUP_PORT:-9888} -y >$HOME/jup.log 2>&1 &
|
||||
export JUP_PID=$!
|
||||
}
|
||||
|
||||
function jupyter_kill() {
|
||||
# prefix jupyter server stop ${JUP_PORT:-9888} >>$HOME/jup.log 2>&1
|
||||
suffix_null jupyter server stop ${JUP_PORT:-9888}
|
||||
unset JUP_PID
|
||||
}
|
||||
|
||||
function jupyter_help() {
|
||||
printf "Usage: jupyter_run [-h, --help] [--stop] [-s, --show] [-r, --restart]\n"
|
||||
}
|
||||
|
||||
function jupyter_run() {
|
||||
typeset -i num_arg
|
||||
num_arg=$#
|
||||
if [[ $num_arg == '0' ]]; then
|
||||
suffix_null suffix_grep jupyter server list || jupyter_start
|
||||
return 0 2>/dev/null
|
||||
fi
|
||||
while [[ "$1" =~ ^- && ! "$1" == "--" ]]; do
|
||||
case $1 in
|
||||
-r | --restart)
|
||||
jupyter_kill
|
||||
jupyter_start
|
||||
return 0 2>/dev/null
|
||||
;;
|
||||
--stop)
|
||||
jupyter_kill
|
||||
return 0 2>/dev/null
|
||||
;;
|
||||
-s | --show)
|
||||
suffix_grep jupyter server list
|
||||
return 0 2>/dev/null
|
||||
;;
|
||||
-h | --help)
|
||||
jupyter_help
|
||||
return 0 2>/dev/null
|
||||
;;
|
||||
*)
|
||||
jupyter_help
|
||||
return 1 2>/dev/null
|
||||
;;
|
||||
esac
|
||||
shift
|
||||
done
|
||||
if [[ "$1" == '--' ]]; then shift; fi
|
||||
}
|
||||
|
||||
jupyter_run
|
||||
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env zsh
|
||||
set -euo pipefail
|
||||
|
||||
SESS_DIR="$HOME/.config/kitty/sessions"
|
||||
|
||||
launch() {
|
||||
nohup kitty "$@" >/dev/null 2>&1 &
|
||||
disown
|
||||
}
|
||||
|
||||
if [[ -n "${1:-$DCS_PROJ_NAME}" ]]; then
|
||||
launch --session "${SESS_DIR}/${1:-$DCS_PROJ_NAME}.session"
|
||||
fi
|
||||
|
||||
cwd="$(pwd -P)"
|
||||
#
|
||||
#if [[ "$cwd" == "$HOME/dev/dcsctl"* ]]; then
|
||||
# launch --session "${SESS_DIR}/dcsctl.session"
|
||||
#elif [[ "$cwd" == "$HOME/PAN_Macbook/cycling-climbs"* ]]; then
|
||||
# launch --session "${SESS_DIR}/cycling.session"
|
||||
#else
|
||||
# launch --directory "$cwd"
|
||||
#fi
|
||||
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
#!/usr/bin/env zsh
|
||||
# ===============================================================
|
||||
# log_parser.zsh — Pretty-print call_file JSON logs (aligned + exit codes)
|
||||
# ===============================================================
|
||||
|
||||
LOGFILE="${1:-$HOME/zshrc-log.json}"
|
||||
|
||||
if [[ ! -r "$LOGFILE" ]]; then
|
||||
echo "❌ Log file not found: $LOGFILE"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Parsed call_file log:"
|
||||
|
||||
# Arrays to hold parsed data
|
||||
labels=()
|
||||
durations=()
|
||||
exit_codes=()
|
||||
files=()
|
||||
|
||||
# Parse the JSON lines
|
||||
while IFS= read -r line; do
|
||||
label=$(echo "$line" | jq -r '.label // empty' 2>/dev/null)
|
||||
duration=$(echo "$line" | jq -r '.duration_ms // empty' 2>/dev/null)
|
||||
exit_code=$(echo "$line" | jq -r '.exit_code // empty' 2>/dev/null)
|
||||
file=$(echo "$line" | jq -r '.file // empty' 2>/dev/null)
|
||||
|
||||
[[ -z "$label" || -z "$file" ]] && continue
|
||||
|
||||
labels+=("$label")
|
||||
durations+=("${duration:-0}")
|
||||
exit_codes+=("${exit_code:-0}")
|
||||
files+=("$file")
|
||||
done < <(grep -E '"label"' "$LOGFILE")
|
||||
|
||||
# Determine max column widths for alignment
|
||||
max_label=0
|
||||
max_duration=0
|
||||
max_exit=0
|
||||
for label in "${labels[@]}"; do
|
||||
(( ${#label} > max_label )) && max_label=${#label}
|
||||
done
|
||||
for dur in "${durations[@]}"; do
|
||||
(( ${#dur} > max_duration )) && max_duration=${#dur}
|
||||
done
|
||||
for code in "${exit_codes[@]}"; do
|
||||
(( ${#code} > max_exit )) && max_exit=${#code}
|
||||
done
|
||||
|
||||
# Build sorted, aligned output
|
||||
{
|
||||
for i in {1..${#labels[@]}}; do
|
||||
printf "%-${max_label}s %${max_duration}sms %${max_exit}s %s\n" \
|
||||
"${labels[i-1]}" "${durations[i-1]}" "${exit_codes[i-1]}" "${files[i-1]}"
|
||||
done
|
||||
} | sort -k2,2n
|
||||
@@ -0,0 +1,9 @@
|
||||
function make_reqs() {
|
||||
mods=("${(@f)$(awk '/import/ { print $2 }' *.(py|ipynb) | sed -E 's:[\\\.\"].*::g;s~_~-~g' | sort --unique )}")
|
||||
pip freeze | grep -E "$(export IFS="|"; echo "${mods[*]}")"
|
||||
for mod in ${mods[*]}; do
|
||||
echo $mod
|
||||
echo `pip freeze | grep $mod`
|
||||
done
|
||||
unset mods
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
#!/bin/sh
|
||||
if [[ -w $HOME/PAN ]]; then
|
||||
hdiutil attach -mountpoint $HOME/PAN $HOME/Library/Mobile\ Documents/com\~apple\~CloudDocs/PAN.sparsebundle
|
||||
fi
|
||||
@@ -0,0 +1,19 @@
|
||||
#autoload
|
||||
|
||||
move_bak() {
|
||||
emulate -L zsh
|
||||
[[ -n $DEBUG_ZSH ]] && {
|
||||
export PS4='+%N:%i> '
|
||||
set -x
|
||||
}
|
||||
local _files
|
||||
typeset -a bakfiles=($HOME/.dotfiles/**/*.bak(DN))
|
||||
[[ ${#bakfiles} -gt 0 ]] || return 0
|
||||
for _files in "${(@u)bakfiles:h}"; do
|
||||
[[ -d ${_files//dotfiles/dotfiles-bak} ]] || eval "mkdir -p ${_files//dotfiles/dotfiles-bak}"
|
||||
done
|
||||
for _files in "${(@)bakfiles}"; do
|
||||
eval "mv ${(A)_files} ${_files//dotfiles/dotfiles-bak}"
|
||||
done
|
||||
unset bakfiles
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
#/bin/zsh
|
||||
mvo () {
|
||||
for file in $@
|
||||
do
|
||||
if [[ ${file##*.} == "zsh" ]]; then
|
||||
mv "$file" "${file%%.*}.old"
|
||||
elif [[ ${1##*.} == "old" ]]; then
|
||||
mv "$file" "${file%%.*}.zsh"
|
||||
fi
|
||||
done
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#autoload
|
||||
neo_open() {
|
||||
if [[ -z $SSH_CONNECTION ]]; then
|
||||
local _nport=127.0.0.1:7777
|
||||
local _nfile="${1:A}"
|
||||
else
|
||||
local _nport=127.0.0.1:7778
|
||||
local _nfile="scp://$(hostname)//${1:A}"
|
||||
fi
|
||||
cmd_args=( "nvim" "--server" "${_nport}" "--remote" "${_nfile}")
|
||||
eval $cmd_args[@]
|
||||
if kitty @ ls -m "env:PANEL" >/dev/null 2>&1; then
|
||||
kitten @ --to ${KITTY_LISTEN_ON} resize-os-window --match env:PANEL --action=toggle-visibility
|
||||
fi
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
#!/usr/bin/env zsh
|
||||
# nvim-open — open a file (with optional line) in the session's nvim server
|
||||
#
|
||||
# Usage:
|
||||
# nvim-open <path> [line]
|
||||
#
|
||||
# The nvim server is identified by $NVIM_SERVER, which is set per-session:
|
||||
# - Local: 127.0.0.1:7777 (default in nvim/env.zsh)
|
||||
# - nasng: 127.0.0.1:27777 (injected by ssh.conf)
|
||||
# - mining: 127.0.0.1:17777
|
||||
# - mininv: 127.0.0.1:37777
|
||||
#
|
||||
# Called from kitty hints:
|
||||
# map ctrl+shift+p>n kitten hints --type=linenum --linenum-action=overlay \
|
||||
# zsh -lic 'nvim-open {path} {line}'
|
||||
#
|
||||
# Called from nvim_panel.py for hyperlink-click-to-panel flow.
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
NVIM_BIN="${NVIM_BIN:-/opt/homebrew/bin/nvim}"
|
||||
SERVER="${NVIM_SERVER:-127.0.0.1:7777}"
|
||||
FILE="${1:?nvim-open: file argument required}"
|
||||
LINE="${2:-}"
|
||||
|
||||
# Open file in a new tab in the running nvim instance
|
||||
"$NVIM_BIN" --server "$SERVER" --remote-tab "$(realpath "$FILE")"
|
||||
|
||||
# Jump to line if provided
|
||||
if [[ -n "$LINE" && "$LINE" =~ ^[0-9]+$ ]]; then
|
||||
"$NVIM_BIN" --server "$SERVER" --remote-send "<C-\\><C-n>:${LINE}<CR>"
|
||||
fi
|
||||
@@ -0,0 +1,6 @@
|
||||
#autoload
|
||||
o_panel(){
|
||||
local _fi
|
||||
_fi=$(realpath "$1")
|
||||
$HOME/.config/kitty/nvim_panel "${_fi}"
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
#autoload
|
||||
o_vim() { nvim --server 127.0.0.1:7777 --remote "${1:A}"; }
|
||||
@@ -0,0 +1,5 @@
|
||||
#!/usr/bin/zsh
|
||||
# parent_find () {
|
||||
# # [[ -a ~/.localrc ]] && source ~/.localrc
|
||||
# topic=$(basename `${READLINK_DIR:="/bin"}/readlink -f ${1%/*}`)
|
||||
# }
|
||||
@@ -0,0 +1,20 @@
|
||||
#!/usr/bin/zsh
|
||||
# while read -d ':' p; do
|
||||
# [[ -d $p ]] || NEW_PATH=$NEW_PATH"$p"
|
||||
# done <<< "$PATH:"
|
||||
# [[ $VIRTUAL_ENV != "*virtualenvs*" ]] &&
|
||||
|
||||
path_fix() {
|
||||
paths=($(tr ':' '\n' <<<"$PATH"))
|
||||
for p in "${paths[@]}"; do
|
||||
if [[ $VIRTUAL_ENV ]]; then
|
||||
continue
|
||||
fi
|
||||
[[ -d $p ]] && NEW_PATH="${NEW_PATH}${DELIM}${p}"
|
||||
DELIM=":"
|
||||
done
|
||||
# export OLDPATH=$PATH
|
||||
# export PATH=$NEW_PATH
|
||||
tr ':' '\n' <<<"$NEW_PATH"
|
||||
unset NEW_PATH
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
#!/usr/bin/zsh
|
||||
# while read -d ':' p; do
|
||||
# [[ -d $p ]] || NEW_PATH=$NEW_PATH"$p"
|
||||
# done <<< "$PATH:"
|
||||
# [[ $VIRTUAL_ENV != "*virtualenvs*" ]] &&
|
||||
|
||||
path_fix () {
|
||||
paths=(`tr ':' '\n' <<< "$PATH"`)
|
||||
for p in "${paths[@]}" ; do
|
||||
if [[ $VIRTUAL_ENV =~ "virtualenvs"]] && [[ $p =~ "pyenv" ]]; then
|
||||
echo $p
|
||||
[[ -d $p ]] && NEW_PATH="${NEW_PATH}${DELIM}${p}"
|
||||
fi
|
||||
[[ -d $p ]] && NEW_PATH="${NEW_PATH}${DELIM}${p}"
|
||||
DELIM=":"
|
||||
done
|
||||
export OLDPATH=$PATH
|
||||
export PATH=$NEW_PATH
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#autoload
|
||||
pdir() {
|
||||
local proj
|
||||
proj="${PROJECT:-$@}"
|
||||
[[ -z "$proj" ]] && { print -u2 "usage: pdir <project>"; return 2; }
|
||||
cd -- "$(dcsctl dir ${(As./.)proj})" || return
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
function ppgrep() { pgrep "$@" | xargs ps -fp 2> /dev/null; }
|
||||
@@ -0,0 +1,9 @@
|
||||
#/bin/zsh
|
||||
for file in $@
|
||||
do
|
||||
if [[ ${file##*.} == "sh" ]]; then
|
||||
mv "$file" "${file%%.*}"
|
||||
elif [[ ${1##*.} == "old" ]]; then
|
||||
mv "$file" "${file%%.*}.zsh"
|
||||
fi
|
||||
done
|
||||
@@ -0,0 +1,18 @@
|
||||
#!/usr/local/bin/zsh
|
||||
setopt extended_glob
|
||||
setopt null_glob
|
||||
setopt rematch_pcre
|
||||
|
||||
echo $1
|
||||
echo $2
|
||||
echo $3
|
||||
|
||||
if [[ -d $1 ]]; then
|
||||
echo ${1}"/**/*"${2}
|
||||
search_files=($1/**/*$2)
|
||||
fi
|
||||
# load the path files
|
||||
for file in $search_files
|
||||
do
|
||||
cat ${file} | grep $3
|
||||
done
|
||||
@@ -0,0 +1,5 @@
|
||||
#!/bin/sh
|
||||
#
|
||||
# Sets macOS defaults by running $ZSH/macos/set-defaults.sh.
|
||||
|
||||
exec $ZSH/macos/set-defaults.sh
|
||||
@@ -0,0 +1,9 @@
|
||||
#compdef biggest
|
||||
|
||||
# Completion for:
|
||||
# biggest [-n COUNT|--number COUNT] [-p PATTERN|--pattern PATTERN]
|
||||
|
||||
_arguments -s \
|
||||
'(-h --help)'{-h,--help}'[show help information]' \
|
||||
'(-n --number)'{-n,--number}'[number of entries to display]:number of entries:(10 20 50 100 200)' \
|
||||
'(-p --pattern)'{-p,--pattern}'[glob-style pattern to match]:glob pattern:_files' && return 0
|
||||
@@ -0,0 +1,212 @@
|
||||
#compdef colima
|
||||
compdef _colima colima
|
||||
|
||||
# zsh completion for colima -*- shell-script -*-
|
||||
|
||||
__colima_debug()
|
||||
{
|
||||
local file="$BASH_COMP_DEBUG_FILE"
|
||||
if [[ -n ${file} ]]; then
|
||||
echo "$*" >> "${file}"
|
||||
fi
|
||||
}
|
||||
|
||||
_colima()
|
||||
{
|
||||
local shellCompDirectiveError=1
|
||||
local shellCompDirectiveNoSpace=2
|
||||
local shellCompDirectiveNoFileComp=4
|
||||
local shellCompDirectiveFilterFileExt=8
|
||||
local shellCompDirectiveFilterDirs=16
|
||||
local shellCompDirectiveKeepOrder=32
|
||||
|
||||
local lastParam lastChar flagPrefix requestComp out directive comp lastComp noSpace keepOrder
|
||||
local -a completions
|
||||
|
||||
__colima_debug "\n========= starting completion logic =========="
|
||||
__colima_debug "CURRENT: ${CURRENT}, words[*]: ${words[*]}"
|
||||
|
||||
# The user could have moved the cursor backwards on the command-line.
|
||||
# We need to trigger completion from the $CURRENT location, so we need
|
||||
# to truncate the command-line ($words) up to the $CURRENT location.
|
||||
# (We cannot use $CURSOR as its value does not work when a command is an alias.)
|
||||
words=("${=words[1,CURRENT]}")
|
||||
__colima_debug "Truncated words[*]: ${words[*]},"
|
||||
|
||||
lastParam=${words[-1]}
|
||||
lastChar=${lastParam[-1]}
|
||||
__colima_debug "lastParam: ${lastParam}, lastChar: ${lastChar}"
|
||||
|
||||
# For zsh, when completing a flag with an = (e.g., colima -n=<TAB>)
|
||||
# completions must be prefixed with the flag
|
||||
setopt local_options BASH_REMATCH
|
||||
if [[ "${lastParam}" =~ '-.*=' ]]; then
|
||||
# We are dealing with a flag with an =
|
||||
flagPrefix="-P ${BASH_REMATCH}"
|
||||
fi
|
||||
|
||||
# Prepare the command to obtain completions
|
||||
requestComp="${words[1]} __complete ${words[2,-1]}"
|
||||
if [ "${lastChar}" = "" ]; then
|
||||
# If the last parameter is complete (there is a space following it)
|
||||
# We add an extra empty parameter so we can indicate this to the go completion code.
|
||||
__colima_debug "Adding extra empty parameter"
|
||||
requestComp="${requestComp} \"\""
|
||||
fi
|
||||
|
||||
__colima_debug "About to call: eval ${requestComp}"
|
||||
|
||||
# Use eval to handle any environment variables and such
|
||||
out=$(eval ${requestComp} 2>/dev/null)
|
||||
__colima_debug "completion output: ${out}"
|
||||
|
||||
# Extract the directive integer following a : from the last line
|
||||
local lastLine
|
||||
while IFS='\n' read -r line; do
|
||||
lastLine=${line}
|
||||
done < <(printf "%s\n" "${out[@]}")
|
||||
__colima_debug "last line: ${lastLine}"
|
||||
|
||||
if [ "${lastLine[1]}" = : ]; then
|
||||
directive=${lastLine[2,-1]}
|
||||
# Remove the directive including the : and the newline
|
||||
local suffix
|
||||
(( suffix=${#lastLine}+2))
|
||||
out=${out[1,-$suffix]}
|
||||
else
|
||||
# There is no directive specified. Leave $out as is.
|
||||
__colima_debug "No directive found. Setting do default"
|
||||
directive=0
|
||||
fi
|
||||
|
||||
__colima_debug "directive: ${directive}"
|
||||
__colima_debug "completions: ${out}"
|
||||
__colima_debug "flagPrefix: ${flagPrefix}"
|
||||
|
||||
if [ $((directive & shellCompDirectiveError)) -ne 0 ]; then
|
||||
__colima_debug "Completion received error. Ignoring completions."
|
||||
return
|
||||
fi
|
||||
|
||||
local activeHelpMarker="_activeHelp_ "
|
||||
local endIndex=${#activeHelpMarker}
|
||||
local startIndex=$((${#activeHelpMarker}+1))
|
||||
local hasActiveHelp=0
|
||||
while IFS='\n' read -r comp; do
|
||||
# Check if this is an activeHelp statement (i.e., prefixed with $activeHelpMarker)
|
||||
if [ "${comp[1,$endIndex]}" = "$activeHelpMarker" ];then
|
||||
__colima_debug "ActiveHelp found: $comp"
|
||||
comp="${comp[$startIndex,-1]}"
|
||||
if [ -n "$comp" ]; then
|
||||
compadd -x "${comp}"
|
||||
__colima_debug "ActiveHelp will need delimiter"
|
||||
hasActiveHelp=1
|
||||
fi
|
||||
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ -n "$comp" ]; then
|
||||
# If requested, completions are returned with a description.
|
||||
# The description is preceded by a TAB character.
|
||||
# For zsh's _describe, we need to use a : instead of a TAB.
|
||||
# We first need to escape any : as part of the completion itself.
|
||||
comp=${comp//:/\\:}
|
||||
|
||||
local tab="$(printf '\t')"
|
||||
comp=${comp//$tab/:}
|
||||
|
||||
__colima_debug "Adding completion: ${comp}"
|
||||
completions+=${comp}
|
||||
lastComp=$comp
|
||||
fi
|
||||
done < <(printf "%s\n" "${out[@]}")
|
||||
|
||||
# Add a delimiter after the activeHelp statements, but only if:
|
||||
# - there are completions following the activeHelp statements, or
|
||||
# - file completion will be performed (so there will be choices after the activeHelp)
|
||||
if [ $hasActiveHelp -eq 1 ]; then
|
||||
if [ ${#completions} -ne 0 ] || [ $((directive & shellCompDirectiveNoFileComp)) -eq 0 ]; then
|
||||
__colima_debug "Adding activeHelp delimiter"
|
||||
compadd -x "--"
|
||||
hasActiveHelp=0
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $((directive & shellCompDirectiveNoSpace)) -ne 0 ]; then
|
||||
__colima_debug "Activating nospace."
|
||||
noSpace="-S ''"
|
||||
fi
|
||||
|
||||
if [ $((directive & shellCompDirectiveKeepOrder)) -ne 0 ]; then
|
||||
__colima_debug "Activating keep order."
|
||||
keepOrder="-V"
|
||||
fi
|
||||
|
||||
if [ $((directive & shellCompDirectiveFilterFileExt)) -ne 0 ]; then
|
||||
# File extension filtering
|
||||
local filteringCmd
|
||||
filteringCmd='_files'
|
||||
for filter in ${completions[@]}; do
|
||||
if [ ${filter[1]} != '*' ]; then
|
||||
# zsh requires a glob pattern to do file filtering
|
||||
filter="\*.$filter"
|
||||
fi
|
||||
filteringCmd+=" -g $filter"
|
||||
done
|
||||
filteringCmd+=" ${flagPrefix}"
|
||||
|
||||
__colima_debug "File filtering command: $filteringCmd"
|
||||
_arguments '*:filename:'"$filteringCmd"
|
||||
elif [ $((directive & shellCompDirectiveFilterDirs)) -ne 0 ]; then
|
||||
# File completion for directories only
|
||||
local subdir
|
||||
subdir="${completions[1]}"
|
||||
if [ -n "$subdir" ]; then
|
||||
__colima_debug "Listing directories in $subdir"
|
||||
pushd "${subdir}" >/dev/null 2>&1
|
||||
else
|
||||
__colima_debug "Listing directories in ."
|
||||
fi
|
||||
|
||||
local result
|
||||
_arguments '*:dirname:_files -/'" ${flagPrefix}"
|
||||
result=$?
|
||||
if [ -n "$subdir" ]; then
|
||||
popd >/dev/null 2>&1
|
||||
fi
|
||||
return $result
|
||||
else
|
||||
__colima_debug "Calling _describe"
|
||||
if eval _describe $keepOrder "completions" completions $flagPrefix $noSpace; then
|
||||
__colima_debug "_describe found some completions"
|
||||
|
||||
# Return the success of having called _describe
|
||||
return 0
|
||||
else
|
||||
__colima_debug "_describe did not find completions."
|
||||
__colima_debug "Checking if we should do file completion."
|
||||
if [ $((directive & shellCompDirectiveNoFileComp)) -ne 0 ]; then
|
||||
__colima_debug "deactivating file completion"
|
||||
|
||||
# We must return an error code here to let zsh know that there were no
|
||||
# completions found by _describe; this is what will trigger other
|
||||
# matching algorithms to attempt to find completions.
|
||||
# For example zsh can match letters in the middle of words.
|
||||
return 1
|
||||
else
|
||||
# Perform file completion
|
||||
__colima_debug "Activating file completion"
|
||||
|
||||
# We must return the result of this command, so it must be the
|
||||
# last command, or else we must store its result to return it.
|
||||
_arguments '*:filename:_files'" ${flagPrefix}"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# don't run the completion function when being source-ed or eval-ed
|
||||
if [ "$funcstack[1]" = "_colima" ]; then
|
||||
_colima
|
||||
fi
|
||||
@@ -0,0 +1,15 @@
|
||||
#compdef compinit_doctor
|
||||
|
||||
local -a args
|
||||
args=(
|
||||
'(-h --help)'{-h,--help}'[Show help/usage]'
|
||||
'(-s --status)'{-s,--status}'[Show current compinit/dump status (default)]'
|
||||
'(-a --audit)'{-a,--audit}'[Run compaudit for insecure paths]'
|
||||
'(-r --rebuild)'{-r,--rebuild}'[Force compinit -i -d and background zcompile]'
|
||||
'(-S --snap)'{-S,--snap}'[Snapshot current $fpath to file]:snapshot file:_files'
|
||||
'(-d --diff)'{-d,--diff}'[Diff current $fpath against a snapshot file]:snapshot file:_files'
|
||||
'(-D --dump)'{-D,--dump}'[Override zcompdump path]:dump file:_files -g "*zcompdump(-.)"'
|
||||
'(-j --json)'{-j,--json}'[Emit status as single JSON object]'
|
||||
)
|
||||
|
||||
_arguments -s -S $args '*:file:_files'
|
||||
@@ -0,0 +1,33 @@
|
||||
#compdef confe
|
||||
|
||||
# Completion for the confe function
|
||||
|
||||
local -a topics opts
|
||||
|
||||
topics=(
|
||||
alias
|
||||
keybindings
|
||||
options
|
||||
path
|
||||
packages
|
||||
ssh
|
||||
brewfile
|
||||
)
|
||||
|
||||
opts=(
|
||||
'--backup[Backup the file before editing]'
|
||||
'-b[Backup the file before editing]'
|
||||
'--staged[Stage the file into its git repo after editing]'
|
||||
'-s[Stage the file into its git repo after editing]'
|
||||
)
|
||||
|
||||
_arguments \
|
||||
$opts \
|
||||
'1:topic:->topic' \
|
||||
'*:filename:_files' && return 0
|
||||
|
||||
case $state in
|
||||
topic)
|
||||
_describe 'topic' topics
|
||||
;;
|
||||
esac
|
||||
@@ -0,0 +1,89 @@
|
||||
#compdef copy_files
|
||||
|
||||
_copy_files() {
|
||||
local -a dirs
|
||||
local -a cp_hosts_default
|
||||
local -a used_hosts avail_hosts
|
||||
local _dotfiles="${DOTFILES:-$HOME/.dotfiles}"
|
||||
local i host_mode=0 host_arg_index=0
|
||||
|
||||
cp_hosts_default=( $(_copy_files_ssh_hosts) )
|
||||
|
||||
[[ -d "$_dotfiles" ]] || return 1
|
||||
dirs=(${(f)"$(print -rl -- $_dotfiles/*(/N:t))"})
|
||||
|
||||
for (( i = 2; i < CURRENT; i++ )); do
|
||||
case "${words[i]}" in
|
||||
-h|--hosts)
|
||||
host_mode=1
|
||||
host_arg_index=$i
|
||||
;;
|
||||
--)
|
||||
(( host_mode )) && host_mode=0
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
if (( host_mode )); then
|
||||
used_hosts=()
|
||||
|
||||
for (( i = host_arg_index + 1; i < CURRENT; i++ )); do
|
||||
[[ "${words[i]}" == "--" ]] && break
|
||||
used_hosts+=("${words[i]}")
|
||||
done
|
||||
|
||||
avail_hosts=( "${(@)cp_hosts_default:|used_hosts}" )
|
||||
compadd -S '' -- "${avail_hosts[@]}" --
|
||||
return
|
||||
fi
|
||||
|
||||
_describe -t dotfiles 'dotfiles subdirectory' dirs
|
||||
compadd -S '' -- -h --hosts
|
||||
}
|
||||
|
||||
_copy_files_ssh_hosts() {
|
||||
local cfg="${HOME}/.ssh/config"
|
||||
[[ -r "$cfg" ]] || return 0
|
||||
|
||||
awk '
|
||||
BEGIN {
|
||||
in_block = 0
|
||||
has_hostname = 0
|
||||
aliases = ""
|
||||
}
|
||||
|
||||
/^[[:space:]]*#/ { next }
|
||||
/^[[:space:]]*$/ { next }
|
||||
|
||||
tolower($1) == "host" {
|
||||
if (in_block && has_hostname && aliases != "") {
|
||||
print aliases
|
||||
}
|
||||
|
||||
in_block = 1
|
||||
has_hostname = 0
|
||||
aliases = ""
|
||||
|
||||
for (i = 2; i <= NF; i++) {
|
||||
if ($i ~ /[*?\[]/) {
|
||||
continue
|
||||
}
|
||||
aliases = aliases $i "\n"
|
||||
}
|
||||
next
|
||||
}
|
||||
|
||||
in_block && tolower($1) == "hostname" {
|
||||
has_hostname = 1
|
||||
next
|
||||
}
|
||||
|
||||
END {
|
||||
if (in_block && has_hostname && aliases != "") {
|
||||
print aliases
|
||||
}
|
||||
}
|
||||
' "$cfg" | awk '!seen[$0]++'
|
||||
}
|
||||
|
||||
_copy_files "$@"
|
||||
@@ -0,0 +1,11 @@
|
||||
#compdef dcs
|
||||
|
||||
# dcs is a shim to dcsctl, so reuse the same completion.
|
||||
# This ensures `dcs …` and `dcsctl …` behave identically.
|
||||
|
||||
_dcs() {
|
||||
autoload -Uz _dcsctl
|
||||
_dcsctl "$@"
|
||||
}
|
||||
|
||||
_dcs "$@"
|
||||
@@ -0,0 +1,445 @@
|
||||
#compdef dcsctl
|
||||
|
||||
# --- helpers ---------------------------------------------------------------
|
||||
|
||||
_dcsctl__docker_contexts() {
|
||||
local -a ctxs
|
||||
ctxs=(${(f)"$(command docker context ls --format '{{.Name}}' 2>/dev/null)"})
|
||||
_describe -t docker-contexts 'docker contexts' ctxs
|
||||
}
|
||||
|
||||
_dcsctl__current_context() {
|
||||
command docker context show 2>/dev/null
|
||||
}
|
||||
|
||||
_dcsctl__control_plane_base() {
|
||||
# Match dcsctl logic: base is $DCS_ROOT if set, else ~/.dcs
|
||||
print -r -- "${DCS_ROOT:-$HOME/.dcs}"
|
||||
}
|
||||
|
||||
_dcsctl__projects() {
|
||||
local ctx base
|
||||
ctx="$(_dcsctl__current_context)"
|
||||
[[ -z "$ctx" ]] && return 0
|
||||
|
||||
base="$(_dcsctl__control_plane_base)/$ctx/compose"
|
||||
[[ -d "$base" ]] || return 0
|
||||
|
||||
local -a ignored projects
|
||||
ignored=(shared '@Recycle' '@Recently-Snapshot' '.DS_Store')
|
||||
|
||||
projects=()
|
||||
local dir name
|
||||
for dir in "$base"/*; do
|
||||
[[ -d "$dir" ]] || continue
|
||||
name="${dir:t}"
|
||||
(( ${ignored[(I)$name]} )) && continue
|
||||
projects+=("$name")
|
||||
done
|
||||
|
||||
_describe -t dcs-projects 'projects' projects
|
||||
}
|
||||
|
||||
_dcsctl__secret_names_from_dir() {
|
||||
local ctx proj base projdir secretdir
|
||||
ctx="$(_dcsctl__current_context)"
|
||||
[[ -z "$ctx" ]] && return 1
|
||||
|
||||
proj="${words[4]}" # dcsctl secret add <project> <name>
|
||||
[[ -z "$proj" ]] && return 1
|
||||
|
||||
base="$(_dcsctl__control_plane_base)/$ctx/compose"
|
||||
projdir="$base/$proj"
|
||||
secretdir="$projdir/secrets"
|
||||
[[ -d "$secretdir" ]] || return 1
|
||||
|
||||
local -a names
|
||||
names=()
|
||||
local f n
|
||||
for f in "$secretdir"/*.txt; do
|
||||
[[ -f "$f" ]] || continue
|
||||
n="${f:t}"
|
||||
n="${n%.txt}"
|
||||
names+=("$n")
|
||||
done
|
||||
|
||||
(( ${#names} == 0 )) && return 1
|
||||
_describe -t dcs-secrets 'secret names' names
|
||||
}
|
||||
|
||||
# List service subdirectories in a swarm-mode project.
|
||||
# Takes one argument: the project name.
|
||||
_dcsctl__swarm_services() {
|
||||
local ctx proj base projdir svcdir
|
||||
ctx="$(_dcsctl__current_context)"
|
||||
[[ -z "$ctx" ]] && return 1
|
||||
|
||||
proj="$1"
|
||||
[[ -z "$proj" ]] && return 1
|
||||
|
||||
base="$(_dcsctl__control_plane_base)/$ctx/compose"
|
||||
projdir="$base/$proj"
|
||||
svcdir="$projdir/services"
|
||||
[[ -d "$svcdir" ]] || return 1
|
||||
|
||||
local -a svcs
|
||||
svcs=()
|
||||
local dir name
|
||||
for dir in "$svcdir"/*/; do
|
||||
[[ -d "$dir" ]] || continue
|
||||
name="${dir:t}"
|
||||
svcs+=("$name")
|
||||
done
|
||||
|
||||
(( ${#svcs} == 0 )) && return 1
|
||||
_describe -t dcs-swarm-services 'services' svcs
|
||||
}
|
||||
|
||||
# Detect whether a project is in swarm mode (has services/ dir).
|
||||
_dcsctl__is_swarm() {
|
||||
local ctx proj base projdir
|
||||
ctx="$(_dcsctl__current_context)"
|
||||
[[ -z "$ctx" ]] && return 1
|
||||
|
||||
proj="$1"
|
||||
[[ -z "$proj" ]] && return 1
|
||||
|
||||
base="$(_dcsctl__control_plane_base)/$ctx/compose"
|
||||
projdir="$base/$proj"
|
||||
[[ -d "$projdir/services" ]]
|
||||
}
|
||||
|
||||
# Fetch services for a given project by asking docker compose for the resolved config.
|
||||
_dcsctl__services() {
|
||||
local ctx proj base projdir
|
||||
ctx="$(_dcsctl__current_context)"
|
||||
[[ -z "$ctx" ]] && return 1
|
||||
|
||||
proj="${words[3]}" # dcsctl run <project> ...
|
||||
[[ -z "$proj" ]] && return 1
|
||||
|
||||
base="$(_dcsctl__control_plane_base)/$ctx/compose"
|
||||
projdir="$base/$proj"
|
||||
[[ -d "$projdir" ]] || return 1
|
||||
|
||||
local env1 env2 env3
|
||||
env1="$projdir/.env"
|
||||
env2="$projdir/service.env"
|
||||
env3="$projdir/service.secrets.env"
|
||||
|
||||
[[ -f "$env1" && -f "$env2" && -f "$env3" && -f "$projdir/docker-compose.yml" ]] || return 1
|
||||
|
||||
local -a svcs
|
||||
svcs=(${(f)"$(
|
||||
command docker --context "$ctx" compose \
|
||||
--project-directory "$projdir" \
|
||||
--project-name "$proj" \
|
||||
--env-file "$env1" \
|
||||
--env-file "$env2" \
|
||||
--env-file "$env3" \
|
||||
config --services 2>/dev/null
|
||||
)"})
|
||||
|
||||
(( ${#svcs} == 0 )) && return 1
|
||||
_describe -t dcs-services 'services' svcs
|
||||
}
|
||||
|
||||
# Heuristic: decide if we are in a position where a service name is expected.
|
||||
_dcsctl__maybe_complete_service() {
|
||||
local subcmd cur
|
||||
subcmd="${words[4]}" # dcsctl run <project> <subcmd> ...
|
||||
cur="${words[CURRENT]}"
|
||||
|
||||
[[ "$cur" == -* ]] && return 1
|
||||
|
||||
case "$subcmd" in
|
||||
logs|ps|stop|start|restart|up|pull|build|rm|kill|pause|unpause|top|exec)
|
||||
_dcsctl__services
|
||||
return $?
|
||||
;;
|
||||
esac
|
||||
|
||||
return 1
|
||||
}
|
||||
|
||||
# Delegate completion to docker after we reconstruct words as docker compose ...
|
||||
_dcsctl__docker_compose_delegate() {
|
||||
local ctx proj base projdir
|
||||
ctx="$(_dcsctl__current_context)"
|
||||
[[ -z "$ctx" ]] && return 1
|
||||
|
||||
proj="${words[3]}"
|
||||
[[ -z "$proj" ]] && return 1
|
||||
|
||||
base="$(_dcsctl__control_plane_base)/$ctx/compose"
|
||||
projdir="$base/$proj"
|
||||
|
||||
local -a dc_words
|
||||
dc_words=(docker --context "$ctx" compose --project-directory "$projdir" --project-name "$proj")
|
||||
|
||||
if (( ${#words} >= 4 )); then
|
||||
dc_words+=("${words[@]:3}") # from 4th element
|
||||
fi
|
||||
|
||||
local -a save_words
|
||||
local save_cword
|
||||
save_words=("${words[@]}")
|
||||
save_cword=$CURRENT
|
||||
|
||||
words=("${dc_words[@]}")
|
||||
CURRENT=${#words}
|
||||
|
||||
autoload -Uz _docker
|
||||
_docker
|
||||
|
||||
words=("${save_words[@]}")
|
||||
CURRENT=$save_cword
|
||||
}
|
||||
|
||||
# Helper for edit --service completion: extract the project arg from the command
|
||||
# line and complete swarm service names.
|
||||
_dcsctl__swarm_services_for_edit() {
|
||||
local proj=""
|
||||
local i
|
||||
for (( i=3; i <= ${#words}; i++ )); do
|
||||
local w="${words[$i]}"
|
||||
case "$w" in
|
||||
-e|--env|-r|--runtime|-s|--secret) ;;
|
||||
--service) (( i++ )) ;; # skip the next arg (the service value)
|
||||
-*) ;;
|
||||
*)
|
||||
if [[ -z "$proj" ]]; then
|
||||
proj="$w"
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
[[ -z "$proj" ]] && return 1
|
||||
_dcsctl__swarm_services "$proj"
|
||||
}
|
||||
|
||||
# --- main completion -------------------------------------------------------
|
||||
|
||||
_dcsctl() {
|
||||
local -a subcmds
|
||||
subcmds=(
|
||||
'new:Create a new project scaffold'
|
||||
'verify:Verify and enforce host permissions for appdata bind mounts'
|
||||
'run:Run docker compose for a project (compose mode)'
|
||||
'up:Alias for run <project> up -d (compose mode)'
|
||||
'down:Bring down a project (compose or swarm)'
|
||||
'stop:Scale all services to 0 replicas (swarm projects only)'
|
||||
'deploy:Merge service fragments and deploy as Docker Swarm stack'
|
||||
'ls:Alias for docker compose ls'
|
||||
'dir:Print the project directory, or a service directory in a swarm project'
|
||||
'edit:Edit project files in $EDITOR'
|
||||
'secret:Manage project secrets'
|
||||
'service:Manage services within a swarm project'
|
||||
'import:Import an existing docker-compose.yml into a DCS project'
|
||||
'context:Manage DCS control-plane contexts'
|
||||
'reload:Down, rebuild and up all containers within a DCS project'
|
||||
'rename:Rename and update a DCS project'
|
||||
'help:Show help for a command'
|
||||
)
|
||||
|
||||
if (( CURRENT == 2 )); then
|
||||
_describe -t commands 'dcsctl commands' subcmds
|
||||
return
|
||||
fi
|
||||
|
||||
local cmd
|
||||
cmd="${words[2]}"
|
||||
|
||||
case "$cmd" in
|
||||
new)
|
||||
_arguments -C \
|
||||
'(-v --verify)'{-v,--verify}'[Run verification after creating the project]' \
|
||||
'--verbose[Verbose output for verification (used with --verify)]' \
|
||||
'--compose[Create flat single-compose layout (legacy)]' \
|
||||
'--service[Initial service name for swarm layout]:service name:' \
|
||||
'1:project name:' \
|
||||
&& return
|
||||
;;
|
||||
|
||||
verify)
|
||||
_arguments -C \
|
||||
'--verbose[Show current vs expected ownership/mode while verifying (QNAP only)]' \
|
||||
'1:project name:_dcsctl__projects' \
|
||||
&& return
|
||||
;;
|
||||
|
||||
run)
|
||||
if (( CURRENT == 3 )); then
|
||||
_dcsctl__projects
|
||||
return
|
||||
fi
|
||||
|
||||
if (( CURRENT >= 5 )); then
|
||||
_dcsctl__maybe_complete_service && return
|
||||
fi
|
||||
|
||||
_dcsctl__docker_compose_delegate
|
||||
return
|
||||
;;
|
||||
|
||||
up)
|
||||
_arguments -C \
|
||||
'(-v --verify)'{-v,--verify}'[Run `dcsctl verify` before running docker compose]' \
|
||||
'1:project name:_dcsctl__projects' \
|
||||
&& return
|
||||
;;
|
||||
|
||||
down)
|
||||
_arguments -C \
|
||||
'1:project name:_dcsctl__projects' \
|
||||
&& return
|
||||
;;
|
||||
|
||||
stop)
|
||||
_arguments -C \
|
||||
'1:project name:_dcsctl__projects' \
|
||||
&& return
|
||||
;;
|
||||
|
||||
deploy)
|
||||
_arguments -C \
|
||||
'(-v --verify)'{-v,--verify}'[Run verification before deploying]' \
|
||||
'--verbose[Verbose verification output]' \
|
||||
'1:project name:_dcsctl__projects' \
|
||||
&& return
|
||||
;;
|
||||
|
||||
ls)
|
||||
return
|
||||
;;
|
||||
|
||||
dir)
|
||||
if (( CURRENT == 3 )); then
|
||||
_dcsctl__projects
|
||||
return
|
||||
fi
|
||||
if (( CURRENT == 4 )); then
|
||||
local proj="${words[3]}"
|
||||
_dcsctl__swarm_services "$proj" && return
|
||||
return 0
|
||||
fi
|
||||
;;
|
||||
|
||||
edit)
|
||||
_arguments -C \
|
||||
'(-e --env -r --runtime -s --secret)'{-e,--env}'[Edit .env]' \
|
||||
'(-e --env -r --runtime -s --secret)'{-r,--runtime}'[Edit service.env]' \
|
||||
'(-e --env -r --runtime -s --secret)'{-s,--secret}'[Edit service.secrets.env]' \
|
||||
'--service[Target a specific service (swarm mode)]:service name:_dcsctl__swarm_services_for_edit' \
|
||||
'1:project name:_dcsctl__projects' \
|
||||
&& return
|
||||
;;
|
||||
|
||||
secret)
|
||||
if (( CURRENT == 3 )); then
|
||||
_describe -t secret-subcmds 'secret commands' \
|
||||
'add:Create/open secrets/<name>.txt in $EDITOR'
|
||||
return
|
||||
fi
|
||||
|
||||
local sub
|
||||
sub="${words[3]}"
|
||||
case "$sub" in
|
||||
add)
|
||||
if (( CURRENT == 4 )); then
|
||||
_dcsctl__projects
|
||||
return
|
||||
fi
|
||||
if (( CURRENT == 5 )); then
|
||||
_dcsctl__secret_names_from_dir && return
|
||||
return 0
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
|
||||
service)
|
||||
if (( CURRENT == 3 )); then
|
||||
local -a service_subcmds
|
||||
service_subcmds=(
|
||||
'add:Add a new service to a swarm project'
|
||||
'ls:List services in a swarm project'
|
||||
)
|
||||
_describe -t service-subcmds 'service commands' service_subcmds
|
||||
return
|
||||
fi
|
||||
|
||||
local sub
|
||||
sub="${words[3]}"
|
||||
case "$sub" in
|
||||
add)
|
||||
if (( CURRENT == 4 )); then
|
||||
_dcsctl__projects
|
||||
return
|
||||
fi
|
||||
# CURRENT == 5: free-form service name, no completions needed
|
||||
;;
|
||||
ls)
|
||||
if (( CURRENT == 4 )); then
|
||||
_dcsctl__projects
|
||||
return
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
|
||||
import)
|
||||
_arguments -C \
|
||||
'--env-file[Additional env file to ingest]:env file:_files' \
|
||||
'--strict[Fail if compose violates template contract after import]' \
|
||||
'--swarm[Import as swarm project, splitting services into per-service fragments]' \
|
||||
'1:project name:_dcsctl__projects' \
|
||||
'2:compose path:_files' \
|
||||
&& return
|
||||
;;
|
||||
|
||||
context)
|
||||
if (( CURRENT == 3 )); then
|
||||
_describe -t context-subcmds 'context commands' \
|
||||
'init:Initialize ~/.dcs/<context> with env.system and compose/'
|
||||
return
|
||||
fi
|
||||
|
||||
local sub
|
||||
sub="${words[3]}"
|
||||
case "$sub" in
|
||||
init)
|
||||
if (( CURRENT == 4 )); then
|
||||
_dcsctl__docker_contexts
|
||||
return
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
;;
|
||||
|
||||
reload)
|
||||
_arguments -C \
|
||||
'1:project name:_dcsctl__projects' \
|
||||
&& return
|
||||
;;
|
||||
|
||||
rename)
|
||||
_arguments -C \
|
||||
'1:project name:_dcsctl__projects' \
|
||||
'2:new project name:' \
|
||||
&& return
|
||||
;;
|
||||
|
||||
help)
|
||||
if (( CURRENT == 3 )); then
|
||||
_describe -t commands 'dcsctl commands' subcmds
|
||||
return
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
|
||||
_describe -t commands 'dcsctl commands' subcmds
|
||||
}
|
||||
|
||||
_dcsctl "$@"
|
||||
@@ -0,0 +1,12 @@
|
||||
#compdef dot
|
||||
|
||||
_dot() {
|
||||
local -a dirs
|
||||
local _dotfiles="${DOTFILES:-$HOME/.dotfiles}"
|
||||
|
||||
[[ -d "$_dotfiles" ]] || return 1
|
||||
|
||||
dirs=(${(f)"$(print -rl -- $_dotfiles/*(/N:t))"})
|
||||
|
||||
_describe 'dotfiles subdirectory' dirs
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
#compdef dotfiles_doctor
|
||||
|
||||
# Zsh completion for dotfiles_doctor
|
||||
# Supports: --timeline, --logfile=PATH (completes *.json)
|
||||
# Usage: place this file on your $fpath, then compinit (or let your compinit.zsh rebuild)
|
||||
|
||||
local -a _args
|
||||
_args=(
|
||||
'--timeline[Show per-file start/end timeline with μs precision]'
|
||||
'--logfile=[Path to NDJSON log (default: ~/zshrc-log.json)]:logfile:_files -g "*.json"'
|
||||
'--help[Show usage]'
|
||||
)
|
||||
|
||||
_arguments -s -S $_args \
|
||||
'*:filename:_files'
|
||||
@@ -0,0 +1,7 @@
|
||||
#compdef find_dupes
|
||||
|
||||
_arguments \
|
||||
'(-d --maxdepth)'{-d,--maxdepth}'[maximum recursion depth]:maxdepth (int):_guard "[0-9]#" "integer depth"' \
|
||||
'(-r --regex)'{-r,--regex}'[POSIX extended regular expression]:regex (ERE)' \
|
||||
'(-i --ignore-case)'{-i,--ignore-case}'[match regex case-insensitively]' \
|
||||
'*: :->rest'
|
||||
@@ -0,0 +1,228 @@
|
||||
#compdef kubectl
|
||||
compdef _kubectl kubectl
|
||||
|
||||
# Copyright 2016 The Kubernetes Authors.
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
#compdef kubectl
|
||||
compdef _kubectl kubectl
|
||||
|
||||
# zsh completion for kubectl -*- shell-script -*-
|
||||
|
||||
__kubectl_debug()
|
||||
{
|
||||
local file="$BASH_COMP_DEBUG_FILE"
|
||||
if [[ -n ${file} ]]; then
|
||||
echo "$*" >> "${file}"
|
||||
fi
|
||||
}
|
||||
|
||||
_kubectl()
|
||||
{
|
||||
local shellCompDirectiveError=1
|
||||
local shellCompDirectiveNoSpace=2
|
||||
local shellCompDirectiveNoFileComp=4
|
||||
local shellCompDirectiveFilterFileExt=8
|
||||
local shellCompDirectiveFilterDirs=16
|
||||
local shellCompDirectiveKeepOrder=32
|
||||
|
||||
local lastParam lastChar flagPrefix requestComp out directive comp lastComp noSpace keepOrder
|
||||
local -a completions
|
||||
|
||||
__kubectl_debug "\n========= starting completion logic =========="
|
||||
__kubectl_debug "CURRENT: ${CURRENT}, words[*]: ${words[*]}"
|
||||
|
||||
# The user could have moved the cursor backwards on the command-line.
|
||||
# We need to trigger completion from the $CURRENT location, so we need
|
||||
# to truncate the command-line ($words) up to the $CURRENT location.
|
||||
# (We cannot use $CURSOR as its value does not work when a command is an alias.)
|
||||
words=("${=words[1,CURRENT]}")
|
||||
__kubectl_debug "Truncated words[*]: ${words[*]},"
|
||||
|
||||
lastParam=${words[-1]}
|
||||
lastChar=${lastParam[-1]}
|
||||
__kubectl_debug "lastParam: ${lastParam}, lastChar: ${lastChar}"
|
||||
|
||||
# For zsh, when completing a flag with an = (e.g., kubectl -n=<TAB>)
|
||||
# completions must be prefixed with the flag
|
||||
setopt local_options BASH_REMATCH
|
||||
if [[ "${lastParam}" =~ '-.*=' ]]; then
|
||||
# We are dealing with a flag with an =
|
||||
flagPrefix="-P ${BASH_REMATCH}"
|
||||
fi
|
||||
|
||||
# Prepare the command to obtain completions
|
||||
requestComp="${words[1]} __complete ${words[2,-1]}"
|
||||
if [ "${lastChar}" = "" ]; then
|
||||
# If the last parameter is complete (there is a space following it)
|
||||
# We add an extra empty parameter so we can indicate this to the go completion code.
|
||||
__kubectl_debug "Adding extra empty parameter"
|
||||
requestComp="${requestComp} \"\""
|
||||
fi
|
||||
|
||||
__kubectl_debug "About to call: eval ${requestComp}"
|
||||
|
||||
# Use eval to handle any environment variables and such
|
||||
out=$(eval ${requestComp} 2>/dev/null)
|
||||
__kubectl_debug "completion output: ${out}"
|
||||
|
||||
# Extract the directive integer following a : from the last line
|
||||
local lastLine
|
||||
while IFS='\n' read -r line; do
|
||||
lastLine=${line}
|
||||
done < <(printf "%s\n" "${out[@]}")
|
||||
__kubectl_debug "last line: ${lastLine}"
|
||||
|
||||
if [ "${lastLine[1]}" = : ]; then
|
||||
directive=${lastLine[2,-1]}
|
||||
# Remove the directive including the : and the newline
|
||||
local suffix
|
||||
(( suffix=${#lastLine}+2))
|
||||
out=${out[1,-$suffix]}
|
||||
else
|
||||
# There is no directive specified. Leave $out as is.
|
||||
__kubectl_debug "No directive found. Setting do default"
|
||||
directive=0
|
||||
fi
|
||||
|
||||
__kubectl_debug "directive: ${directive}"
|
||||
__kubectl_debug "completions: ${out}"
|
||||
__kubectl_debug "flagPrefix: ${flagPrefix}"
|
||||
|
||||
if [ $((directive & shellCompDirectiveError)) -ne 0 ]; then
|
||||
__kubectl_debug "Completion received error. Ignoring completions."
|
||||
return
|
||||
fi
|
||||
|
||||
local activeHelpMarker="_activeHelp_ "
|
||||
local endIndex=${#activeHelpMarker}
|
||||
local startIndex=$((${#activeHelpMarker}+1))
|
||||
local hasActiveHelp=0
|
||||
while IFS='\n' read -r comp; do
|
||||
# Check if this is an activeHelp statement (i.e., prefixed with $activeHelpMarker)
|
||||
if [ "${comp[1,$endIndex]}" = "$activeHelpMarker" ];then
|
||||
__kubectl_debug "ActiveHelp found: $comp"
|
||||
comp="${comp[$startIndex,-1]}"
|
||||
if [ -n "$comp" ]; then
|
||||
compadd -x "${comp}"
|
||||
__kubectl_debug "ActiveHelp will need delimiter"
|
||||
hasActiveHelp=1
|
||||
fi
|
||||
|
||||
continue
|
||||
fi
|
||||
|
||||
if [ -n "$comp" ]; then
|
||||
# If requested, completions are returned with a description.
|
||||
# The description is preceded by a TAB character.
|
||||
# For zsh's _describe, we need to use a : instead of a TAB.
|
||||
# We first need to escape any : as part of the completion itself.
|
||||
comp=${comp//:/\\:}
|
||||
|
||||
local tab="$(printf '\t')"
|
||||
comp=${comp//$tab/:}
|
||||
|
||||
__kubectl_debug "Adding completion: ${comp}"
|
||||
completions+=${comp}
|
||||
lastComp=$comp
|
||||
fi
|
||||
done < <(printf "%s\n" "${out[@]}")
|
||||
|
||||
# Add a delimiter after the activeHelp statements, but only if:
|
||||
# - there are completions following the activeHelp statements, or
|
||||
# - file completion will be performed (so there will be choices after the activeHelp)
|
||||
if [ $hasActiveHelp -eq 1 ]; then
|
||||
if [ ${#completions} -ne 0 ] || [ $((directive & shellCompDirectiveNoFileComp)) -eq 0 ]; then
|
||||
__kubectl_debug "Adding activeHelp delimiter"
|
||||
compadd -x "--"
|
||||
hasActiveHelp=0
|
||||
fi
|
||||
fi
|
||||
|
||||
if [ $((directive & shellCompDirectiveNoSpace)) -ne 0 ]; then
|
||||
__kubectl_debug "Activating nospace."
|
||||
noSpace="-S ''"
|
||||
fi
|
||||
|
||||
if [ $((directive & shellCompDirectiveKeepOrder)) -ne 0 ]; then
|
||||
__kubectl_debug "Activating keep order."
|
||||
keepOrder="-V"
|
||||
fi
|
||||
|
||||
if [ $((directive & shellCompDirectiveFilterFileExt)) -ne 0 ]; then
|
||||
# File extension filtering
|
||||
local filteringCmd
|
||||
filteringCmd='_files'
|
||||
for filter in ${completions[@]}; do
|
||||
if [ ${filter[1]} != '*' ]; then
|
||||
# zsh requires a glob pattern to do file filtering
|
||||
filter="\*.$filter"
|
||||
fi
|
||||
filteringCmd+=" -g $filter"
|
||||
done
|
||||
filteringCmd+=" ${flagPrefix}"
|
||||
|
||||
__kubectl_debug "File filtering command: $filteringCmd"
|
||||
_arguments '*:filename:'"$filteringCmd"
|
||||
elif [ $((directive & shellCompDirectiveFilterDirs)) -ne 0 ]; then
|
||||
# File completion for directories only
|
||||
local subdir
|
||||
subdir="${completions[1]}"
|
||||
if [ -n "$subdir" ]; then
|
||||
__kubectl_debug "Listing directories in $subdir"
|
||||
pushd "${subdir}" >/dev/null 2>&1
|
||||
else
|
||||
__kubectl_debug "Listing directories in ."
|
||||
fi
|
||||
|
||||
local result
|
||||
_arguments '*:dirname:_files -/'" ${flagPrefix}"
|
||||
result=$?
|
||||
if [ -n "$subdir" ]; then
|
||||
popd >/dev/null 2>&1
|
||||
fi
|
||||
return $result
|
||||
else
|
||||
__kubectl_debug "Calling _describe"
|
||||
if eval _describe $keepOrder "completions" completions $flagPrefix $noSpace; then
|
||||
__kubectl_debug "_describe found some completions"
|
||||
|
||||
# Return the success of having called _describe
|
||||
return 0
|
||||
else
|
||||
__kubectl_debug "_describe did not find completions."
|
||||
__kubectl_debug "Checking if we should do file completion."
|
||||
if [ $((directive & shellCompDirectiveNoFileComp)) -ne 0 ]; then
|
||||
__kubectl_debug "deactivating file completion"
|
||||
|
||||
# We must return an error code here to let zsh know that there were no
|
||||
# completions found by _describe; this is what will trigger other
|
||||
# matching algorithms to attempt to find completions.
|
||||
# For example zsh can match letters in the middle of words.
|
||||
return 1
|
||||
else
|
||||
# Perform file completion
|
||||
__kubectl_debug "Activating file completion"
|
||||
|
||||
# We must return the result of this command, so it must be the
|
||||
# last command, or else we must store its result to return it.
|
||||
_arguments '*:filename:_files'" ${flagPrefix}"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
}
|
||||
|
||||
# don't run the completion function when being source-ed or eval-ed
|
||||
if [ "$funcstack[1]" = "_kubectl" ]; then
|
||||
_kubectl
|
||||
fi
|
||||
@@ -0,0 +1,28 @@
|
||||
#compdef pdir
|
||||
|
||||
_pdir_projects() {
|
||||
local ctx base
|
||||
ctx="$(docker context show 2>/dev/null)"
|
||||
[[ -z "$ctx" ]] && return 0
|
||||
|
||||
# Respect DOCKER_DIR override (same as your dcsctl behavior)
|
||||
base="${DOCKER_DIR:-$HOME/.dcs}"
|
||||
base="$base/$ctx/compose"
|
||||
[[ -d "$base" ]] || return 0
|
||||
|
||||
local -a ignored projects
|
||||
ignored=(shared '@Recycle' '@Recently-Snapshot' '.DS_Store')
|
||||
|
||||
projects=()
|
||||
local dir name
|
||||
for dir in "$base"/*; do
|
||||
[[ -d "$dir" ]] || continue
|
||||
name="${dir:t}"
|
||||
(( ${ignored[(I)$name]} )) && continue
|
||||
projects+=("$name")
|
||||
done
|
||||
|
||||
_describe -t dcs-projects 'projects' projects
|
||||
}
|
||||
|
||||
_arguments -C '1:project:_pdir_projects'
|
||||
@@ -0,0 +1 @@
|
||||
export eza_params=(--grid --git-ignore --all --header --grid --long --no-permissions --no-filesize --no-time --no-user --icons --git --hyperlink)
|
||||
@@ -0,0 +1,23 @@
|
||||
|
||||
|
||||
if command -v brew >/dev/null 2>&1; then
|
||||
FZF_PREFIX=$(brew --prefix fzf)
|
||||
elif [[ -x $HOME/.fzf/bin/fzf ]]; then
|
||||
FZF_PREFIX=$HOME/.fzf
|
||||
PATH="${PATH:+${PATH}:}${(F)FZF_PREFIX}/bin"
|
||||
elif [[ ! -d $HOME/.fzf ]]; then
|
||||
git clone --depth 1 https://github.com/junegunn/fzf.git $HOME/.fzf
|
||||
FZF_PREFIX=$HOME/.fzf
|
||||
PATH="${PATH:+${PATH}:}${(F)FZF_PREFIX}/bin"
|
||||
fi
|
||||
|
||||
|
||||
if [[ -n "${FZF_PREFIX}" ]]; then
|
||||
export FZF_PREFIX
|
||||
export FZF_DEFAULT_COMMAND='fd --type file --follow --hidden --color=always --exclude .git'
|
||||
export FZF_DEFAULT_OPTS="--ansi --bind 'ctrl-y:execute-silent(echo -n {2..} | pbcopy)+abort'"
|
||||
export FZF_CTRL_T_COMMAND="$FZF_DEFAULT_COMMAND"
|
||||
export FZF_CTRL_T_OPTS="--walker-skip .git,node_modules,Library,.venv --preview 'bat -n --color=always {}' --bind 'ctrl-/:change-preview-window(right|hidden)'"
|
||||
fi
|
||||
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
|
||||
# Auto-completion
|
||||
# ---------------
|
||||
if [[ -d "${FZF_PREFIX}" ]]; then
|
||||
call_file "${FZF_PREFIX}/shell/completion.zsh" "fzf-comp"
|
||||
fi
|
||||
@@ -0,0 +1,15 @@
|
||||
if [[ -d "${FZF_PREFIX}" ]]; then
|
||||
autoload -Uz $DOTFILES/fzf/fe
|
||||
# [ -f $HOME/.dotfiles/fzf/fzf-git.sh ] && source $HOME/.dotfiles/fzf/fzf-git.sh
|
||||
v() {
|
||||
local file
|
||||
file="$(fd -t f -H -E .git 2>/dev/null | fzf --preview 'bat --style=numbers --color=always --line-range :200 {}' )" || return
|
||||
nvim --server "$NVIM_SERVER" --remote "$file"
|
||||
}
|
||||
call_file "${FZF_PREFIX}/shell/key-bindings.zsh" "fzf"
|
||||
fi
|
||||
|
||||
|
||||
if ! command -v fd >/dev/null 2>&1; then
|
||||
command -v fdfind >/dev/null 2>&1 && ln -s $(which fdfind) $HOME/.local/bin/fd
|
||||
fi
|
||||
@@ -0,0 +1,4 @@
|
||||
fe() {
|
||||
IFS=$'\n' files=($(fzf-tmux --query="$1" --multi --select-1 --exit-0 --preview="bat --color=always {}"))
|
||||
[[ -n "$files" ]] && ${EDITOR:-vim} "${files[@]}"
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2022 Junegunn Choi
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
if [[ $# -eq 1 ]]; then
|
||||
branches() {
|
||||
git branch "$@" --sort=-committerdate --sort=-HEAD --format=$'%(HEAD) %(color:yellow)%(refname:short) %(color:green)(%(committerdate:relative))\t%(color:blue)%(subject)%(color:reset)' --color=always | column -ts$'\t'
|
||||
}
|
||||
refs() {
|
||||
git for-each-ref --sort=-creatordate --sort=-HEAD --color=always --format=$'%(refname) %(color:green)(%(creatordate:relative))\t%(color:blue)%(subject)%(color:reset)' |
|
||||
eval "$1" |
|
||||
sed 's#^refs/remotes/#\x1b[95mremote-branch\t\x1b[33m#; s#^refs/heads/#\x1b[92mbranch\t\x1b[33m#; s#^refs/tags/#\x1b[96mtag\t\x1b[33m#; s#refs/stash#\x1b[91mstash\t\x1b[33mrefs/stash#' |
|
||||
column -ts$'\t'
|
||||
}
|
||||
case "$1" in
|
||||
branches)
|
||||
echo $'CTRL-O (open in browser) ╱ ALT-A (show all branches)\n'
|
||||
branches
|
||||
;;
|
||||
all-branches)
|
||||
echo $'CTRL-O (open in browser)\n'
|
||||
branches -a
|
||||
;;
|
||||
refs)
|
||||
echo $'CTRL-O (open in browser) ╱ ALT-E (examine in editor) ╱ ALT-A (show all refs)\n'
|
||||
refs 'grep -v ^refs/remotes'
|
||||
;;
|
||||
all-refs)
|
||||
echo $'CTRL-O (open in browser) ╱ ALT-E (examine in editor)\n'
|
||||
refs 'cat'
|
||||
;;
|
||||
nobeep) ;;
|
||||
*) exit 1 ;;
|
||||
esac
|
||||
elif [[ $# -gt 1 ]]; then
|
||||
set -e
|
||||
|
||||
branch=$(git rev-parse --abbrev-ref HEAD 2> /dev/null)
|
||||
if [[ $branch = HEAD ]]; then
|
||||
branch=$(git describe --exact-match --tags 2> /dev/null || git rev-parse --short HEAD)
|
||||
fi
|
||||
|
||||
# Only supports GitHub for now
|
||||
case "$1" in
|
||||
commit)
|
||||
hash=$(grep -o "[a-f0-9]\{7,\}" <<< "$2")
|
||||
path=/commit/$hash
|
||||
;;
|
||||
branch|remote-branch)
|
||||
branch=$(sed 's/^[* ]*//' <<< "$2" | cut -d' ' -f1)
|
||||
remote=$(git config branch."${branch}".remote || echo 'origin')
|
||||
branch=${branch#$remote/}
|
||||
path=/tree/$branch
|
||||
;;
|
||||
remote)
|
||||
remote=$2
|
||||
path=/tree/$branch
|
||||
;;
|
||||
file) path=/blob/$branch/$(git rev-parse --show-prefix)$2 ;;
|
||||
tag) path=/releases/tag/$2 ;;
|
||||
*) exit 1 ;;
|
||||
esac
|
||||
|
||||
remote=${remote:-$(git config branch."${branch}".remote || echo 'origin')}
|
||||
remote_url=$(git remote get-url "$remote" 2> /dev/null || echo "$remote")
|
||||
|
||||
if [[ $remote_url =~ ^git@ ]]; then
|
||||
url=${remote_url%.git}
|
||||
url=${url#git@}
|
||||
url=https://${url/://}
|
||||
elif [[ $remote_url =~ ^http ]]; then
|
||||
url=${remote_url%.git}
|
||||
fi
|
||||
|
||||
case "$(uname -s)" in
|
||||
Darwin) open "$url$path" ;;
|
||||
*) xdg-open "$url$path" ;;
|
||||
esac
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [[ $- =~ i ]]; then
|
||||
# -----------------------------------------------------------------------------
|
||||
|
||||
# Redefine this function to change the options
|
||||
_fzf_git_fzf() {
|
||||
fzf-tmux -p80%,60% -- \
|
||||
--layout=reverse --multi --height=50% --min-height=20 --border \
|
||||
--border-label-pos=2 \
|
||||
--color='header:italic:underline,label:blue' \
|
||||
--preview-window='right,50%,border-left' \
|
||||
--bind='ctrl-/:change-preview-window(down,50%,border-top|hidden|)' "$@"
|
||||
}
|
||||
|
||||
_fzf_git_check() {
|
||||
git rev-parse HEAD > /dev/null 2>&1 && return
|
||||
|
||||
[[ -n $TMUX ]] && tmux display-message "Not in a git repository"
|
||||
return 1
|
||||
}
|
||||
|
||||
__fzf_git=${BASH_SOURCE[0]:-${(%):-%x}}
|
||||
__fzf_git=$(readlink -f "$__fzf_git" 2> /dev/null || /usr/bin/ruby --disable-gems -e 'puts File.expand_path(ARGV.first)' "$__fzf_git" 2> /dev/null)
|
||||
|
||||
if [[ -z $_fzf_git_cat ]]; then
|
||||
# Sometimes bat is installed as batcat
|
||||
export _fzf_git_cat="cat"
|
||||
_fzf_git_bat_options="--style='${BAT_STYLE:-full}' --color=always --pager=never"
|
||||
if command -v batcat > /dev/null; then
|
||||
_fzf_git_cat="batcat $_fzf_git_bat_options"
|
||||
elif command -v bat > /dev/null; then
|
||||
_fzf_git_cat="bat $_fzf_git_bat_options"
|
||||
fi
|
||||
fi
|
||||
|
||||
_fzf_git_files() {
|
||||
_fzf_git_check || return
|
||||
(git -c color.status=always status --short --no-branch
|
||||
git ls-files | grep -vxFf <(git status -s | grep '^[^?]' | cut -c4-; echo :) | sed 's/^/ /') |
|
||||
_fzf_git_fzf -m --ansi --nth 2..,.. \
|
||||
--border-label '📁 Files' \
|
||||
--header $'CTRL-O (open in browser) ╱ ALT-E (open in editor)\n\n' \
|
||||
--bind "ctrl-o:execute-silent:bash $__fzf_git file {-1}" \
|
||||
--bind "alt-e:execute:${EDITOR:-vim} {-1} > /dev/tty" \
|
||||
--preview "git diff --no-ext-diff --color=always -- {-1} | sed 1,4d; $_fzf_git_cat {-1}" "$@" |
|
||||
cut -c4- | sed 's/.* -> //'
|
||||
}
|
||||
|
||||
_fzf_git_branches() {
|
||||
_fzf_git_check || return
|
||||
bash "$__fzf_git" branches |
|
||||
_fzf_git_fzf --ansi \
|
||||
--border-label '🌲 Branches' \
|
||||
--header-lines 2 \
|
||||
--tiebreak begin \
|
||||
--preview-window down,border-top,40% \
|
||||
--color hl:underline,hl+:underline \
|
||||
--no-hscroll \
|
||||
--bind 'ctrl-/:change-preview-window(down,70%|hidden|)' \
|
||||
--bind "ctrl-o:execute-silent:bash $__fzf_git branch {}" \
|
||||
--bind "alt-a:change-prompt(🌳 All branches> )+reload:bash \"$__fzf_git\" all-branches" \
|
||||
--preview 'git log --oneline --graph --date=short --color=always --pretty="format:%C(auto)%cd %h%d %s" $(sed s/^..// <<< {} | cut -d" " -f1)' "$@" |
|
||||
sed 's/^..//' | cut -d' ' -f1
|
||||
}
|
||||
|
||||
_fzf_git_tags() {
|
||||
_fzf_git_check || return
|
||||
git tag --sort -version:refname |
|
||||
_fzf_git_fzf --preview-window right,70% \
|
||||
--border-label '📛 Tags' \
|
||||
--header $'CTRL-O (open in browser)\n\n' \
|
||||
--bind "ctrl-o:execute-silent:bash $__fzf_git tag {}" \
|
||||
--preview 'git show --color=always {}' "$@"
|
||||
}
|
||||
|
||||
_fzf_git_hashes() {
|
||||
_fzf_git_check || return
|
||||
git log --date=short --format="%C(green)%C(bold)%cd %C(auto)%h%d %s (%an)" --graph --color=always |
|
||||
_fzf_git_fzf --ansi --no-sort --bind 'ctrl-s:toggle-sort' \
|
||||
--border-label '🍡 Hashes' \
|
||||
--header $'CTRL-O (open in browser) ╱ CTRL-D (diff) ╱ CTRL-S (toggle sort)\n\n' \
|
||||
--bind "ctrl-o:execute-silent:bash $__fzf_git commit {}" \
|
||||
--bind 'ctrl-d:execute:grep -o "[a-f0-9]\{7,\}" <<< {} | head -n 1 | xargs git diff > /dev/tty' \
|
||||
--color hl:underline,hl+:underline \
|
||||
--preview 'grep -o "[a-f0-9]\{7,\}" <<< {} | head -n 1 | xargs git show --color=always' "$@" |
|
||||
awk 'match($0, /[a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9][a-f0-9]*/) { print substr($0, RSTART, RLENGTH) }'
|
||||
}
|
||||
|
||||
_fzf_git_remotes() {
|
||||
_fzf_git_check || return
|
||||
git remote -v | awk '{print $1 "\t" $2}' | uniq |
|
||||
_fzf_git_fzf --tac \
|
||||
--border-label '📡 Remotes' \
|
||||
--header $'CTRL-O (open in browser)\n\n' \
|
||||
--bind "ctrl-o:execute-silent:bash $__fzf_git remote {1}" \
|
||||
--preview-window right,70% \
|
||||
--preview 'git log --oneline --graph --date=short --color=always --pretty="format:%C(auto)%cd %h%d %s" {1}/"$(git rev-parse --abbrev-ref HEAD)"' "$@" |
|
||||
cut -d$'\t' -f1
|
||||
}
|
||||
|
||||
_fzf_git_stashes() {
|
||||
_fzf_git_check || return
|
||||
git stash list | _fzf_git_fzf \
|
||||
--border-label '🥡 Stashes' \
|
||||
--header $'CTRL-X (drop stash)\n\n' \
|
||||
--bind 'ctrl-x:execute-silent(git stash drop {1})+reload(git stash list)' \
|
||||
-d: --preview 'git show --color=always {1}' "$@" |
|
||||
cut -d: -f1
|
||||
}
|
||||
|
||||
_fzf_git_lreflogs() {
|
||||
_fzf_git_check || return
|
||||
git reflog --color=always --format="%C(blue)%gD %C(yellow)%h%C(auto)%d %gs" | _fzf_git_fzf --ansi \
|
||||
--border-label '📒 Reflogs' \
|
||||
--preview 'git show --color=always {1}' "$@" |
|
||||
awk '{print $1}'
|
||||
}
|
||||
|
||||
_fzf_git_each_ref() {
|
||||
_fzf_git_check || return
|
||||
bash "$__fzf_git" refs | _fzf_git_fzf --ansi \
|
||||
--nth 2,2.. \
|
||||
--tiebreak begin \
|
||||
--border-label '☘️ Each ref' \
|
||||
--header-lines 2 \
|
||||
--preview-window down,border-top,40% \
|
||||
--color hl:underline,hl+:underline \
|
||||
--no-hscroll \
|
||||
--bind 'ctrl-/:change-preview-window(down,70%|hidden|)' \
|
||||
--bind "ctrl-o:execute-silent:bash $__fzf_git {1} {2}" \
|
||||
--bind "alt-e:execute:${EDITOR:-vim} <(git show {2}) > /dev/tty" \
|
||||
--bind "alt-a:change-prompt(🍀 Every ref> )+reload:bash \"$__fzf_git\" all-refs" \
|
||||
--preview 'git log --oneline --graph --date=short --color=always --pretty="format:%C(auto)%cd %h%d %s" {2}' "$@" |
|
||||
awk '{print $2}'
|
||||
}
|
||||
|
||||
if [[ -n "${BASH_VERSION:-}" ]]; then
|
||||
__fzf_git_init() {
|
||||
bind '"\er": redraw-current-line'
|
||||
local o
|
||||
for o in "$@"; do
|
||||
bind '"\C-g\C-'${o:0:1}'": "`_fzf_git_'$o'`\e\C-e\er"'
|
||||
bind '"\C-g'${o:0:1}'": "`_fzf_git_'$o'`\e\C-e\er"'
|
||||
done
|
||||
}
|
||||
elif [[ -n "${ZSH_VERSION:-}" ]]; then
|
||||
__fzf_git_join() {
|
||||
local item
|
||||
while read item; do
|
||||
echo -n "${(q)item} "
|
||||
done
|
||||
}
|
||||
|
||||
__fzf_git_init() {
|
||||
local o
|
||||
for o in "$@"; do
|
||||
eval "fzf-git-$o-widget() { local result=\$(_fzf_git_$o | __fzf_git_join); zle reset-prompt; LBUFFER+=\$result }"
|
||||
eval "zle -N fzf-git-$o-widget"
|
||||
eval "bindkey '^g^${o[1]}' fzf-git-$o-widget"
|
||||
eval "bindkey '^g${o[1]}' fzf-git-$o-widget"
|
||||
done
|
||||
}
|
||||
fi
|
||||
__fzf_git_init files branches tags remotes hashes stashes lreflogs each_ref
|
||||
|
||||
# -----------------------------------------------------------------------------
|
||||
fi
|
||||
@@ -0,0 +1,12 @@
|
||||
# Setup fzf
|
||||
# ---------
|
||||
# [ -f ~/.fzf.zsh ] && call_file ~/.fzf.zsh "fzf"
|
||||
if [[ ! "$PATH" == *$(brew --prefix)/fzf/opt/bin* ]]; then
|
||||
PATH="${PATH:+${PATH}:}${(f)$(brew --prefix fzf)}/bin"
|
||||
fi
|
||||
|
||||
|
||||
|
||||
# Key bindings
|
||||
# ------------
|
||||
call_file $HOMEBREW_PREFIX/opt/fzf/shell/key-bindings.zsh "fzf-symlink"
|
||||
@@ -0,0 +1,12 @@
|
||||
create_dot_symlink () {
|
||||
_file="${HOME}/.${${1:t}%.*}"
|
||||
if ! test -L "$_file"; then
|
||||
if [[ -r "$_file" ]]; then
|
||||
mv "$_file" "${_file}.symlink-replacement"
|
||||
fi
|
||||
ln -s $1 $_file
|
||||
fi
|
||||
}
|
||||
for _item in ${DOTFILES}/git/*.symlink; do
|
||||
create_dot_symlink $_item
|
||||
done
|
||||
@@ -0,0 +1,5 @@
|
||||
[user]
|
||||
name = AUTHORNAME
|
||||
email = AUTHOREMAIL
|
||||
[credential]
|
||||
helper = GIT_CREDENTIAL_HELPER
|
||||
@@ -0,0 +1,27 @@
|
||||
# Local/private config goes in the include
|
||||
[include]
|
||||
path = ~/.gitconfig.local
|
||||
[filter "lfs"]
|
||||
required = true
|
||||
clean = git-lfs clean -- %f
|
||||
smudge = git-lfs smudge -- %f
|
||||
process = git-lfs filter-process
|
||||
[user]
|
||||
name = nick-gorse
|
||||
email = nick@olb42.com
|
||||
[core]
|
||||
excludesFile = ~/.gitignore
|
||||
pager = delta
|
||||
[webui]
|
||||
autoupdate = true
|
||||
[alias]
|
||||
webui = !~/.git-webui/release/libexec/git-core/git-webui
|
||||
st = "!f(){ git status --ignored --untracked-file; }; f"
|
||||
[instaweb]
|
||||
browser = open
|
||||
[interactive]
|
||||
diffFilter = delta --color-only
|
||||
[delta]
|
||||
navigate = true
|
||||
[merge]
|
||||
conflictStyle = zdiff3
|
||||
@@ -0,0 +1,9 @@
|
||||
.DS_Store
|
||||
._*
|
||||
*.swp
|
||||
__pycache__
|
||||
.ipynb_checkpoints
|
||||
*.log
|
||||
*egg-info
|
||||
.autoversion
|
||||
.idea/
|
||||
@@ -0,0 +1,27 @@
|
||||
#!/usr/bin/env zsh
|
||||
#
|
||||
if test -n "$KITTY_INSTALLATION_DIR"; then
|
||||
if ! command -v kitty >/dev/null 2>&1; then
|
||||
export KITTY_SHELL_INTEGRATION="enabled"
|
||||
autoload -Uz -- "$KITTY_INSTALLATION_DIR"/shell-integration/zsh/kitty-integration
|
||||
kitty-integration
|
||||
unfunction kitty-integration
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ -f "$DOTFILES/kitty/logo/${$(hostname):l}.png" ]]; then
|
||||
KITTY_LOGO_FILE=$DOTFILES/kitty/logo/${$(hostname):l}.png
|
||||
else
|
||||
KITTY_LOGO_FILE=$DOTFILES/kitty/logo/default.png
|
||||
fi
|
||||
export KITTY_LOGO_FILE
|
||||
|
||||
_change_kitty_logo () {
|
||||
[[ -n $KITTY_WINDOW_ID ]] && kitten @ set-window-logo --match id:$KITTY_WINDOW_ID $KITTY_LOGO_FILE
|
||||
}
|
||||
|
||||
if [[ -n $KITTY_WINDOW_ID ]]; then
|
||||
autoload -Uz add-zsh-hook
|
||||
add-zsh-hook precmd _change_kitty_logo
|
||||
fi
|
||||
|
||||
|
After Width: | Height: | Size: 94 KiB |
|
After Width: | Height: | Size: 24 KiB |
|
After Width: | Height: | Size: 27 KiB |
|
After Width: | Height: | Size: 11 KiB |
@@ -0,0 +1 @@
|
||||
nicks-macbook-air.local.png
|
||||
|
After Width: | Height: | Size: 233 KiB |
|
After Width: | Height: | Size: 65 KiB |
|
After Width: | Height: | Size: 9.6 KiB |
|
After Width: | Height: | Size: 9.6 KiB |
|
After Width: | Height: | Size: 9.6 KiB |
|
After Width: | Height: | Size: 50 KiB |
|
After Width: | Height: | Size: 50 KiB |
@@ -0,0 +1,118 @@
|
||||
.TH BIGGEST 1 "December 2025" "User Commands"
|
||||
.SH NAME
|
||||
biggest \- list the largest files and directories within a limited depth
|
||||
|
||||
.SH SYNOPSIS
|
||||
.B biggest
|
||||
.RI [ \-n " COUNT" | \-\-number " COUNT" ]
|
||||
.RI [ \-p " GLOB_PATTERN" | \-\-pattern " GLOB_PATTERN" ]
|
||||
|
||||
.SH DESCRIPTION
|
||||
.B biggest
|
||||
scans the current directory tree to a maximum depth of three levels,
|
||||
including hidden files and directories, and prints the largest entries
|
||||
sorted by size.
|
||||
|
||||
Sizes are sorted using a block-based numeric size (via
|
||||
.BR du (1)
|
||||
with the
|
||||
.B -sk
|
||||
option), but displayed in human-readable form (e.g. 1K, 23M, 4.2G)
|
||||
using
|
||||
.BR du (1)
|
||||
with the
|
||||
.B -sh
|
||||
option. Permission-related errors and similar diagnostics are
|
||||
suppressed.
|
||||
|
||||
The command considers both files and directories and "rolls up" the
|
||||
size of directories as
|
||||
.B du
|
||||
does.
|
||||
|
||||
If no options are given, the default is to list the 20 largest entries
|
||||
that match the pattern "\fB*\fR".
|
||||
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.BR -n " " \fICOUNT\fR ", " --number =\fICOUNT\fR
|
||||
Set the number of entries to display (largest first). \fICOUNT\fR must
|
||||
be a positive integer. The default is 20.
|
||||
|
||||
.TP
|
||||
.BR -p " " \fIGLOB_PATTERN\fR ", " --pattern =\fIGLOB_PATTERN\fR
|
||||
Shell-style glob pattern used to filter file and directory names
|
||||
(e.g. "\fB*.log\fR", "\fBnode*\fR"). The pattern is applied by
|
||||
.BR find (1)
|
||||
via its
|
||||
.B -name
|
||||
predicate. The default pattern is "\fB*\fR", which matches all names.
|
||||
|
||||
.TP
|
||||
.BR -h ", " --help
|
||||
Display a short usage message and exit.
|
||||
|
||||
.SH BEHAVIOUR
|
||||
The search is limited to paths within three levels from the current
|
||||
directory:
|
||||
|
||||
.IP \[bu]
|
||||
\fB./*\fR
|
||||
.IP \[bu]
|
||||
\fB./*/*\fR
|
||||
.IP \[bu]
|
||||
\fB./*/*/*\fR
|
||||
|
||||
Hidden files and directories (names beginning with ".") are included as
|
||||
long as they match the specified pattern.
|
||||
|
||||
The command suppresses "permission denied" and similar error messages
|
||||
from underlying tools.
|
||||
|
||||
Output is colourised: the size field is printed in one colour and the
|
||||
path in another, subject to terminal support for ANSI escape codes.
|
||||
|
||||
.SH EXAMPLES
|
||||
.TP
|
||||
List the top 20 largest files and directories within three levels:
|
||||
.PP
|
||||
.nf
|
||||
.RS
|
||||
biggest
|
||||
.RE
|
||||
.fi
|
||||
|
||||
.TP
|
||||
List the top 50 largest entries:
|
||||
.PP
|
||||
.nf
|
||||
.RS
|
||||
biggest -n 50
|
||||
.RE
|
||||
.fi
|
||||
|
||||
.TP
|
||||
List the top 20 largest log files:
|
||||
.PP
|
||||
.nf
|
||||
.RS
|
||||
biggest -p "*.log"
|
||||
.RE
|
||||
.fi
|
||||
|
||||
.TP
|
||||
Use both options together:
|
||||
.PP
|
||||
.nf
|
||||
.RS
|
||||
biggest -n 100 -p "node*"
|
||||
.RE
|
||||
.fi
|
||||
|
||||
.SH SEE ALSO
|
||||
.BR du (1),
|
||||
.BR find (1),
|
||||
.BR zsh (1)
|
||||
|
||||
.SH AUTHOR
|
||||
Written by the user and maintained locally.
|
||||
@@ -0,0 +1,79 @@
|
||||
.TH COMPINIT_DOCTOR 1 "2025-11-11" "compinit_doctor" "User Commands"
|
||||
.SH NAME
|
||||
compinit_doctor \- inspect, audit, diff, and rebuild zsh completion state
|
||||
.SH SYNOPSIS
|
||||
.B compinit_doctor
|
||||
.RI [ --status ]
|
||||
.RI [ --audit ]
|
||||
.RI [ --rebuild ]
|
||||
.RI [ --snap\ FILE ]
|
||||
.RI [ --diff\ FILE ]
|
||||
.RI [ --dump\ PATH ]
|
||||
.RI [ --json ]
|
||||
.RI [ --help ]
|
||||
.SH DESCRIPTION
|
||||
.B compinit_doctor
|
||||
is an autoloaded zsh function that diagnoses the shell completion system.
|
||||
It reports the zcompdump location and age, compiled dump presence, basic
|
||||
health of the completion setup, and can optionally rebuild the dump, snapshot
|
||||
the current
|
||||
.I $fpath
|
||||
and perform diffs against a previous snapshot.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.BR --status , " -s"
|
||||
Show current compinit and zcompdump status (default action).
|
||||
.TP
|
||||
.BR --audit , " -a"
|
||||
Run
|
||||
.B compaudit
|
||||
and list insecure directories or files.
|
||||
.TP
|
||||
.BR --rebuild , " -r"
|
||||
Force
|
||||
.B compinit -i
|
||||
using the configured dump path and background
|
||||
.BR zcompile .
|
||||
.TP
|
||||
.BR --snap " " \fIFILE\fR , " -S " \fIFILE\fR
|
||||
Snapshot the current (sorted)
|
||||
.I $fpath
|
||||
to
|
||||
.IR FILE .
|
||||
.TP
|
||||
.BR --diff " " \fIFILE\fR , " -d " \fIFILE\fR
|
||||
Compare the current (sorted)
|
||||
.I $fpath
|
||||
to a snapshot
|
||||
.IR FILE ,
|
||||
reporting added and removed paths.
|
||||
.TP
|
||||
.BR --dump " " \fIPATH\fR , " -D " \fIPATH\fR
|
||||
Override the zcompdump path (default:
|
||||
.IR $XDG_CACHE_HOME/zcompdump ).
|
||||
.TP
|
||||
.BR --json , " -j"
|
||||
Emit the status as a single JSON object (machine readable).
|
||||
.TP
|
||||
.BR --help , " -h"
|
||||
Show usage.
|
||||
.SH ENVIRONMENT
|
||||
.TP
|
||||
.B ZCOMP_DUMP_PATH
|
||||
Overrides the zcompdump file path.
|
||||
.SH FILES
|
||||
.TP
|
||||
.I ~/.cache/zcompdump
|
||||
Default zcompdump file read by
|
||||
.BR compinit .
|
||||
.TP
|
||||
.I ~/.cache/zcompdump.zwc
|
||||
Optional compiled dump file created by
|
||||
.BR zcompile .
|
||||
.SH EXIT STATUS
|
||||
Returns 0 on success; non\-zero on argument or runtime errors.
|
||||
.SH SEE ALSO
|
||||
.BR zsh (1),
|
||||
.BR compinit (1),
|
||||
.BR compaudit (1),
|
||||
.BR zcompile (1)
|
||||
@@ -0,0 +1,127 @@
|
||||
.TH CONFE 1 "2025" "User Commands"
|
||||
.SH NAME
|
||||
confe \- edit configuration files with optional backup and git staging
|
||||
.SH SYNOPSIS
|
||||
.B confe
|
||||
[\fB--backup\fR | \fB-b\fR]
|
||||
[\fB--staged\fR | \fB-s\fR]
|
||||
\fITOPIC\fR
|
||||
.SH DESCRIPTION
|
||||
The
|
||||
.B confe
|
||||
command provides a unified interface for editing a set of known
|
||||
configuration files. Each topic corresponds to a specific file path,
|
||||
and the command can optionally perform a backup of the file before
|
||||
editing, or stage the file into a git repository afterwards.
|
||||
|
||||
The editing action itself is performed by an external executable,
|
||||
determined in the following order:
|
||||
.IP \(bu 3
|
||||
The value of the environment variable \fBCONFE_EDIT_CMD\fR
|
||||
.IP \(bu 3
|
||||
The user's \fBEDITOR\fR variable
|
||||
.IP \(bu 3
|
||||
A fallback to \fBecho\fR
|
||||
|
||||
File backups are performed using the command referenced in
|
||||
\fBCONFE_BACKUP_CMD\fR, falling back to \fBecho\fR if unset.
|
||||
|
||||
The function ensures that when staging is requested, the file is only
|
||||
added if it resides inside an actual git repository.
|
||||
|
||||
.SH TOPICS
|
||||
The following topics are supported:
|
||||
|
||||
.TP
|
||||
.B alias
|
||||
The user's shell alias configuration file.
|
||||
|
||||
.TP
|
||||
.B keybindings
|
||||
Shell keybinding configuration file.
|
||||
|
||||
.TP
|
||||
.B options
|
||||
General shell options file.
|
||||
|
||||
.TP
|
||||
.B path
|
||||
Shell path configuration file.
|
||||
|
||||
.TP
|
||||
.B packages
|
||||
A custom list of package declarations.
|
||||
|
||||
.TP
|
||||
.B ssh
|
||||
SSH client configuration file (typically \fI~/.ssh/config\fR).
|
||||
|
||||
.TP
|
||||
.B brewfile
|
||||
The user's Brewfile.
|
||||
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.B --backup, -b
|
||||
Before editing, the file is passed to a backup helper. This helper is a
|
||||
dummy command by default and must be implemented by the user.
|
||||
|
||||
.TP
|
||||
.B --staged, -s
|
||||
After editing, the file is staged into the git repository it belongs to.
|
||||
The command checks that the file resides within a valid git work tree.
|
||||
|
||||
.SH ENVIRONMENT
|
||||
.TP
|
||||
.B CONFE_EDIT_CMD
|
||||
Defines the editor executable. Overrides \fBEDITOR\fR.
|
||||
|
||||
.TP
|
||||
.B CONFE_BACKUP_CMD
|
||||
Defines the backup executable. If unset, backups use \fBecho\fR.
|
||||
|
||||
.SH EXIT STATUS
|
||||
The command exits with:
|
||||
.TP
|
||||
0
|
||||
Success.
|
||||
.TP
|
||||
>0
|
||||
Failure in backup, edit, staging, or invalid topic.
|
||||
|
||||
.SH EXAMPLES
|
||||
Edit alias file normally:
|
||||
.PP
|
||||
.nf
|
||||
confe alias
|
||||
.fi
|
||||
|
||||
Backup first, then edit keybindings:
|
||||
.PP
|
||||
.nf
|
||||
confe --backup keybindings
|
||||
.fi
|
||||
|
||||
Edit and automatically stage path file:
|
||||
.PP
|
||||
.nf
|
||||
confe -s path
|
||||
.fi
|
||||
|
||||
Backup + edit + stage:
|
||||
.PP
|
||||
.nf
|
||||
confe -b -s options
|
||||
.fi
|
||||
|
||||
Override editor:
|
||||
.PP
|
||||
.nf
|
||||
CONFE_EDIT_CMD=nvim confe packages
|
||||
.fi
|
||||
|
||||
.SH AUTHOR
|
||||
Generated automatically upon request.
|
||||
|
||||
.SH SEE ALSO
|
||||
zsh(1), git(1), editor(1)
|
||||
@@ -0,0 +1,342 @@
|
||||
.TH DCSCTL 1 "2026" "User Commands"
|
||||
.SH NAME
|
||||
dcsctl \- control-plane CLI for Docker Compose and Docker Swarm stacks
|
||||
.SH SYNOPSIS
|
||||
.B dcsctl
|
||||
\fIcommand\fR [\fIarguments\fR] [\fIflags\fR]
|
||||
.SH DESCRIPTION
|
||||
.B dcsctl
|
||||
provides a structured control-plane for managing Docker Compose and Docker
|
||||
Swarm projects. It enforces a consistent file and directory layout,
|
||||
manages environment variable layering across stack and per-service
|
||||
scopes, handles bind-mount permission enforcement, and supports
|
||||
deploying to Docker Swarm with automatic compose fragment merging
|
||||
and variable resolution.
|
||||
.PP
|
||||
Projects operate in one of two modes:
|
||||
.TP
|
||||
.B compose mode
|
||||
(legacy) A flat layout with a single
|
||||
.I docker-compose.yml
|
||||
at the project root and shared
|
||||
.IR service.env / service.secrets.env
|
||||
files. Managed with
|
||||
.BR "dcsctl run" ", " "dcsctl up" ", " "dcsctl down" .
|
||||
.TP
|
||||
.B swarm mode
|
||||
(default for new projects) Each service lives in its own subdirectory
|
||||
under
|
||||
.IR services/ ,
|
||||
with per-service compose fragments and env files. At deploy time,
|
||||
fragments are merged, environment variables resolved, and the result
|
||||
written as
|
||||
.I docker-compose.resolved.yml
|
||||
for use with
|
||||
.BR "docker stack deploy" .
|
||||
Managed with
|
||||
.BR "dcsctl deploy" " and " "dcsctl down" .
|
||||
.PP
|
||||
Mode is detected automatically: if a project has a
|
||||
.I services/
|
||||
directory, it is treated as swarm mode; otherwise compose mode.
|
||||
.SH PROJECT LAYOUT
|
||||
.SS Swarm mode (default)
|
||||
.nf
|
||||
~/.dcs/<context>/compose/<project>/
|
||||
\(ba\(em .env stack-level orchestration vars
|
||||
\(ba\(em services/
|
||||
\(ba \(ba\(em traefik/
|
||||
\(ba \(ba \(ba\(em compose.yml service compose fragment
|
||||
\(ba \(ba \(ba\(em service.env service runtime config
|
||||
\(ba \(ba \(ba\(em service.secrets.env service secrets
|
||||
\(ba \(ba\(em app/
|
||||
\(ba \(ba\(em compose.yml
|
||||
\(ba \(ba\(em service.env
|
||||
\(ba \(ba\(em service.secrets.env
|
||||
\(ba\(em docker-compose.resolved.yml generated at deploy time
|
||||
\(ba\(em secrets/
|
||||
\(ba\(em .gitignore
|
||||
.fi
|
||||
.SS Compose mode (legacy)
|
||||
.nf
|
||||
~/.dcs/<context>/compose/<project>/
|
||||
\(ba\(em docker-compose.yml
|
||||
\(ba\(em .env
|
||||
\(ba\(em service.env
|
||||
\(ba\(em service.secrets.env
|
||||
\(ba\(em secrets/
|
||||
\(ba\(em .gitignore
|
||||
.fi
|
||||
.SH COMMANDS
|
||||
.SS Project creation
|
||||
.TP
|
||||
.BI "dcsctl new " "project " "[flags]"
|
||||
Create a new project. By default creates a swarm-mode layout with an
|
||||
initial service named
|
||||
.BR app .
|
||||
.RS
|
||||
.TP
|
||||
.B \-\-compose
|
||||
Create a legacy flat compose layout instead.
|
||||
.TP
|
||||
.BI \-\-service " name"
|
||||
Name the initial service (default:
|
||||
.BR app ).
|
||||
Only applies in swarm mode.
|
||||
.TP
|
||||
.BR \-v ", " \-\-verify
|
||||
Run verification after creating the project.
|
||||
.TP
|
||||
.B \-\-verbose
|
||||
Verbose verification output (used with
|
||||
.BR \-\-verify ).
|
||||
.RE
|
||||
.SS Deployment
|
||||
.TP
|
||||
.BI "dcsctl deploy " "project " "[flags]"
|
||||
Merge all service compose fragments, resolve environment variables,
|
||||
write
|
||||
.IR docker-compose.resolved.yml ,
|
||||
and run
|
||||
.BR "docker stack deploy" .
|
||||
Only available for swarm-mode projects.
|
||||
.RS
|
||||
.TP
|
||||
.BR \-v ", " \-\-verify
|
||||
Run verification before deploying.
|
||||
.TP
|
||||
.B \-\-verbose
|
||||
Verbose verification output.
|
||||
.RE
|
||||
.TP
|
||||
.BI "dcsctl up " "project " "[flags]"
|
||||
Alias for
|
||||
.BR "dcsctl run " "\fIproject\fR up -d" .
|
||||
Only available for compose-mode projects. Swarm-mode projects should
|
||||
use
|
||||
.BR "dcsctl deploy" .
|
||||
.RS
|
||||
.TP
|
||||
.BR \-v ", " \-\-verify
|
||||
Run verification before starting.
|
||||
.RE
|
||||
.TP
|
||||
.BI "dcsctl stop " project
|
||||
Scale all services in a deployed swarm stack to 0 replicas, leaving the
|
||||
stack definition in place. Use
|
||||
.BR "dcsctl deploy"
|
||||
to resume. Only available for swarm-mode projects.
|
||||
.TP
|
||||
.BI "dcsctl down " project
|
||||
Bring down a project. For compose-mode projects, runs
|
||||
.BR "docker compose down" .
|
||||
For swarm-mode projects, runs
|
||||
.BR "docker stack rm" .
|
||||
.TP
|
||||
.BI "dcsctl run " "project " "[docker compose args...]"
|
||||
Run docker compose with the project's context and environment files.
|
||||
Compose-mode only. All arguments after the project name are passed
|
||||
directly to
|
||||
.BR "docker compose" .
|
||||
.TP
|
||||
.BI "dcsctl reload " project
|
||||
Down, rebuild, then up a project (compose mode).
|
||||
.SS Service management
|
||||
.TP
|
||||
.BI "dcsctl service add " "project service-name"
|
||||
Add a new service to a swarm-mode project. Creates the service
|
||||
subdirectory with compose fragment template, service.env, and
|
||||
service.secrets.env.
|
||||
.TP
|
||||
.BI "dcsctl service ls " project
|
||||
List services in a swarm-mode project.
|
||||
.SS Verification
|
||||
.TP
|
||||
.BI "dcsctl verify " "project " "[flags]"
|
||||
Verify and enforce host permissions for bind-mounted appdata
|
||||
directories. Applies compose fixups (docker.sock group_add,
|
||||
single-service normalization). For swarm-mode projects, checks
|
||||
all service fragments.
|
||||
.RS
|
||||
.TP
|
||||
.B \-\-verbose
|
||||
Show current vs expected ownership/mode while verifying.
|
||||
.RE
|
||||
.SS Editing
|
||||
.TP
|
||||
.BI "dcsctl edit " "project " "[flags]"
|
||||
Open a project file in
|
||||
.BR $EDITOR .
|
||||
Defaults to the compose file.
|
||||
.RS
|
||||
.TP
|
||||
.BR \-e ", " \-\-env
|
||||
Edit the stack-level .env file.
|
||||
.TP
|
||||
.BR \-r ", " \-\-runtime
|
||||
Edit service.env (or per-service service.env if
|
||||
.B \-\-service
|
||||
is specified).
|
||||
.TP
|
||||
.BR \-s ", " \-\-secret
|
||||
Edit service.secrets.env (or per-service service.secrets.env if
|
||||
.B \-\-service
|
||||
is specified).
|
||||
.TP
|
||||
.BI \-\-service " name"
|
||||
Target a specific service's files (swarm mode). When combined with
|
||||
.B \-\-env
|
||||
, always edits the stack-level .env.
|
||||
.RE
|
||||
.SS Secrets
|
||||
.TP
|
||||
.BI "dcsctl secret add " "project name"
|
||||
Create or edit
|
||||
.IR secrets/<name>.txt
|
||||
in
|
||||
.BR $EDITOR .
|
||||
.SS Import
|
||||
.TP
|
||||
.BI "dcsctl import " "project compose-path " "[flags]"
|
||||
Import an existing docker-compose.yml into a DCS project.
|
||||
Routes environment variables to the appropriate DCS layers.
|
||||
.RS
|
||||
.TP
|
||||
.BI \-\-env\-file " path"
|
||||
Additional env file to ingest alongside env_file references
|
||||
found in the compose.
|
||||
.TP
|
||||
.B \-\-strict
|
||||
Fail if the compose violates the template contract after import.
|
||||
.TP
|
||||
.B \-\-swarm
|
||||
Import as a swarm project, splitting each service into its own
|
||||
subdirectory under
|
||||
.IR services/ .
|
||||
.RE
|
||||
.SS Project management
|
||||
.TP
|
||||
.BI "dcsctl rename " "project new-name"
|
||||
Rename a project directory and update all env metadata references.
|
||||
.TP
|
||||
.BI "dcsctl dir " "project " "[service]"
|
||||
Print the resolved compose project directory path. If
|
||||
.I service
|
||||
is specified and the project is in swarm mode, prints the path to
|
||||
.IR services/<service>
|
||||
instead.
|
||||
.TP
|
||||
.B dcsctl ls
|
||||
List all running compose stacks (alias for
|
||||
.BR "docker compose ls" ).
|
||||
.SS Context management
|
||||
.TP
|
||||
.BI "dcsctl context init " "[context]"
|
||||
Initialize
|
||||
.I ~/.dcs/<context>
|
||||
with
|
||||
.I env.system
|
||||
and
|
||||
.IR compose/ .
|
||||
If no context is specified, uses the current Docker context.
|
||||
.SH ENVIRONMENT
|
||||
.TP
|
||||
.B DCS_ROOT
|
||||
Base directory for all DCS contexts. Defaults to
|
||||
.IR ~/.dcs .
|
||||
.TP
|
||||
.B DCS_CONTEXT_ROOT
|
||||
Override the context root directory. Defaults to
|
||||
.IR $DCS_ROOT/<context> .
|
||||
.TP
|
||||
.B EDITOR
|
||||
Editor used by
|
||||
.B edit
|
||||
and
|
||||
.B secret add
|
||||
commands. Falls back to
|
||||
.BR vi .
|
||||
.SH FILES
|
||||
.TP
|
||||
.I ~/.dcs/<context>/env.system
|
||||
Context-level environment overrides (HOST_DATA_ROOT, APPDATA_UID, etc.).
|
||||
.TP
|
||||
.I ~/.dcs/<context>/compose/<project>/.env
|
||||
Stack-level orchestration variables (DCS_PROJ_NAME, DCS_STACK_NAME,
|
||||
DCS_NET_NAME, HOST_DATA_ROOT, BASE_DIR).
|
||||
.TP
|
||||
.I services/*/compose.yml
|
||||
Per-service compose fragments (swarm mode).
|
||||
.TP
|
||||
.I services/*/service.env
|
||||
Per-service runtime configuration (swarm mode).
|
||||
.TP
|
||||
.I services/*/service.secrets.env
|
||||
Per-service secrets (swarm mode). Not committed to version control.
|
||||
.TP
|
||||
.I docker-compose.resolved.yml
|
||||
Fully resolved compose file generated by
|
||||
.BR deploy .
|
||||
All variables substituted, env_file directives inlined. Not committed
|
||||
to version control.
|
||||
.SH EXIT STATUS
|
||||
.TP
|
||||
0
|
||||
Success.
|
||||
.TP
|
||||
>0
|
||||
Failure. Error message printed to stderr.
|
||||
.SH EXAMPLES
|
||||
Create a new swarm-mode project:
|
||||
.PP
|
||||
.nf
|
||||
dcsctl new mystack
|
||||
.fi
|
||||
.PP
|
||||
Create with a custom initial service name:
|
||||
.PP
|
||||
.nf
|
||||
dcsctl new mystack --service traefik
|
||||
.fi
|
||||
.PP
|
||||
Create a legacy compose-mode project:
|
||||
.PP
|
||||
.nf
|
||||
dcsctl new mystack --compose
|
||||
.fi
|
||||
.PP
|
||||
Add a service to a swarm project:
|
||||
.PP
|
||||
.nf
|
||||
dcsctl service add mystack redis
|
||||
.fi
|
||||
.PP
|
||||
Deploy a swarm project:
|
||||
.PP
|
||||
.nf
|
||||
dcsctl deploy mystack
|
||||
dcsctl deploy mystack --verify
|
||||
.fi
|
||||
.PP
|
||||
Edit a specific service's env:
|
||||
.PP
|
||||
.nf
|
||||
dcsctl edit mystack --service traefik --runtime
|
||||
.fi
|
||||
.PP
|
||||
Import an existing compose as a swarm project:
|
||||
.PP
|
||||
.nf
|
||||
dcsctl import mystack /path/to/docker-compose.yml --swarm
|
||||
.fi
|
||||
.PP
|
||||
Run docker compose commands (compose mode):
|
||||
.PP
|
||||
.nf
|
||||
dcsctl run myapp logs -f
|
||||
dcsctl up myapp --verify
|
||||
dcsctl down myapp
|
||||
.fi
|
||||
.SH SEE ALSO
|
||||
.BR docker (1),
|
||||
.BR docker-compose (1)
|
||||
@@ -0,0 +1,80 @@
|
||||
.\" ===========================================================================
|
||||
.\" dotfiles_doctor.1 — Manual page for the dotfiles_doctor utility
|
||||
.\" ===========================================================================
|
||||
.TH DOTFILES_DOCTOR 1 "2025-11-06" "dotfiles_doctor" "User Commands"
|
||||
.SH NAME
|
||||
dotfiles_doctor \- analyze zsh startup health, timings, and timeline from NDJSON logs
|
||||
.SH SYNOPSIS
|
||||
.B dotfiles_doctor
|
||||
.RI [ --timeline ]
|
||||
.RI [ --logfile= PATH ]
|
||||
.SH DESCRIPTION
|
||||
.B dotfiles_doctor
|
||||
reads an NDJSON log (newline\-delimited JSON objects) produced by your
|
||||
.ZN call_file
|
||||
logger during shell startup and prints a health report:
|
||||
per\-file durations, exit codes, aggregate timings, and optionally a
|
||||
microsecond\-precision load timeline.
|
||||
|
||||
The log entries are emitted one JSON object per line (not a single JSON array).
|
||||
The tool always "slurps" the file internally.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.BR --timeline
|
||||
Show a start\->end timeline for each loaded file with microsecond timestamps.
|
||||
.TP
|
||||
.BR --logfile = PATH
|
||||
Path to the NDJSON log file to analyze. Defaults to
|
||||
.IR ~/zshrc\-log.json .
|
||||
.SH ENVIRONMENT
|
||||
.TP
|
||||
.B SLOW_THRESHOLD
|
||||
Duration (ms) above which a module is flagged "slow". Default: 200.
|
||||
.TP
|
||||
.B WARN_THRESHOLD
|
||||
Duration (ms) above which a module is flagged "warning/very slow". Default: 500.
|
||||
.SH REQUIREMENTS
|
||||
The script relies on
|
||||
.BR jq (1),
|
||||
.BR awk (1),
|
||||
.BR column (1),
|
||||
and standard shell utilities.
|
||||
.SH FILES
|
||||
.TP
|
||||
.I ~/zshrc\-log.json
|
||||
Default NDJSON log consumed by
|
||||
.B dotfiles_doctor .
|
||||
Each line is a JSON object with keys:
|
||||
.BR file ,
|
||||
.BR label ,
|
||||
.BR start ,
|
||||
.BR end ,
|
||||
.BR duration_ms ,
|
||||
.BR exit_code .
|
||||
.SH EXIT STATUS
|
||||
Returns 0 on success; non\-zero on invocation errors (e.g. missing jq or log file).
|
||||
.SH EXAMPLES
|
||||
Analyze with defaults:
|
||||
.P
|
||||
.EX
|
||||
dotfiles_doctor
|
||||
.EE
|
||||
.P
|
||||
Show microsecond timeline:
|
||||
.P
|
||||
.EX
|
||||
dotfiles_doctor --timeline
|
||||
.EE
|
||||
.P
|
||||
Use a custom log and tighter thresholds:
|
||||
.P
|
||||
.EX
|
||||
SLOW_THRESHOLD=100 WARN_THRESHOLD=300 dotfiles_doctor \\
|
||||
--logfile="$HOME/tmp/startup.ndjson" --timeline
|
||||
.EE
|
||||
.SH SEE ALSO
|
||||
.BR jq (1),
|
||||
.BR zsh (1),
|
||||
.BR compinit (1)
|
||||
.SH AUTHOR
|
||||
Written for your dotfiles; completion and manpage generated by ChatGPT.
|
||||
@@ -0,0 +1,139 @@
|
||||
.TH FIND_DUPES 1 "December 2025" "find_dupes 1.0" "User Commands"
|
||||
.SH NAME
|
||||
find_dupes \- find duplicated lines across shell-related files
|
||||
.SH SYNOPSIS
|
||||
.B find_dupes
|
||||
.RI [ --maxdepth\ N | -d\ N ]
|
||||
.RI [ --regex\ PATTERN | -r\ PATTERN ]
|
||||
.RI [ --ignore-case | -i ]
|
||||
.SH DESCRIPTION
|
||||
.B find_dupes
|
||||
recursively searches for duplicated lines across files below the current
|
||||
directory. Only files with no extension, or with the extensions
|
||||
.BR .zsh ,
|
||||
.BR .sh ,
|
||||
.BR .zlogin
|
||||
or
|
||||
.BR .symlink
|
||||
are considered.
|
||||
|
||||
Lines that are comments (first non\-whitespace character is
|
||||
.BR # ),
|
||||
blank or whitespace\-only lines, and lines shorter than five characters
|
||||
are ignored.
|
||||
|
||||
The search is limited to a maximum directory recursion depth, and
|
||||
optionally to lines matching a supplied regular expression.
|
||||
.SH ARGUMENTS
|
||||
.TP
|
||||
.I maxdepth
|
||||
Maximum depth (integer) to pass to
|
||||
.BR find (1)
|
||||
via
|
||||
.B \-maxdepth .
|
||||
Depth 1 means the current directory only, 2 includes its immediate
|
||||
subdirectories, and so on.
|
||||
.TP
|
||||
.I regex
|
||||
An extended POSIX regular expression used to filter which lines are
|
||||
considered before duplicate detection. If omitted, all non\-comment,
|
||||
non\-blank lines of length at least five are considered.
|
||||
.SH OPTIONS
|
||||
.TP
|
||||
.BI --maxdepth\ N " , " -d\ N
|
||||
Set maximum recursion depth. Default is 3.
|
||||
|
||||
.TP
|
||||
.BI --regex\ PATTERN " , " -r\ PATTERN
|
||||
Filter lines using the given POSIX extended regular expression.
|
||||
|
||||
.TP
|
||||
.BR --ignore-case " , " -i
|
||||
Case-insensitive matching.
|
||||
.SH BEHAVIOUR
|
||||
The command operates in three phases:
|
||||
|
||||
.IP 1. 3
|
||||
Find all regular files under the current directory up to
|
||||
.I maxdepth
|
||||
whose names either have no extension or end in
|
||||
.BR .zsh ,
|
||||
.BR .sh ,
|
||||
.BR .zlogin
|
||||
or
|
||||
.BR .symlink .
|
||||
|
||||
.IP 2. 3
|
||||
For each file, run
|
||||
.BR grep (1)
|
||||
with the given
|
||||
.I regex
|
||||
and optional case\-insensitive flag, then use
|
||||
.BR awk (1)
|
||||
to:
|
||||
.RS
|
||||
.IP \(bu 2
|
||||
discard comment lines (first non\-whitespace character
|
||||
.BR # ),
|
||||
.IP \(bu 2
|
||||
discard blank or whitespace\-only lines,
|
||||
.IP \(bu 2
|
||||
discard lines whose length is less than five characters,
|
||||
.IP \(bu 2
|
||||
normalise line endings and remove stray carriage returns and non\-breaking spaces.
|
||||
.RE
|
||||
|
||||
.IP 3. 3
|
||||
All remaining lines are grouped by their content. Any line that appears
|
||||
in more than one file (or more than once across all scanned files) is
|
||||
printed together with the list of file paths in which it occurs.
|
||||
|
||||
.SH OUTPUT
|
||||
For each duplicated line, output is of the form:
|
||||
.PP
|
||||
.nf
|
||||
==== Duplicate line: "export DOTFILES=\\"${DOTFILES:-$HOME/.dotfiles}\\"" ====
|
||||
./zlogin.symlink
|
||||
./zshrc.symlink
|
||||
|
||||
==== Duplicate line: "export outfile=\\"${outfile:-$HOME/zshrc-log.json}\\"" ====
|
||||
./zlogin.symlink
|
||||
./zshrc.symlink
|
||||
.fi
|
||||
.SH EXIT STATUS
|
||||
.TP
|
||||
.B 0
|
||||
Command completed successfully (whether or not any duplicates were found).
|
||||
.TP
|
||||
.B 1
|
||||
Usage error (missing
|
||||
.I maxdepth
|
||||
or invalid arguments).
|
||||
.SH EXAMPLES
|
||||
.TP
|
||||
Search up to depth 3 for any duplicated non\-comment lines:
|
||||
.PP
|
||||
.nf
|
||||
find_dupes 3
|
||||
.fi
|
||||
.TP
|
||||
Search up to depth 4, restricting to lines containing "export" (case\-sensitive):
|
||||
.PP
|
||||
.nf
|
||||
find_dupes 4 'export'
|
||||
.fi
|
||||
.TP
|
||||
Search up to depth 2, restricting to "export" lines case\-insensitively:
|
||||
.PP
|
||||
.nf
|
||||
find_dupes 2 'export' --ignore-case
|
||||
.fi
|
||||
.SH FILES
|
||||
.TP
|
||||
.I ~/.dotfiles/bin/find_dupes
|
||||
Autoloaded Zsh function implementing this command.
|
||||
.SH SEE ALSO
|
||||
.BR zsh (1),
|
||||
.BR grep (1),
|
||||
.BR awk (1),
|
||||
.BR find (1)
|
||||
@@ -0,0 +1,10 @@
|
||||
return {
|
||||
[[]],
|
||||
[[██╗ ██████╗ ██████╗ █████╗ ██╗ ]],
|
||||
[[██║ ██╔═══██╗██╔════╝██╔══██╗██║ ]],
|
||||
[[██║ ██║ ██║██║ ███████║██║ ]],
|
||||
[[██║ ██║ ██║██║ ██╔══██║██║ ]],
|
||||
[[███████╗╚██████╔╝╚██████╗██║ ██║███████╗]],
|
||||
[[╚══════╝ ╚═════╝ ╚═════╝╚═╝ ╚═╝╚══════╝]],
|
||||
[[]],
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
return {
|
||||
[[███████╗██╗ ██╗ █████╗ ██████╗ ███╗ ███╗ ███╗ ███╗ ██████╗ ██████╗ ██╗]],
|
||||
[[██╔════╝██║ ██║██╔══██╗██╔══██╗████╗ ████║ ████╗ ████║██╔════╝ ██╔══██╗ ██║]],
|
||||
[[███████╗██║ █╗ ██║███████║██████╔╝██╔████╔██║█████╗██╔████╔██║██║ ███╗██████╔╝█████╗██║]],
|
||||
[[╚════██║██║███╗██║██╔══██║██╔══██╗██║╚██╔╝██║╚════╝██║╚██╔╝██║██║ ██║██╔══██╗╚════╝██║]],
|
||||
[[███████║╚███╔███╔╝██║ ██║██║ ██║██║ ╚═╝ ██║ ██║ ╚═╝ ██║╚██████╔╝██║ ██║ ██║]],
|
||||
[[╚══════╝ ╚══╝╚══╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═╝]],
|
||||
[[]],
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
return {
|
||||
[[███████╗██╗ ██╗ █████╗ ██████╗ ███╗ ███╗ ███╗ ███╗ ██████╗ ██████╗ ██████╗]],
|
||||
[[██╔════╝██║ ██║██╔══██╗██╔══██╗████╗ ████║ ████╗ ████║██╔════╝ ██╔══██╗ ╚════██╗]],
|
||||
[[███████╗██║ █╗ ██║███████║██████╔╝██╔████╔██║█████╗██╔████╔██║██║ ███╗██████╔╝█████╗ █████╔╝]],
|
||||
[[╚════██║██║███╗██║██╔══██║██╔══██╗██║╚██╔╝██║╚════╝██║╚██╔╝██║██║ ██║██╔══██╗╚════╝██╔═══╝ ]],
|
||||
[[███████║╚███╔███╔╝██║ ██║██║ ██║██║ ╚═╝ ██║ ██║ ╚═╝ ██║╚██████╔╝██║ ██║ ███████╗]],
|
||||
[[╚══════╝ ╚══╝╚══╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚══════╝]],
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
return {
|
||||
[[███████╗██╗ ██╗ █████╗ ██████╗ ███╗ ███╗ ███╗ ███╗ ██████╗ ██████╗ ██████╗ ]],
|
||||
[[██╔════╝██║ ██║██╔══██╗██╔══██╗████╗ ████║ ████╗ ████║██╔════╝ ██╔══██╗ ╚════██╗]],
|
||||
[[███████╗██║ █╗ ██║███████║██████╔╝██╔████╔██║█████╗██╔████╔██║██║ ███╗██████╔╝█████╗ █████╔╝]],
|
||||
[[╚════██║██║███╗██║██╔══██║██╔══██╗██║╚██╔╝██║╚════╝██║╚██╔╝██║██║ ██║██╔══██╗╚════╝ ╚═══██╗]],
|
||||
[[███████║╚███╔███╔╝██║ ██║██║ ██║██║ ╚═╝ ██║ ██║ ╚═╝ ██║╚██████╔╝██║ ██║ ██████╔╝]],
|
||||
[[╚══════╝ ╚══╝╚══╝ ╚═╝ ╚═╝╚═╝ ╚═╝╚═╝ ╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═╝ ╚═════╝ ]],
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
return {
|
||||
[[]],
|
||||
[[███╗ ██╗ █████╗ ███████╗███╗ ██╗ ██████╗ ]],
|
||||
[[████╗ ██║██╔══██╗██╔════╝████╗ ██║██╔════╝ ]],
|
||||
[[██╔██╗ ██║███████║███████╗██╔██╗ ██║██║ ███╗]],
|
||||
[[██║╚██╗██║██╔══██║╚════██║██║╚██╗██║██║ ██║]],
|
||||
[[██║ ╚████║██║ ██║███████║██║ ╚████║╚██████╔╝]],
|
||||
[[╚═╝ ╚═══╝╚═╝ ╚═╝╚══════╝╚═╝ ╚═══╝ ╚═════╝ ]],
|
||||
[[]]
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#export VIM=$HOME/.dotfiles/nvim
|
||||
#export VIMRUNTIME=$HOME/.config/nvim/runtime
|
||||
#export NVIM_SERVER="${XDG_CACHE_HOME:-$HOME/.cache}/nvim/server.socket"
|
||||
|
||||
# Don't clobber values already injected by ssh.conf or the environment.
|
||||
# On local: these default to LOCAL / 7777.
|
||||
# On remote hosts: ssh.conf injects NVIM_NAME=<host> and NVIM_SERVER=127.0.0.1:<port>
|
||||
# before the shell starts, so :- means those values are preserved.
|
||||
export NVIM_NAME="${NVIM_NAME:-LOCAL}"
|
||||
export NVIM_SERVER="${NVIM_SERVER:-127.0.0.1:7777}"
|
||||
export NVIM_BACKUP_DIR="$HOME/.local/share/nvim/backups"
|
||||
|
||||
# Stamp kitty window user vars so nvim_panel.py kitten can read host identity
|
||||
# without relying on os.environ (kittens run in an isolated subprocess).
|
||||
# Fires once on first prompt, then removes itself.
|
||||
if [[ "$LC_TERMINAL" == "kitty" ]] && command -v kitten >/dev/null 2>&1; then
|
||||
_stamp_kitty_nvim_vars() {
|
||||
kitten @ set-user-var NVIM_NAME "${NVIM_NAME}" 2>/dev/null
|
||||
kitten @ set-user-var NVIM_SERVER "${NVIM_SERVER}" 2>/dev/null
|
||||
add-zsh-hook -d precmd _stamp_kitty_nvim_vars
|
||||
}
|
||||
add-zsh-hook precmd _stamp_kitty_nvim_vars
|
||||
fi
|
||||
@@ -0,0 +1,52 @@
|
||||
filetype on
|
||||
filetype off
|
||||
|
||||
packloadall
|
||||
set autowrite
|
||||
set mouse=a
|
||||
|
||||
set number
|
||||
set relativenumber
|
||||
|
||||
" Brackets Highlighting Colors
|
||||
hi MatchParen cterm=none ctermbg=black ctermfg=white
|
||||
|
||||
" Errors/BadSpellings Higlighing Colors
|
||||
:highlight clear SpellBad
|
||||
|
||||
" CUSOR SETUP
|
||||
let &t_SI = "\<Esc>]50;CursorShape=1\x7"
|
||||
let &t_SR = "\<Esc>]50;CursorShape=5\x7"
|
||||
let &t_EI = "\<Esc>]50;CursorShape=6\x7"
|
||||
|
||||
" TAB SETUPS
|
||||
set tabstop=4 " The width of a TAB is set to 4.
|
||||
" Still it is a \t. It is just that
|
||||
" Vim will interpret it to be having
|
||||
" a width of 4.
|
||||
|
||||
set shiftwidth=4 " Indents will have a width of 4
|
||||
|
||||
set softtabstop=4
|
||||
set expandtab " Expand TABs to spaces
|
||||
|
||||
" Search
|
||||
set hlsearch "Highlight Search Result
|
||||
set ignorecase "Ignore case when searching
|
||||
set incsearch
|
||||
set smartcase "Automatically switch search to case-sensitive when search query contains an uppercase letter
|
||||
|
||||
set wildmenu
|
||||
set showmatch
|
||||
nnoremap gV `[v`]
|
||||
set t_Co=256 " This is may or may not needed.
|
||||
|
||||
set background=light
|
||||
colorscheme PaperColor
|
||||
|
||||
syntax enable
|
||||
set title
|
||||
set autoindent
|
||||
filetype plugin indent on
|
||||
set foldmethod=marker
|
||||
set nofoldenable
|
||||
@@ -0,0 +1,136 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
TARGET_USER="${1:-}"
|
||||
PORT="${2:-7777}"
|
||||
NVIM_BIN="${3:-}"
|
||||
|
||||
if [[ -z "$TARGET_USER" ]]; then
|
||||
echo "Usage: sudo $0 <target-user> [port] [nvim-bin]"
|
||||
echo 'Example: sudo ./deploy-nevoid.sh ngorse 7777 /home/ngorse/.local/share/mise/shims/nvim'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
if ! id "$TARGET_USER" >/dev/null 2>&1; then
|
||||
echo "User does not exist: $TARGET_USER" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
USER_HOME="$(getent passwd "$TARGET_USER" | cut -d: -f6)"
|
||||
USER_UID="$(id -u "$TARGET_USER")"
|
||||
USER_CONFIG_DIR="$USER_HOME/.config/systemd/user"
|
||||
|
||||
if [[ -z "$NVIM_BIN" ]]; then
|
||||
if [[ -x "/opt/usr/bin/nvim" ]]; then
|
||||
NVIM_BIN="/opt/usr/bin/nvim"
|
||||
elif sudo -u "$TARGET_USER" bash -lc 'command -v nvim >/dev/null 2>&1'; then
|
||||
NVIM_BIN="$(sudo -u "$TARGET_USER" bash -lc 'command -v nvim')"
|
||||
else
|
||||
echo "Could not find nvim for user $TARGET_USER." >&2
|
||||
echo "Pass the full path as the third argument." >&2
|
||||
exit 1
|
||||
fi
|
||||
fi
|
||||
|
||||
if [[ ! -x "$NVIM_BIN" ]]; then
|
||||
echo "nvim binary is not executable: $NVIM_BIN" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PROXY_BIN="/usr/lib/systemd/systemd-socket-proxyd"
|
||||
if [[ ! -x "$PROXY_BIN" ]]; then
|
||||
PROXY_BIN="$(command -v systemd-socket-proxyd || true)"
|
||||
fi
|
||||
|
||||
if [[ -z "$PROXY_BIN" || ! -x "$PROXY_BIN" ]]; then
|
||||
echo "Could not find systemd-socket-proxyd." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
mkdir -p "$USER_CONFIG_DIR"
|
||||
chown -R "$TARGET_USER:$TARGET_USER" "$USER_HOME/.config"
|
||||
|
||||
cat >"$USER_CONFIG_DIR/nevoid.socket" <<EOF
|
||||
[Unit]
|
||||
Description=Neovim RPC socket (on-demand) on 127.0.0.1:${PORT}
|
||||
|
||||
[Socket]
|
||||
ListenStream=127.0.0.1:${PORT}
|
||||
NoDelay=true
|
||||
SocketMode=0600
|
||||
Service=nevoid-proxy.service
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
EOF
|
||||
|
||||
cat >"$USER_CONFIG_DIR/nevoid-proxy.service" <<EOF
|
||||
[Unit]
|
||||
Description=Socket proxy: 127.0.0.1:${PORT} -> unix:%t/nevoid/nvim.sock
|
||||
After=nevoid-backend.service
|
||||
|
||||
[Service]
|
||||
ExecStartPre=/usr/bin/systemctl --user start nevoid-backend.service
|
||||
ExecStart=${PROXY_BIN} --exit-idle-time=600 %t/nevoid/nvim.sock
|
||||
|
||||
Restart=on-failure
|
||||
RestartSec=1s
|
||||
|
||||
NoNewPrivileges=yes
|
||||
PrivateTmp=yes
|
||||
ProtectSystem=strict
|
||||
ProtectHome=yes
|
||||
EOF
|
||||
|
||||
cat >"$USER_CONFIG_DIR/nevoid-backend.service" <<EOF
|
||||
[Unit]
|
||||
Description=Neovim RPC backend (headless)
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
|
||||
RuntimeDirectory=nevoid
|
||||
RuntimeDirectoryMode=0700
|
||||
|
||||
ExecStartPre=/bin/rm -f %t/nevoid/nvim.sock
|
||||
ExecStart=${NVIM_BIN} --headless --listen %t/nevoid/nvim.sock
|
||||
|
||||
Restart=on-failure
|
||||
RestartSec=1s
|
||||
|
||||
TimeoutStopSec=10s
|
||||
KillSignal=SIGTERM
|
||||
|
||||
NoNewPrivileges=yes
|
||||
PrivateTmp=yes
|
||||
ProtectSystem=strict
|
||||
ProtectHome=yes
|
||||
EOF
|
||||
|
||||
chown "$TARGET_USER:$TARGET_USER" \
|
||||
"$USER_CONFIG_DIR/nevoid.socket" \
|
||||
"$USER_CONFIG_DIR/nevoid-proxy.service" \
|
||||
"$USER_CONFIG_DIR/nevoid-backend.service"
|
||||
|
||||
loginctl enable-linger "$TARGET_USER"
|
||||
|
||||
USER_BUS_DIR="/run/user/$USER_UID"
|
||||
if [[ ! -d "$USER_BUS_DIR" ]]; then
|
||||
echo "Creating runtime systemd session by starting user@${USER_UID}.service"
|
||||
systemctl start "user@${USER_UID}.service"
|
||||
fi
|
||||
|
||||
sudo -u "$TARGET_USER" XDG_RUNTIME_DIR="/run/user/$USER_UID" systemctl --user daemon-reload
|
||||
sudo -u "$TARGET_USER" XDG_RUNTIME_DIR="/run/user/$USER_UID" systemctl --user reset-failed nevoid.socket nevoid-proxy.service nevoid-backend.service || true
|
||||
sudo -u "$TARGET_USER" XDG_RUNTIME_DIR="/run/user/$USER_UID" systemctl --user enable --now nevoid.socket
|
||||
|
||||
echo
|
||||
echo "Installed successfully for user: $TARGET_USER"
|
||||
echo "Port: $PORT"
|
||||
echo "nvim: $NVIM_BIN"
|
||||
echo
|
||||
echo "Check status with:"
|
||||
echo " sudo -u $TARGET_USER XDG_RUNTIME_DIR=/run/user/$USER_UID systemctl --user status nevoid.socket nevoid-proxy.service nevoid-backend.service"
|
||||
echo
|
||||
echo "Test locally on the host with:"
|
||||
echo " nc -vz 127.0.0.1 $PORT"
|
||||
@@ -0,0 +1,9 @@
|
||||
autoload -Uz edit-command-line
|
||||
zle -N edit-command-line
|
||||
function kitty_scrollback_edit_command_line() {
|
||||
local VISUAL='/Users/ngorse/.local/share/nvim/lazy/kitty-scrollback.nvim/scripts/edit_command_line.sh'
|
||||
zle edit-command-line
|
||||
zle kill-whole-line
|
||||
}
|
||||
zle -N kitty_scrollback_edit_command_line
|
||||
bindkey '^x^e' kitty_scrollback_edit_command_line
|
||||
@@ -0,0 +1,13 @@
|
||||
if [[ ! -d $HOME/powerlevel10k ]]; then
|
||||
git clone --depth=1 https://github.com/romkatv/powerlevel10k.git $HOME/powerlevel10k >/dev/null 2>&1
|
||||
fi
|
||||
|
||||
POWERLEVEL9K_DISABLE_CONFIGURATION_WIZARD=true
|
||||
call_file $HOME/powerlevel10k/powerlevel10k.zsh-theme "theme"
|
||||
|
||||
function toggle-right-prompt() { p10k display '*/right'=hide,show; }
|
||||
zle -N toggle-right-prompt
|
||||
bindkey '^B' toggle-right-prompt
|
||||
|
||||
# function hide-right-pyenv() { p10k display '1/right/pyenv'=hide; }
|
||||
# function show-right-pyenv() { p10k display '1/right/pyenv'=show; }
|
||||
@@ -0,0 +1,55 @@
|
||||
#!/usr/bin/env zsh
|
||||
# ===============================================================
|
||||
# zlogin — Post-Startup Finalisation & Summary
|
||||
# ===============================================================
|
||||
|
||||
# --- Ensure environment ---
|
||||
#export DOTFILES="${DOTFILES:-$HOME/.dotfiles}"
|
||||
|
||||
#export outfile="${outfile:-$HOME/zshrc-log.json}"
|
||||
#LOGFILE="$outfile"
|
||||
|
||||
if [[ ! `typeset -f call_file` ]]; then
|
||||
source "$HOME/.dotfiles/bin/call_file"
|
||||
fi
|
||||
|
||||
# --- Execute all module .zlogin files dynamically ---
|
||||
zlogin_files=($DOTFILES/**/*.zlogin)
|
||||
for file in ${zlogin_files[@]}; do
|
||||
call_file "${file}" "zlogin"
|
||||
done
|
||||
|
||||
# --- Post-login environment tweaks ---
|
||||
export PIP_DISABLE_PIP_VERSION_CHECK=1
|
||||
|
||||
# --- Clean duplicate PATH entries ---
|
||||
typeset -U path
|
||||
PATH=${(Rj/:/)${(us/:/)PATH}:#* *}
|
||||
export PATH
|
||||
|
||||
if [[ "${GHOSTTY_QUICK_TERMINAL}" == 1 ]]; then
|
||||
eval ${QT_RUN}
|
||||
fi
|
||||
|
||||
# --- Timing summary from JSON log ---
|
||||
# if [[ -f "$LOGFILE" ]]; then
|
||||
# echo
|
||||
# echo "⏱ Startup Summary:"
|
||||
# if command -v jq >/dev/null 2>&1; then
|
||||
# jq -r '. | [.label, .duration_ms, .status, .file] | @tsv' "$LOGFILE" \
|
||||
# | column -t -s $'\t'
|
||||
# else
|
||||
# echo "(Install jq to parse $LOGFILE)"
|
||||
# fi
|
||||
# fi
|
||||
|
||||
# # --- Optional compdump refresh ---
|
||||
# [[ -f "$DOTFILES/zsh/compdump.zlogin" ]] && call_file "$DOTFILES/zsh/compdump.zlogin" "compdump"
|
||||
|
||||
# # Run completion diagnostics if failures logged
|
||||
# if grep -q '"label":"compinit".*"exit_code":[1-9]' "$HOME/zshrc-log.json" 2>/dev/null; then
|
||||
# echo "⚠️ Re-running compinit_doctor due to prior failure..."
|
||||
# ZSH_COMPLETION_DEBUG=1 source "$DOTFILES/zsh/compinit_doctor.zsh"
|
||||
# fi
|
||||
# zprof
|
||||
|
||||
@@ -0,0 +1,2 @@
|
||||
export ZSH_COMPDUMP_AGE=7 # refresh cache every 7 days
|
||||
# export PATH="$PATH:/Users/ngorse/Library/Application Support/JetBrains/Toolbox/scripts"
|
||||
@@ -0,0 +1 @@
|
||||
zshenv.symlink
|
||||
@@ -0,0 +1,74 @@
|
||||
#!/usr/bin/env zsh
|
||||
# ===============================================================
|
||||
# zshrc — Dynamic Modular Config Loader with JSON Logging
|
||||
# ===============================================================
|
||||
|
||||
# Profiling
|
||||
[[ "$ZPROFRC" -ne 1 ]] || zmodload zsh/zprof
|
||||
alias zprofrc="ZPROFRC=1 zsh"
|
||||
typeset -g POWERLEVEL9K_INSTANT_PROMPT=false
|
||||
|
||||
# Ensure globals
|
||||
typeset -g DOTFILES="${DOTFILES:-$HOME/.dotfiles}"
|
||||
typeset -g outfile="${outfile:-$HOME/zshrc-log.json}"
|
||||
: > "$outfile" # clear old log
|
||||
|
||||
setopt EXTENDED_GLOB
|
||||
|
||||
if [[ ! `typeset -f call_file` ]]; then
|
||||
source "$DOTFILES/bin/call_file"
|
||||
fi
|
||||
|
||||
POWERLEVEL9K_INSTANT_PROMPT=true
|
||||
#if [[ -e "${HOME}/zsh-defer/zsh-defer.plugin.zsh" ]]; then
|
||||
# call_file ${HOME}/zsh-defer/zsh-defer.plugin.zsh
|
||||
#fi
|
||||
|
||||
|
||||
|
||||
# Use extended globbing syntax.
|
||||
|
||||
# load envrc from $HOME
|
||||
if [[ -a $HOME/.envrc ]];then
|
||||
call_file $HOME/.envrc "env"
|
||||
fi
|
||||
# load localrc from $HOME
|
||||
if [[ -a $HOME/.localrc ]];then
|
||||
call_file $HOME/.localrc "local"
|
||||
fi
|
||||
|
||||
if $POWERLEVEL9K_INSTANT_PROMPT; then
|
||||
if [[ -r "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" ]]; then
|
||||
call_file "${XDG_CACHE_HOME:-$HOME/.cache}/p10k-instant-prompt-${(%):-%n}.zsh" "instant"
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
unsetopt complete_aliases nobgnice
|
||||
typeset -U config_files
|
||||
config_files=($DOTFILES/**/*.zsh)
|
||||
|
||||
# --- PATH phase -------------------------------------------------
|
||||
for file in ${(M)config_files:#*/path.zsh}; do
|
||||
call_file "$file" "path"
|
||||
typeset -gU path
|
||||
done
|
||||
export -U PATH
|
||||
|
||||
# --- CONFIG phase ------------------------------------------------
|
||||
# exclude path.zsh, any *completion*.zsh, and the compinit driver itself
|
||||
for file in ${${${config_files:#*/path.zsh}:#*/*completion*.zsh}:#*/zsh/compinit.zsh}; do
|
||||
call_file "$file" # label auto-derives (dir for config.zsh, stem otherwise)
|
||||
done
|
||||
|
||||
# --- COMPLETION (cache-aware) -----------------------------------
|
||||
call_file "$DOTFILES/zsh/compinit.zsh" "compinit"
|
||||
|
||||
unset config_files
|
||||
|
||||
# Finish profiling by calling zprof.
|
||||
[[ "$ZPROFRC" -eq 1 ]] && zprof
|
||||
[[ -v ZPROFRC ]] && unset ZPROFRC
|
||||
|
||||
# Always return success
|
||||
true
|
||||
@@ -0,0 +1,85 @@
|
||||
# mask built-ins with better defaults
|
||||
alias ping='ping -c 5'
|
||||
if command -v nvim >/dev/null 2>&1; then
|
||||
alias vi=nvim
|
||||
alias vim=nvim
|
||||
fi
|
||||
alias grep="${aliases[grep]:-grep} --exclude-dir={.git,.vscode}"
|
||||
if command -v kitten >/dev/null 2>&1 && [[ -z "$SSH_CONNECTION" ]]; then
|
||||
alias s='foa(){ kitten ssh "$1" }; foa '
|
||||
elif command -v it2ssh >/dev/null 2>&1 && [[ -z "$SSH_CONNECTION" ]]; then
|
||||
alias s='foa(){ it2ssh "$1" }; foa '
|
||||
fi
|
||||
|
||||
# more ways to ls
|
||||
if command -v eza >/dev/null 2>&1; then
|
||||
alias ls='eza $eza_params'
|
||||
alias l='eza --git-ignore $eza_params'
|
||||
alias ll='eza --all --header --long $eza_params'
|
||||
alias llm='eza --all --header --long --sort=modified $eza_params'
|
||||
alias la='eza -lbhma --smart-group --hyperlink --icons=auto'
|
||||
alias lx='eza -lbhHigUmuSa@'
|
||||
alias lt='eza --tree $eza_params'
|
||||
alias tree='eza --tree $eza_params'
|
||||
elif command -v colorls >/dev/null 2>&1; then
|
||||
alias ls='colorls -A --sd --hyperlink --gs'
|
||||
alias ll='colorls -lA --sd'
|
||||
alias la='colorls -lAh --sd'
|
||||
fi
|
||||
alias lsa="ls -a"
|
||||
alias ldot='ls -ld .* --sd'
|
||||
|
||||
# disk usage
|
||||
alias df='df -H'
|
||||
# alias du='du -ch'
|
||||
# alias biggest='du -s ./* | sort -nr | awk '\''{print $2}'\'' | xargs du -sh'
|
||||
alias dux='du -x --max-depth=1 | sort -n'
|
||||
alias dud='du -d 1 -h'
|
||||
alias duf='du -sh *'
|
||||
|
||||
# misc
|
||||
alias please='sudo $(fc -ln -1)'
|
||||
alias zshrc='${EDITOR:-nvim} "${ZDOTDIR:-$HOME}"/.zshrc'
|
||||
alias zbench='for i in {1..10}; do /usr/bin/time zsh -lic exit; done'
|
||||
alias cls="clear && printf '\e[3J'"
|
||||
|
||||
# print things
|
||||
alias print-fpath='echo -e ${FPATH//:/\\n}'
|
||||
alias print-path='echo -e ${PATH//:/\\n}'
|
||||
alias print-functions='print -l ${(k)functions[(I)[^_]*]} | sort'
|
||||
|
||||
alias rl!='exec zsh'
|
||||
alias packages='${EDITOR:-vim} $DOTFILES/antidote/zsh_plugins.txt'
|
||||
alias mkcd='foo(){ mkdir -p "$1"; cd "$1" }; foo '
|
||||
alias touchs='foa(){ touch "$1"; '${EDITOR:-vim}' "$1" }; foa '
|
||||
# alias touchs='foa(){ touch "$1"; subl "$1" }; foa '
|
||||
alias e='$EDITOR'
|
||||
alias lsub='$EDITOR $_'
|
||||
alias vl='less $_'
|
||||
alias cv='cat $_'
|
||||
alias ppip='noglob python -m pip'
|
||||
alias d='dirs -v | head -10'
|
||||
alias 1='cd -'
|
||||
alias 2='cd -2'
|
||||
alias 3='cd -3'
|
||||
alias 4='cd -4'
|
||||
alias 5='cd -5'
|
||||
alias 6='cd -6'
|
||||
alias 7='cd -7'
|
||||
alias 8='cd -8'
|
||||
alias 9='cd -9'
|
||||
# alias pwhich='pyenv which $_'
|
||||
alias wget='wget -c'
|
||||
alias mkdir='mkdir -p'
|
||||
alias mount='mount |column -t'
|
||||
#alias -g ±=~
|
||||
alias sess='${EDITOR} $HOME/.config/kitty/sessions/${DCS_PROJ_NAME:?error}.session'
|
||||
# alias zup='zplug update && zplug load --compile'
|
||||
# alias zinstall='zplug install && zplug load --compile'
|
||||
#alias -s {zsh,yml,conf}=o_vim
|
||||
alias lg=lazygit
|
||||
alias pan_id="foo(){ kitty @ ls --match-tab=state:self --match='cmdline:^vim' | jq '.[0].tabs[0].id' }; foo"
|
||||
alias clear_ssh='kitty @ action close_shared_ssh_connections'
|
||||
alias -g NE='2> /dev/null'
|
||||
alias -g NS='>/dev/null'
|
||||
alias -g NN='>/dev/null 2>&1'
|
||||
@@ -0,0 +1,138 @@
|
||||
#!/usr/bin/env zsh
|
||||
# ==================================================================
|
||||
# compinit.zsh — cache-aware completion init
|
||||
# - Fast path: compinit -C -d <dump> (skip rescans)
|
||||
# - Stale path: source *completion*.zsh (+ local rc), then compinit -i
|
||||
# ==================================================================
|
||||
|
||||
|
||||
|
||||
# ensure these options before compinit
|
||||
setopt complete_aliases nobgnice
|
||||
|
||||
# dump location + TTL
|
||||
local _zcomp_loc="${XDG_CACHE_HOME:-$HOME/.cache}/zcomp/zcompdump"
|
||||
if [[ "${ZSH_COMPDUMP}" != "${_zcomp_loc}" ]]; then
|
||||
typeset -g -r ZSH_COMPDUMP="${_zcomp_loc}"
|
||||
fi
|
||||
local _zcd="${ZSH_COMPDUMP:-$HOME/.cache/zcomp/zcompdump}"
|
||||
|
||||
# Guard if file missing
|
||||
#[[ ! -r "$ZSH_COMPDUMP" ]] && {
|
||||
# print -u2 "comp_init: missing or unreadable: $ZSH_COMPDUMP"
|
||||
# return 1
|
||||
#}
|
||||
local ZCOMP_TTL_HOURS="${ZCOMP_TTL_HOURS:-20}"
|
||||
typeset -g ZCOMP_DUMP_PATH=${ZSH_COMPDUMP}
|
||||
|
||||
|
||||
# collect completion files from the already-built `config_files` list
|
||||
typeset -a _completion_files
|
||||
_completion_files=(${(M)${(@)config_files}:#*/*completion*.zsh})
|
||||
|
||||
# fallback if compinit.zsh is called standalone (no `config_files` in scope)
|
||||
(( ${#_completion_files} == 0 )) && _completion_files=($DOTFILES/**/*completion*.zsh(N))
|
||||
|
||||
# include local additions if present
|
||||
local _have_local_comp=0
|
||||
[[ -e "$HOME/.local_comp_rc" ]] && _have_local_comp=1
|
||||
|
||||
# fast if either dump or compiled dump fresher than TTL hours
|
||||
local _fast=0
|
||||
if [[ ${_zcd}(#qNmh-${ZCOMP_TTL_HOURS}) || ${_zcd}.zwc(#qNmh-${ZCOMP_TTL_HOURS}) ]]; then
|
||||
_fast=1
|
||||
fi
|
||||
|
||||
autoload -U compinit
|
||||
|
||||
if (( _fast )); then
|
||||
compinit -C -d "$_zcd"
|
||||
else
|
||||
(( _have_local_comp )) && source "$HOME/.local_comp_rc"
|
||||
local f
|
||||
for f in "${_completion_files[@]}"; do
|
||||
call_file "$f" "${f:h:t}"
|
||||
done
|
||||
compinit -i -d "$_zcd"
|
||||
{ zcompile "$_zcd" 2>/dev/null } &!
|
||||
fi
|
||||
|
||||
unset _zcd _completion_files _fast _have_local_comp ZCOMP_TTL_HOURS
|
||||
|
||||
_comp_options+=(globdots) # With hidden files
|
||||
if [[ ! $(command -v antidote) ]] && [[ -d $DOTFILES/oh-my-zsh ]]; then
|
||||
call_file $DOTFILES/oh-my-zsh/completion.zsh "omz-comp"
|
||||
fi
|
||||
|
||||
|
||||
|
||||
# +---------+
|
||||
# | Options |
|
||||
# +---------+
|
||||
|
||||
# setopt GLOB_COMPLETE # Show autocompletion menu with globs
|
||||
setopt MENU_COMPLETE # Automatically highlight first element of completion menu
|
||||
setopt AUTO_LIST # Automatically list choices on ambiguous completion.
|
||||
setopt COMPLETE_IN_WORD # Complete from both ends of a word.
|
||||
|
||||
# Define completers
|
||||
zstyle ':completion:*' completer _extensions _complete _dynamic_directory_name _ignored
|
||||
|
||||
# Use cache for commands using cache
|
||||
zstyle ':completion:*' use-cache on
|
||||
zstyle ':completion:*' cache-path ${ZSH_COMPDUMP:h}
|
||||
# Complete the alias when _expand_alias is used as a function
|
||||
zstyle ':completion:*' complete true
|
||||
|
||||
zle -C alias-expension complete-word _generic
|
||||
bindkey '^Xa' alias-expension
|
||||
zstyle ':completion:alias-expension:*' completer _expand_alias
|
||||
|
||||
# Use cache for commands which use it
|
||||
|
||||
# Allow you to select in a menu
|
||||
zstyle ':completion:*' menu select
|
||||
|
||||
# Autocomplete options for cd instead of directory stack
|
||||
zstyle ':completion:*' complete-options false
|
||||
|
||||
zstyle ':completion:*' file-sort modification
|
||||
|
||||
|
||||
zstyle ':completion:*:*:*:*:corrections' format '%F{yellow}!- %d (errors: %e) -!%f'
|
||||
zstyle ':completion:*:*:*:*:descriptions' format '%F{blue}-- %D %d --%f'
|
||||
zstyle ':completion:*:*:*:*:messages' format ' %F{purple} -- %d --%f'
|
||||
zstyle ':completion:*:*:*:*:warnings' format ' %F{red}-- no matches found --%f'
|
||||
# zstyle ':completion:*:default' list-prompt '%S%M matches%s'
|
||||
# Colors for files and directory
|
||||
zstyle ':completion:*:*:*:*:default' list-colors ${(s.:.)LS_COLORS}
|
||||
# Directories
|
||||
zstyle ':completion:*:default' list-colors ${(s.:.)LS_COLORS}
|
||||
zstyle ':completion:*:*:cd:*' tag-order local-directories directory-stack path-directories
|
||||
zstyle ':completion:*:*:cd:*:directory-stack' menu yes select
|
||||
zstyle ':completion:*:-tilde-:*' group-order 'dynamic-directory-name' 'named-directories' 'path-directories' 'users' 'expand'
|
||||
|
||||
zstyle ':completion:*' squeeze-slashes true
|
||||
|
||||
# Ignore useless commands and functions
|
||||
zstyle ':completion:*:functions' ignored-patterns '(_*|pre(cmd|exec)|prompt_*)'
|
||||
zstyle ':completion:*:' ignored-patterns '(_*|pre(cmd|exec)|prompt_*)'
|
||||
|
||||
# Only display some tags for the command cd
|
||||
zstyle ':completion:*:*:cd:*' tag-order local-directories directory-stack path-directories
|
||||
# zstyle ':completion:*:complete:git:argument-1:' tag-order !aliases
|
||||
|
||||
# Required for completion to be in good groups (named after the tags)
|
||||
zstyle ':completion:*' group-name ''
|
||||
|
||||
zstyle ':completion:*:*:-command-:*:*' group-order aliases builtins functions commands
|
||||
|
||||
# See ZSHCOMPWID "completion matching control"
|
||||
zstyle ':completion:*' matcher-list '' 'm:{a-zA-Z}={A-Za-z}' 'r:|[._-]=* r:|=*' 'l:|=* r:|=*'
|
||||
|
||||
zstyle ':completion:*' keep-prefix true
|
||||
|
||||
zstyle -e ':completion:*:(ssh|scp|sftp|rsh|rsync):hosts' hosts 'reply=(${=${${(f)"$(cat {/etc/ssh_,~/.ssh/known_}hosts(|2)(N) /dev/null)"}%%[# ]*}//,/ })'
|
||||
|
||||
compctl -V directories -K _bd bd
|
||||
|
||||