mirror of
https://github.com/go-task/task.git
synced 2026-07-01 16:44:34 +00:00
feat(completion): unify shell wrappers behind task __complete
This commit is contained in:
@@ -1,171 +1,65 @@
|
||||
#compdef task
|
||||
typeset -A opt_args
|
||||
#
|
||||
# Thin wrapper around `task __complete`. All suggestion logic lives in the
|
||||
# Go engine — do not add completion logic here.
|
||||
|
||||
TASK_CMD="${TASK_EXE:-task}"
|
||||
compdef _task "$TASK_CMD"
|
||||
|
||||
_GO_TASK_COMPLETION_LIST_OPTION="${GO_TASK_COMPLETION_LIST_OPTION:---list-all}"
|
||||
|
||||
# Check if an experiment is enabled
|
||||
function __task_is_experiment_enabled() {
|
||||
local experiment=$1
|
||||
task --experiments 2>/dev/null | grep -q "^\* ${experiment}:.*on"
|
||||
}
|
||||
|
||||
# Listing commands from Taskfile.yml
|
||||
function __task_list() {
|
||||
local -a scripts cmd task_aliases match mbegin mend
|
||||
local -i enabled=0
|
||||
local taskfile item task desc task_alias
|
||||
|
||||
cmd=($TASK_CMD)
|
||||
taskfile=${(Qv)opt_args[(i)-t|--taskfile]}
|
||||
taskfile=${taskfile//\~/$HOME}
|
||||
|
||||
for arg in "${words[@]:0:$CURRENT}"; do
|
||||
if [[ "$arg" = "--" ]]; then
|
||||
# Use default completion for words after `--` as they are CLI_ARGS.
|
||||
_default
|
||||
return 0
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ -n "$taskfile" && -f "$taskfile" ]]; then
|
||||
cmd+=(--taskfile "$taskfile")
|
||||
fi
|
||||
|
||||
# Check if global flag is set
|
||||
if (( ${+opt_args[-g]} || ${+opt_args[--global]} )); then
|
||||
cmd+=(--global)
|
||||
fi
|
||||
|
||||
if output=$("${cmd[@]}" $_GO_TASK_COMPLETION_LIST_OPTION 2>/dev/null); then
|
||||
enabled=1
|
||||
fi
|
||||
|
||||
(( enabled )) || return 0
|
||||
|
||||
scripts=()
|
||||
|
||||
# Read zstyle verbose option (default = true via -T)
|
||||
local show_desc
|
||||
zstyle -T ":completion:${curcontext}:" verbose && show_desc=true || show_desc=false
|
||||
|
||||
# Read zstyle show-aliases option (default = true via -T)
|
||||
local show_aliases
|
||||
zstyle -T ":completion:${curcontext}:" show-aliases && show_aliases=true || show_aliases=false
|
||||
|
||||
for item in "${(@)${(f)output}[2,-1]#\* }"; do
|
||||
task="${item%%:[[:space:]]*}"
|
||||
|
||||
# Extract the aliases listed in the trailing "(aliases: a, b)" column.
|
||||
# NB: `aliases` is a reserved zsh parameter, so use a different name.
|
||||
task_aliases=()
|
||||
if [[ "$show_aliases" == "true" && "$item" == (#b)*'(aliases: '(*)')' ]]; then
|
||||
task_aliases=( "${(@s:, :)match[1]}" )
|
||||
fi
|
||||
|
||||
if [[ "$show_desc" == "true" ]]; then
|
||||
local desc="${item##[^[:space:]]##[[:space:]]##}"
|
||||
scripts+=( "${task//:/\\:}:$desc" )
|
||||
for task_alias in $task_aliases; do
|
||||
scripts+=( "${task_alias//:/\\:}:$desc (alias of $task)" )
|
||||
done
|
||||
else
|
||||
scripts+=( "$task" )
|
||||
for task_alias in $task_aliases; do
|
||||
scripts+=( "$task_alias" )
|
||||
done
|
||||
fi
|
||||
done
|
||||
|
||||
if [[ "$show_desc" == "true" ]]; then
|
||||
_describe 'Task to run' scripts
|
||||
else
|
||||
compadd -Q -a scripts
|
||||
fi
|
||||
}
|
||||
|
||||
_task() {
|
||||
local -a standard_args operation_args
|
||||
local -a args lines completions opts
|
||||
local output directive line
|
||||
|
||||
standard_args=(
|
||||
'(-C --concurrency)'{-C,--concurrency}'[limit number of concurrent tasks]: '
|
||||
'(-p --parallel)'{-p,--parallel}'[run command-line tasks in parallel]'
|
||||
'(-F --failfast)'{-F,--failfast}'[when running tasks in parallel, stop all tasks if one fails]'
|
||||
'(-f --force)'{-f,--force}'[run even if task is up-to-date]'
|
||||
'(-c --color)'{-c,--color}'[colored output]'
|
||||
'(--completion)--completion[generate shell completion script]:shell:(bash zsh fish powershell)'
|
||||
'(-d --dir)'{-d,--dir}'[dir to run in]:execution dir:_dirs'
|
||||
'(--disable-fuzzy)--disable-fuzzy[disable fuzzy matching for task names]'
|
||||
'(-n --dry)'{-n,--dry}'[compiles and prints tasks without executing]'
|
||||
'(--dry)--dry[dry-run mode, compile and print tasks only]'
|
||||
'(-x --exit-code)'{-x,--exit-code}'[pass-through exit code of task command]'
|
||||
'(--experiments)--experiments[list available experiments]'
|
||||
'(-g --global)'{-g,--global}'[run global Taskfile from home directory]'
|
||||
'(--insecure)--insecure[allow insecure Taskfile downloads]'
|
||||
'(-I --interval)'{-I,--interval}'[interval to watch for changes]:duration: '
|
||||
'(-j --json)'{-j,--json}'[format task list as JSON]'
|
||||
'(--nested)--nested[nest namespaces when listing as JSON]'
|
||||
'(--no-status)--no-status[ignore status when listing as JSON]'
|
||||
'(--interactive)--interactive[prompt for missing required variables]'
|
||||
'(-o --output)'{-o,--output}'[set output style]:style:(interleaved group prefixed)'
|
||||
'(--output-group-begin)--output-group-begin[message template before grouped output]:template text: '
|
||||
'(--output-group-end)--output-group-end[message template after grouped output]:template text: '
|
||||
'(--output-group-error-only)--output-group-error-only[hide output from successful tasks]'
|
||||
'(-s --silent)'{-s,--silent}'[disable echoing]'
|
||||
'(--sort)--sort[set task sorting order]:order:(default alphanumeric none)'
|
||||
'(--status)--status[exit non-zero if supplied tasks not up-to-date]'
|
||||
'(--summary)--summary[show summary\: field from tasks instead of running them]'
|
||||
'(-t --taskfile)'{-t,--taskfile}'[specify a different taskfile]:taskfile:_files'
|
||||
'(-v --verbose)'{-v,--verbose}'[verbose mode]'
|
||||
'(-w --watch)'{-w,--watch}'[watch-mode for given tasks, re-run when inputs change]'
|
||||
'(-y --yes)'{-y,--yes}'[assume yes to all prompts]'
|
||||
)
|
||||
# (@) preserves a trailing empty string, which the engine relies on to
|
||||
# know the cursor is on a fresh word.
|
||||
args=("${(@)words[2,CURRENT]}")
|
||||
(( ${#args} == 0 )) && args=("")
|
||||
|
||||
# Experimental flags (dynamically added based on enabled experiments)
|
||||
# Options (modify behavior)
|
||||
if __task_is_experiment_enabled "GENTLE_FORCE"; then
|
||||
standard_args+=('(--force-all)--force-all[force execution of task and all dependencies]')
|
||||
output=$("$TASK_CMD" __complete "${args[@]}" 2>/dev/null)
|
||||
if [[ -z "$output" ]]; then
|
||||
_files
|
||||
return
|
||||
fi
|
||||
|
||||
if __task_is_experiment_enabled "REMOTE_TASKFILES"; then
|
||||
standard_args+=(
|
||||
'(--offline --download)--offline[use only local or cached Taskfiles]'
|
||||
'(--timeout)--timeout[timeout for remote Taskfile downloads]:duration: '
|
||||
'(--expiry)--expiry[cache expiry duration]:duration: '
|
||||
'(--remote-cache-dir)--remote-cache-dir[directory to cache remote Taskfiles]:cache dir:_dirs'
|
||||
'(--cacert)--cacert[custom CA certificate for TLS]:file:_files'
|
||||
'(--cert)--cert[client certificate for mTLS]:file:_files'
|
||||
'(--cert-key)--cert-key[client certificate private key]:file:_files'
|
||||
)
|
||||
lines=("${(f)output}")
|
||||
directive="${lines[-1]#:}"
|
||||
lines=("${(@)lines[1,-2]}")
|
||||
|
||||
if (( directive & 8 )); then
|
||||
local -a globs
|
||||
for line in "${lines[@]}"; do
|
||||
globs+=("*.${line}")
|
||||
done
|
||||
_files -g "(${(j:|:)globs})"
|
||||
return
|
||||
fi
|
||||
|
||||
operation_args=(
|
||||
# Task names completion (can be specified multiple times)
|
||||
'(operation)*: :__task_list'
|
||||
# Operational args completion (mutually exclusive)
|
||||
+ '(operation)'
|
||||
'(*)'{-l,--list}'[list describable tasks]'
|
||||
'(*)'{-a,--list-all}'[list all tasks]'
|
||||
'(*)'{-i,--init}'[create new Taskfile.yml]'
|
||||
'(- *)'{-h,--help}'[show help]'
|
||||
'(- *)--version[show version and exit]'
|
||||
)
|
||||
|
||||
# Experimental operations (dynamically added based on enabled experiments)
|
||||
if __task_is_experiment_enabled "REMOTE_TASKFILES"; then
|
||||
standard_args+=(
|
||||
'(--offline --clear-cache)--download[download remote Taskfile]'
|
||||
)
|
||||
operation_args+=(
|
||||
'(* --download)--clear-cache[clear remote Taskfile cache]'
|
||||
)
|
||||
if (( directive & 16 )); then
|
||||
_path_files -/
|
||||
return
|
||||
fi
|
||||
|
||||
_arguments -S $standard_args $operation_args
|
||||
# `:` inside the value must be escaped: _describe splits on the first
|
||||
# unescaped colon (e.g. "docs:serve" would otherwise become value "docs").
|
||||
local value desc
|
||||
for line in "${lines[@]}"; do
|
||||
if [[ "$line" == *$'\t'* ]]; then
|
||||
value="${line%%$'\t'*}"
|
||||
desc="${line#*$'\t'}"
|
||||
completions+=("${value//:/\\:}:$desc")
|
||||
else
|
||||
completions+=("${line//:/\\:}")
|
||||
fi
|
||||
done
|
||||
|
||||
(( directive & 2 )) && opts+=(-S '')
|
||||
(( directive & 32 )) && opts+=(-V)
|
||||
|
||||
if (( ${#completions} > 0 )); then
|
||||
_describe -t tasks 'task' completions "${opts[@]}"
|
||||
fi
|
||||
|
||||
(( directive & 4 )) && return
|
||||
_files
|
||||
}
|
||||
|
||||
# don't run the completion function when being source-ed or eval-ed
|
||||
if [ "$funcstack[1]" = "_task" ]; then
|
||||
_task "$@"
|
||||
fi
|
||||
compdef _task "$TASK_CMD"
|
||||
|
||||
Reference in New Issue
Block a user