From fca3854be15aa07da495041622e9b93a2742a955 Mon Sep 17 00:00:00 2001 From: Oliver Date: Fri, 21 Nov 2025 13:06:22 +0100 Subject: [PATCH 1/4] feat(autocompletion): add autocomplete for cmr + create a file for autocomplete --- omz-custom/completion.sh | 76 +++++++++++++++++++++++++++++++++++ omz-custom/custom_commands.sh | 1 + omz-custom/tmux-sessions.sh | 21 ---------- 3 files changed, 77 insertions(+), 21 deletions(-) create mode 100644 omz-custom/completion.sh diff --git a/omz-custom/completion.sh b/omz-custom/completion.sh new file mode 100644 index 0000000..cbbde48 --- /dev/null +++ b/omz-custom/completion.sh @@ -0,0 +1,76 @@ +# autocomplete + +_cmr() { + local -a opts + opts=('--draft') + + # If completing an option + if [[ $words[CURRENT] == -* ]]; then + _describe 'options' opts + return + fi + + # Extract the full argument being completed (comma-separated) + local cur=$words[CURRENT] + local prefix last + prefix="${cur%,*}" + last="${cur##*,}" + + # Build GitLab query + local query="users?state=active&per_page=100" + local last_len=${#last} + + # Only use &search= when there are 3 or more characters + if (( last_len >= 3 )); then + query="${query}&search=${last}" + fi + + # Fetch usernames + local -a users + users=("${(@f)$(glab api "$query" 2>/dev/null | jq -r '.[].username')}") + + # If 1–2 chars, do local filtering over the full list + if (( last_len > 0 && last_len < 3 )); then + local -a filtered + local u + for u in "${users[@]}"; do + # substring match; change to == ${last}* for prefix-only + [[ $u == *${last}* ]] && filtered+="$u" + done + users=("${filtered[@]}") + fi + + # Build final completion items + local -a completions + if [[ "$prefix" != "$cur" ]]; then + prefix="${prefix}," + completions=("${prefix}${^users}") + else + completions=("${users[@]}") + fi + + _describe 'users' completions +} + +function _tat_autocomplete() { + local -a projects files dirs + local expl + + # Get tmuxinator projects + projects=(${(f)"$(tls)"}) + + # Get files and directories with proper completion + _alternative \ + 'projects:tmuxinator projects:compadd -a projects' \ + 'files:files:_files' +} + +# Register the completion function for zsh +if [[ -n ${ZSH_VERSION-} ]]; then + compdef _tat_autocomplete tat + compdef _cmr cmr +else + # Fallback for bash + complete -f -F _tat_autocomplete -o default tat + complete -f -F _cmr -o default tat +fi diff --git a/omz-custom/custom_commands.sh b/omz-custom/custom_commands.sh index 8d13a1f..194ce7e 100755 --- a/omz-custom/custom_commands.sh +++ b/omz-custom/custom_commands.sh @@ -404,3 +404,4 @@ function gpla() { # source the tmux-sessions.sh in the same folder source $(dirname "$0")/tmux-sessions.sh +source $(dirname "$0")/completion.sh diff --git a/omz-custom/tmux-sessions.sh b/omz-custom/tmux-sessions.sh index f598769..43ca731 100644 --- a/omz-custom/tmux-sessions.sh +++ b/omz-custom/tmux-sessions.sh @@ -33,24 +33,3 @@ function tat() { function tls() { tmuxinator list | grep -v tmuxinator | tr ' ' '\n' | grep -v '^$' } - -function _tat_autocomplete() { - local -a projects files dirs - local expl - - # Get tmuxinator projects - projects=(${(f)"$(tls)"}) - - # Get files and directories with proper completion - _alternative \ - 'projects:tmuxinator projects:compadd -a projects' \ - 'files:files:_files' -} - -# Register the completion function for zsh -if [[ -n ${ZSH_VERSION-} ]]; then - compdef _tat_autocomplete tat -else - # Fallback for bash - complete -f -F _tat_autocomplete -o default tat -fi From 599006f288b119c050231b242112012f8febf0be Mon Sep 17 00:00:00 2001 From: Oliver Date: Fri, 21 Nov 2025 13:17:46 +0100 Subject: [PATCH 2/4] fix(autocompletion): fix autocomplete for cmr for multiple users --- omz-custom/completion.sh | 36 +++++++++++++++++++++--------------- 1 file changed, 21 insertions(+), 15 deletions(-) diff --git a/omz-custom/completion.sh b/omz-custom/completion.sh index cbbde48..1172d3a 100644 --- a/omz-custom/completion.sh +++ b/omz-custom/completion.sh @@ -1,26 +1,40 @@ # autocomplete _cmr() { + emulate -L zsh + setopt extended_glob + local -a opts opts=('--draft') - # If completing an option + # Complete options if [[ $words[CURRENT] == -* ]]; then _describe 'options' opts return fi - # Extract the full argument being completed (comma-separated) + # Full current "word" at cursor (may contain commas) local cur=$words[CURRENT] + + # Split into prefix (before last comma) and fragment being typed (after last comma) local prefix last prefix="${cur%,*}" last="${cur##*,}" + local last_len=${#last} + + # Tell zsh: "this part is fixed, don't match on it" + if [[ "$prefix" != "$cur" ]]; then + IPREFIX="${prefix}," + PREFIX="$last" + else + IPREFIX="" + PREFIX="$cur" + fi # Build GitLab query local query="users?state=active&per_page=100" - local last_len=${#last} - # Only use &search= when there are 3 or more characters + # Only use &search= when fragment has 3+ chars if (( last_len >= 3 )); then query="${query}&search=${last}" fi @@ -29,7 +43,7 @@ _cmr() { local -a users users=("${(@f)$(glab api "$query" 2>/dev/null | jq -r '.[].username')}") - # If 1–2 chars, do local filtering over the full list + # If 1–2 chars, filter locally on the full list if (( last_len > 0 && last_len < 3 )); then local -a filtered local u @@ -40,16 +54,8 @@ _cmr() { users=("${filtered[@]}") fi - # Build final completion items - local -a completions - if [[ "$prefix" != "$cur" ]]; then - prefix="${prefix}," - completions=("${prefix}${^users}") - else - completions=("${users[@]}") - fi - - _describe 'users' completions + # Now just offer the usernames; zsh will prepend $IPREFIX automatically + compadd -Q -- "${users[@]}" } function _tat_autocomplete() { From 100c6034ba1f0531c22e72668239eee1a65fb76d Mon Sep 17 00:00:00 2001 From: Oliver Date: Fri, 21 Nov 2025 14:20:56 +0100 Subject: [PATCH 3/4] fix(autocompletion): store usernames locally for fast lookup --- omz-custom/completion.sh | 58 +++++++++++++++++++++++++++------------- 1 file changed, 39 insertions(+), 19 deletions(-) diff --git a/omz-custom/completion.sh b/omz-custom/completion.sh index 1172d3a..997a80f 100644 --- a/omz-custom/completion.sh +++ b/omz-custom/completion.sh @@ -1,5 +1,27 @@ # autocomplete +# Where to cache usernames (override with $CMR_USER_CACHE if you want) +: ${CMR_USER_CACHE:="$HOME/.cache/cmr_users"} + +cmr_update_users() { + local cache="$CMR_USER_CACHE" + local dir="${cache:h}" # directory part + + mkdir -p -- "$dir" + + echo "Updating cmr user cache in $cache..." >&2 + + # Fetch ALL active users (using --paginate for multiple pages) + glab api "users?state=active&per_page=100" --paginate \ + | jq -r '.[].username' \ + | sort -u > "$cache" + + local count + count=$(wc -l < "$cache" | tr -d ' ') + echo "Stored $count usernames in $cache" >&2 +} + + _cmr() { emulate -L zsh setopt extended_glob @@ -13,16 +35,25 @@ _cmr() { return fi - # Full current "word" at cursor (may contain commas) + # Load cached users + : ${CMR_USER_CACHE:="$HOME/.cache/cmr_users"} + local -a users + if [[ -r "$CMR_USER_CACHE" ]]; then + users=("${(@f)$(< "$CMR_USER_CACHE")}") + else + # No cache yet → nothing to complete + return 0 + fi + + # Full current "word" (may contain commas) local cur=$words[CURRENT] # Split into prefix (before last comma) and fragment being typed (after last comma) local prefix last prefix="${cur%,*}" last="${cur##*,}" - local last_len=${#last} - # Tell zsh: "this part is fixed, don't match on it" + # Tell zsh what is fixed and what is being completed if [[ "$prefix" != "$cur" ]]; then IPREFIX="${prefix}," PREFIX="$last" @@ -31,30 +62,19 @@ _cmr() { PREFIX="$cur" fi - # Build GitLab query - local query="users?state=active&per_page=100" - - # Only use &search= when fragment has 3+ chars - if (( last_len >= 3 )); then - query="${query}&search=${last}" - fi - - # Fetch usernames - local -a users - users=("${(@f)$(glab api "$query" 2>/dev/null | jq -r '.[].username')}") - - # If 1–2 chars, filter locally on the full list - if (( last_len > 0 && last_len < 3 )); then + # Filter users by the fragment (substring match) + if [[ -n "$last" ]]; then local -a filtered local u for u in "${users[@]}"; do - # substring match; change to == ${last}* for prefix-only [[ $u == *${last}* ]] && filtered+="$u" done users=("${filtered[@]}") fi - # Now just offer the usernames; zsh will prepend $IPREFIX automatically + (( ${#users} == 0 )) && return 0 + + # Offer usernames; zsh will prefix with $IPREFIX compadd -Q -- "${users[@]}" } From bcb233301751f86e32e49d6bd4dfde03d8fd67cf Mon Sep 17 00:00:00 2001 From: Oliver Date: Fri, 21 Nov 2025 14:24:51 +0100 Subject: [PATCH 4/4] fix(autocompletion): run cmr_update_users if no users in cache --- omz-custom/completion.sh | 30 ++++++++++++++++-------------- 1 file changed, 16 insertions(+), 14 deletions(-) diff --git a/omz-custom/completion.sh b/omz-custom/completion.sh index 997a80f..2d8a7df 100644 --- a/omz-custom/completion.sh +++ b/omz-custom/completion.sh @@ -1,6 +1,5 @@ # autocomplete -# Where to cache usernames (override with $CMR_USER_CACHE if you want) : ${CMR_USER_CACHE:="$HOME/.cache/cmr_users"} cmr_update_users() { @@ -29,31 +28,35 @@ _cmr() { local -a opts opts=('--draft') - # Complete options + # Option completion if [[ $words[CURRENT] == -* ]]; then _describe 'options' opts return fi - # Load cached users + # Cache path : ${CMR_USER_CACHE:="$HOME/.cache/cmr_users"} - local -a users - if [[ -r "$CMR_USER_CACHE" ]]; then - users=("${(@f)$(< "$CMR_USER_CACHE")}") - else - # No cache yet → nothing to complete - return 0 + + # If cache missing → auto-update once + if [[ ! -r "$CMR_USER_CACHE" ]]; then + # Show a short message only once per shell session + print -r -- "cmr: user cache missing, updating..." >&2 + cmr_update_users fi - # Full current "word" (may contain commas) + # Load cached users + local -a users + users=("${(@f)$(< "$CMR_USER_CACHE")}") + + # Full current "word" at cursor local cur=$words[CURRENT] - # Split into prefix (before last comma) and fragment being typed (after last comma) + # Split into prefix (before last comma) + fragment after last comma local prefix last prefix="${cur%,*}" last="${cur##*,}" - # Tell zsh what is fixed and what is being completed + # Tell zsh what is fixed vs typed fragment if [[ "$prefix" != "$cur" ]]; then IPREFIX="${prefix}," PREFIX="$last" @@ -62,7 +65,7 @@ _cmr() { PREFIX="$cur" fi - # Filter users by the fragment (substring match) + # Substring filter locally if [[ -n "$last" ]]; then local -a filtered local u @@ -74,7 +77,6 @@ _cmr() { (( ${#users} == 0 )) && return 0 - # Offer usernames; zsh will prefix with $IPREFIX compadd -Q -- "${users[@]}" }