mirror of
https://github.com/go-task/task.git
synced 2026-07-01 08:34:19 +00:00
feat(completion): unify shell wrappers behind task __complete
This commit is contained in:
@@ -1,60 +1,69 @@
|
||||
# vim: set tabstop=2 shiftwidth=2 expandtab:
|
||||
#
|
||||
# Thin wrapper around `task __complete`. All suggestion logic lives in the
|
||||
# Go engine — do not add completion logic here.
|
||||
|
||||
_GO_TASK_COMPLETION_LIST_OPTION='--list-all'
|
||||
TASK_CMD="${TASK_EXE:-task}"
|
||||
|
||||
function _task()
|
||||
{
|
||||
_task() {
|
||||
local cur prev words cword
|
||||
_init_completion -n : || return
|
||||
|
||||
# Check for `--` within command-line and quit or strip suffix.
|
||||
local i
|
||||
for i in "${!words[@]}"; do
|
||||
if [ "${words[$i]}" == "--" ]; then
|
||||
# Do not complete words following `--` passed to CLI_ARGS.
|
||||
[ $cword -gt $i ] && return
|
||||
# Remove the words following `--` to not put --list in CLI_ARGS.
|
||||
words=( "${words[@]:0:$i}" )
|
||||
break
|
||||
fi
|
||||
local -a args=()
|
||||
if (( cword > 0 )); then
|
||||
args=( "${words[@]:1:cword}" )
|
||||
fi
|
||||
if (( ${#args[@]} == 0 )); then
|
||||
args=( "" )
|
||||
fi
|
||||
|
||||
local output
|
||||
output=$("$TASK_CMD" __complete "${args[@]}" 2>/dev/null)
|
||||
if [[ -z "$output" ]]; then
|
||||
_filedir
|
||||
return
|
||||
fi
|
||||
|
||||
local -a lines=()
|
||||
local line
|
||||
while IFS= read -r line; do
|
||||
lines+=( "$line" )
|
||||
done <<< "$output"
|
||||
|
||||
local last_idx=$(( ${#lines[@]} - 1 ))
|
||||
local directive="${lines[$last_idx]#:}"
|
||||
unset 'lines[$last_idx]'
|
||||
|
||||
if (( directive & 8 )); then
|
||||
local exts=""
|
||||
for line in "${lines[@]}"; do
|
||||
exts+="${exts:+|}$line"
|
||||
done
|
||||
_filedir "@($exts)"
|
||||
return
|
||||
fi
|
||||
|
||||
if (( directive & 16 )); then
|
||||
_filedir -d
|
||||
return
|
||||
fi
|
||||
|
||||
local -a values=()
|
||||
for line in "${lines[@]}"; do
|
||||
values+=( "${line%%$'\t'*}" )
|
||||
done
|
||||
|
||||
# Handle special arguments of options.
|
||||
case "$prev" in
|
||||
-d|--dir|--remote-cache-dir)
|
||||
_filedir -d
|
||||
return $?
|
||||
;;
|
||||
--cacert|--cert|--cert-key)
|
||||
_filedir
|
||||
return $?
|
||||
;;
|
||||
-t|--taskfile)
|
||||
_filedir yaml || return $?
|
||||
_filedir yml
|
||||
return $?
|
||||
;;
|
||||
-o|--output)
|
||||
COMPREPLY=( $( compgen -W "interleaved group prefixed" -- $cur ) )
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
COMPREPLY=( $( compgen -W "${values[*]}" -- "$cur" ) )
|
||||
|
||||
# Handle normal options.
|
||||
case "$cur" in
|
||||
-*)
|
||||
COMPREPLY=( $( compgen -W "$(_parse_help $1)" -- $cur ) )
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
if (( directive & 2 )); then
|
||||
compopt -o nospace 2>/dev/null
|
||||
fi
|
||||
|
||||
# Prepare task name completions.
|
||||
local tasks=( $( "${words[@]}" --silent $_GO_TASK_COMPLETION_LIST_OPTION 2> /dev/null ) )
|
||||
COMPREPLY=( $( compgen -W "${tasks[*]}" -- "$cur" ) )
|
||||
|
||||
# Post-process because task names might contain colons.
|
||||
__ltrim_colon_completions "$cur"
|
||||
|
||||
if (( ${#COMPREPLY[@]} == 0 )) && ! (( directive & 4 )); then
|
||||
_filedir
|
||||
fi
|
||||
}
|
||||
|
||||
complete -F _task "$TASK_CMD"
|
||||
|
||||
@@ -1,120 +1,46 @@
|
||||
# Thin wrapper around `task __complete`. All suggestion logic lives in the
|
||||
# Go engine — do not add completion logic here.
|
||||
|
||||
set -l GO_TASK_PROGNAME (if set -q GO_TASK_PROGNAME; echo $GO_TASK_PROGNAME; else if set -q TASK_EXE; echo $TASK_EXE; else; echo task; end)
|
||||
|
||||
# Cache variables for experiments (global)
|
||||
set -g __task_experiments_cache ""
|
||||
set -g __task_experiments_cache_time 0
|
||||
function __task_complete --inherit-variable GO_TASK_PROGNAME
|
||||
set -l tokens (commandline -opc)
|
||||
set -l current (commandline -ct)
|
||||
set -l args
|
||||
if test (count $tokens) -gt 1
|
||||
set args $tokens[2..-1]
|
||||
end
|
||||
set args $args $current
|
||||
|
||||
# Helper function to get experiments with 1-second cache
|
||||
function __task_get_experiments --inherit-variable GO_TASK_PROGNAME
|
||||
set -l now (date +%s)
|
||||
set -l ttl 1 # Cache for 1 second only
|
||||
|
||||
# Return cached value if still valid
|
||||
if test (math "$now - $__task_experiments_cache_time") -lt $ttl
|
||||
printf '%s\n' $__task_experiments_cache
|
||||
set -l output ($GO_TASK_PROGNAME __complete $args 2>/dev/null)
|
||||
set -l count (count $output)
|
||||
if test $count -eq 0
|
||||
return
|
||||
end
|
||||
|
||||
# Refresh cache
|
||||
set -g __task_experiments_cache ($GO_TASK_PROGNAME --experiments 2>/dev/null)
|
||||
set -g __task_experiments_cache_time $now
|
||||
printf '%s\n' $__task_experiments_cache
|
||||
end
|
||||
|
||||
# Helper function to check if an experiment is enabled
|
||||
function __task_is_experiment_enabled
|
||||
set -l experiment $argv[1]
|
||||
__task_get_experiments | string match -qr "^\* $experiment:.*on"
|
||||
end
|
||||
|
||||
function __task_get_tasks --description "Prints all available tasks with their description" --inherit-variable GO_TASK_PROGNAME
|
||||
# Check if the global task is requested
|
||||
set -l global_task false
|
||||
commandline --current-process | read --tokenize --list --local cmd_args
|
||||
for arg in $cmd_args
|
||||
if test "_$arg" = "_--"
|
||||
break # ignore arguments to be passed to the task
|
||||
set -l last $output[$count]
|
||||
if not string match -q ':*' -- $last
|
||||
# Protocol violation: emit raw lines as a fallback.
|
||||
for line in $output
|
||||
echo $line
|
||||
end
|
||||
return
|
||||
end
|
||||
if test "_$arg" = "_--global" -o "_$arg" = "_-g"
|
||||
set global_task true
|
||||
break
|
||||
|
||||
set -l directive (string replace -r '^:' '' -- $last)
|
||||
# FilterFileExt / FilterDirs are handled by fish's native file completion
|
||||
# via the separate `complete` registrations below.
|
||||
if test (math "$directive & 8") -ne 0; or test (math "$directive & 16") -ne 0
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
# Read the list of tasks (and potential errors)
|
||||
if $global_task
|
||||
$GO_TASK_PROGNAME --global --list-all
|
||||
else
|
||||
$GO_TASK_PROGNAME --list-all
|
||||
end 2>&1 | read -lz rawOutput
|
||||
|
||||
# Return on non-zero exit code (for cases when there is no Taskfile found or etc.)
|
||||
if test $status -ne 0
|
||||
return
|
||||
end
|
||||
|
||||
# Grab names and descriptions (if any) of the tasks
|
||||
set -l output (echo $rawOutput | sed -e '1d; s/\* \(.*\):[[:space:]]\{2,\}\(.*\)[[:space:]]\{2,\}(\(aliases.*\))/\1\t\2\t\3/' -e 's/\* \(.*\):[[:space:]]\{2,\}\(.*\)/\1\t\2/'| string split0)
|
||||
if test $output
|
||||
echo $output
|
||||
end
|
||||
if test $count -gt 1
|
||||
for line in $output[1..(math $count - 1)]
|
||||
echo $line
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
complete -c $GO_TASK_PROGNAME \
|
||||
-d 'Runs the specified task(s). Falls back to the "default" task if no task name was specified, or lists all tasks if an unknown task name was specified.' \
|
||||
-xa "(__task_get_tasks)" \
|
||||
-n "not __fish_seen_subcommand_from --"
|
||||
|
||||
# Standard flags
|
||||
complete -c $GO_TASK_PROGNAME -s a -l list-all -d 'list all tasks'
|
||||
complete -c $GO_TASK_PROGNAME -s c -l color -d 'colored output (default true)'
|
||||
complete -c $GO_TASK_PROGNAME -s C -l concurrency -d 'limit number of concurrent tasks'
|
||||
complete -c $GO_TASK_PROGNAME -l completion -d 'generate shell completion script' -xa "bash zsh fish powershell"
|
||||
complete -c $GO_TASK_PROGNAME -s d -l dir -d 'set directory of execution'
|
||||
complete -c $GO_TASK_PROGNAME -l disable-fuzzy -d 'disable fuzzy matching for task names'
|
||||
complete -c $GO_TASK_PROGNAME -s n -l dry -d 'compile and print tasks without executing'
|
||||
complete -c $GO_TASK_PROGNAME -s x -l exit-code -d 'pass-through exit code of task command'
|
||||
complete -c $GO_TASK_PROGNAME -l experiments -d 'list available experiments'
|
||||
complete -c $GO_TASK_PROGNAME -s F -l failfast -d 'when running tasks in parallel, stop all tasks if one fails'
|
||||
complete -c $GO_TASK_PROGNAME -s f -l force -d 'force execution even when up-to-date'
|
||||
complete -c $GO_TASK_PROGNAME -s g -l global -d 'run global Taskfile from home directory'
|
||||
complete -c $GO_TASK_PROGNAME -s h -l help -d 'show help'
|
||||
complete -c $GO_TASK_PROGNAME -s i -l init -d 'create new Taskfile'
|
||||
complete -c $GO_TASK_PROGNAME -l insecure -d 'allow insecure Taskfile downloads'
|
||||
complete -c $GO_TASK_PROGNAME -s I -l interval -d 'interval to watch for changes'
|
||||
complete -c $GO_TASK_PROGNAME -s j -l json -d 'format task list as JSON'
|
||||
complete -c $GO_TASK_PROGNAME -s l -l list -d 'list tasks with descriptions'
|
||||
complete -c $GO_TASK_PROGNAME -l nested -d 'nest namespaces when listing as JSON'
|
||||
complete -c $GO_TASK_PROGNAME -l no-status -d 'ignore status when listing as JSON'
|
||||
complete -c $GO_TASK_PROGNAME -l interactive -d 'prompt for missing required variables'
|
||||
complete -c $GO_TASK_PROGNAME -s o -l output -d 'set output style' -xa "interleaved group prefixed"
|
||||
complete -c $GO_TASK_PROGNAME -l output-group-begin -d 'message template before grouped output'
|
||||
complete -c $GO_TASK_PROGNAME -l output-group-end -d 'message template after grouped output'
|
||||
complete -c $GO_TASK_PROGNAME -l output-group-error-only -d 'hide output from successful tasks'
|
||||
complete -c $GO_TASK_PROGNAME -s p -l parallel -d 'execute tasks in parallel'
|
||||
complete -c $GO_TASK_PROGNAME -s s -l silent -d 'disable echoing'
|
||||
complete -c $GO_TASK_PROGNAME -l sort -d 'set task sorting order' -xa "default alphanumeric none"
|
||||
complete -c $GO_TASK_PROGNAME -l status -d 'exit non-zero if tasks not up-to-date'
|
||||
complete -c $GO_TASK_PROGNAME -l summary -d 'show task summary'
|
||||
complete -c $GO_TASK_PROGNAME -s t -l taskfile -d 'choose Taskfile to run'
|
||||
complete -c $GO_TASK_PROGNAME -s v -l verbose -d 'verbose output'
|
||||
complete -c $GO_TASK_PROGNAME -l version -d 'show version'
|
||||
complete -c $GO_TASK_PROGNAME -s w -l watch -d 'watch mode, re-run on changes'
|
||||
complete -c $GO_TASK_PROGNAME -s y -l yes -d 'assume yes to all prompts'
|
||||
|
||||
# Experimental flags (dynamically checked at completion time via -n condition)
|
||||
# GentleForce experiment
|
||||
complete -c $GO_TASK_PROGNAME -n "__task_is_experiment_enabled GENTLE_FORCE" -l force-all -d 'force execution of task and all dependencies'
|
||||
|
||||
# RemoteTaskfiles experiment - Options
|
||||
complete -c $GO_TASK_PROGNAME -n "__task_is_experiment_enabled REMOTE_TASKFILES" -l offline -d 'use only local or cached Taskfiles'
|
||||
complete -c $GO_TASK_PROGNAME -n "__task_is_experiment_enabled REMOTE_TASKFILES" -l timeout -d 'timeout for remote Taskfile downloads'
|
||||
complete -c $GO_TASK_PROGNAME -n "__task_is_experiment_enabled REMOTE_TASKFILES" -l expiry -d 'cache expiry duration'
|
||||
complete -c $GO_TASK_PROGNAME -n "__task_is_experiment_enabled REMOTE_TASKFILES" -l remote-cache-dir -d 'directory to cache remote Taskfiles' -xa "(__fish_complete_directories)"
|
||||
complete -c $GO_TASK_PROGNAME -n "__task_is_experiment_enabled REMOTE_TASKFILES" -l cacert -d 'custom CA certificate for TLS' -r
|
||||
complete -c $GO_TASK_PROGNAME -n "__task_is_experiment_enabled REMOTE_TASKFILES" -l cert -d 'client certificate for mTLS' -r
|
||||
complete -c $GO_TASK_PROGNAME -n "__task_is_experiment_enabled REMOTE_TASKFILES" -l cert-key -d 'client certificate private key' -r
|
||||
|
||||
# RemoteTaskfiles experiment - Operations
|
||||
complete -c $GO_TASK_PROGNAME -n "__task_is_experiment_enabled REMOTE_TASKFILES" -l download -d 'download remote Taskfile'
|
||||
complete -c $GO_TASK_PROGNAME -n "__task_is_experiment_enabled REMOTE_TASKFILES" -l clear-cache -d 'clear remote Taskfile cache'
|
||||
complete -c $GO_TASK_PROGNAME --no-files -a "(__task_complete)"
|
||||
complete -c $GO_TASK_PROGNAME -s t -l taskfile -r -k -a "(__fish_complete_suffix .yml .yaml)"
|
||||
complete -c $GO_TASK_PROGNAME -s d -l dir -xa "(__fish_complete_directories)"
|
||||
|
||||
@@ -1,94 +1,61 @@
|
||||
using namespace System.Management.Automation
|
||||
|
||||
# Thin wrapper around `task __complete`. All suggestion logic lives in the
|
||||
# Go engine — do not add completion logic here.
|
||||
|
||||
$cmdNames = @('task') + (Get-Alias -Definition task,task.exe,*\task,*\task.exe -ErrorAction SilentlyContinue).Name | Select-Object -Unique
|
||||
|
||||
Register-ArgumentCompleter -CommandName $cmdNames -ScriptBlock {
|
||||
param($commandName, $parameterName, $wordToComplete, $commandAst, $fakeBoundParameters)
|
||||
Register-ArgumentCompleter -Native -CommandName $cmdNames -ScriptBlock {
|
||||
param($wordToComplete, $commandAst, $cursorPosition)
|
||||
|
||||
if ($commandName.StartsWith('-')) {
|
||||
$completions = @(
|
||||
# Standard flags (alphabetical order)
|
||||
[CompletionResult]::new('-a', '-a', [CompletionResultType]::ParameterName, 'list all tasks'),
|
||||
[CompletionResult]::new('--list-all', '--list-all', [CompletionResultType]::ParameterName, 'list all tasks'),
|
||||
[CompletionResult]::new('-c', '-c', [CompletionResultType]::ParameterName, 'colored output'),
|
||||
[CompletionResult]::new('--color', '--color', [CompletionResultType]::ParameterName, 'colored output'),
|
||||
[CompletionResult]::new('-C', '-C', [CompletionResultType]::ParameterName, 'limit concurrent tasks'),
|
||||
[CompletionResult]::new('--concurrency', '--concurrency', [CompletionResultType]::ParameterName, 'limit concurrent tasks'),
|
||||
[CompletionResult]::new('--completion', '--completion', [CompletionResultType]::ParameterName, 'generate shell completion'),
|
||||
[CompletionResult]::new('-d', '-d', [CompletionResultType]::ParameterName, 'set directory'),
|
||||
[CompletionResult]::new('--dir', '--dir', [CompletionResultType]::ParameterName, 'set directory'),
|
||||
[CompletionResult]::new('--disable-fuzzy', '--disable-fuzzy', [CompletionResultType]::ParameterName, 'disable fuzzy matching'),
|
||||
[CompletionResult]::new('-n', '-n', [CompletionResultType]::ParameterName, 'dry run'),
|
||||
[CompletionResult]::new('--dry', '--dry', [CompletionResultType]::ParameterName, 'dry run'),
|
||||
[CompletionResult]::new('-x', '-x', [CompletionResultType]::ParameterName, 'pass-through exit code'),
|
||||
[CompletionResult]::new('--exit-code', '--exit-code', [CompletionResultType]::ParameterName, 'pass-through exit code'),
|
||||
[CompletionResult]::new('--experiments', '--experiments', [CompletionResultType]::ParameterName, 'list experiments'),
|
||||
[CompletionResult]::new('-F', '-F', [CompletionResultType]::ParameterName, 'fail fast on pallalel tasks'),
|
||||
[CompletionResult]::new('--failfast', '--failfast', [CompletionResultType]::ParameterName, 'force execution'),
|
||||
[CompletionResult]::new('-f', '-f', [CompletionResultType]::ParameterName, 'force execution'),
|
||||
[CompletionResult]::new('--force', '--force', [CompletionResultType]::ParameterName, 'force execution'),
|
||||
[CompletionResult]::new('-g', '-g', [CompletionResultType]::ParameterName, 'run global Taskfile'),
|
||||
[CompletionResult]::new('--global', '--global', [CompletionResultType]::ParameterName, 'run global Taskfile'),
|
||||
[CompletionResult]::new('-h', '-h', [CompletionResultType]::ParameterName, 'show help'),
|
||||
[CompletionResult]::new('--help', '--help', [CompletionResultType]::ParameterName, 'show help'),
|
||||
[CompletionResult]::new('-i', '-i', [CompletionResultType]::ParameterName, 'create new Taskfile'),
|
||||
[CompletionResult]::new('--init', '--init', [CompletionResultType]::ParameterName, 'create new Taskfile'),
|
||||
[CompletionResult]::new('--insecure', '--insecure', [CompletionResultType]::ParameterName, 'allow insecure downloads'),
|
||||
[CompletionResult]::new('-I', '-I', [CompletionResultType]::ParameterName, 'watch interval'),
|
||||
[CompletionResult]::new('--interval', '--interval', [CompletionResultType]::ParameterName, 'watch interval'),
|
||||
[CompletionResult]::new('-j', '-j', [CompletionResultType]::ParameterName, 'format as JSON'),
|
||||
[CompletionResult]::new('--json', '--json', [CompletionResultType]::ParameterName, 'format as JSON'),
|
||||
[CompletionResult]::new('-l', '-l', [CompletionResultType]::ParameterName, 'list tasks'),
|
||||
[CompletionResult]::new('--list', '--list', [CompletionResultType]::ParameterName, 'list tasks'),
|
||||
[CompletionResult]::new('--nested', '--nested', [CompletionResultType]::ParameterName, 'nest namespaces in JSON'),
|
||||
[CompletionResult]::new('--no-status', '--no-status', [CompletionResultType]::ParameterName, 'ignore status in JSON'),
|
||||
[CompletionResult]::new('--interactive', '--interactive', [CompletionResultType]::ParameterName, 'prompt for missing required variables'),
|
||||
[CompletionResult]::new('-o', '-o', [CompletionResultType]::ParameterName, 'set output style'),
|
||||
[CompletionResult]::new('--output', '--output', [CompletionResultType]::ParameterName, 'set output style'),
|
||||
[CompletionResult]::new('--output-group-begin', '--output-group-begin', [CompletionResultType]::ParameterName, 'template before group'),
|
||||
[CompletionResult]::new('--output-group-end', '--output-group-end', [CompletionResultType]::ParameterName, 'template after group'),
|
||||
[CompletionResult]::new('--output-group-error-only', '--output-group-error-only', [CompletionResultType]::ParameterName, 'hide successful output'),
|
||||
[CompletionResult]::new('-p', '-p', [CompletionResultType]::ParameterName, 'execute in parallel'),
|
||||
[CompletionResult]::new('--parallel', '--parallel', [CompletionResultType]::ParameterName, 'execute in parallel'),
|
||||
[CompletionResult]::new('-s', '-s', [CompletionResultType]::ParameterName, 'silent mode'),
|
||||
[CompletionResult]::new('--silent', '--silent', [CompletionResultType]::ParameterName, 'silent mode'),
|
||||
[CompletionResult]::new('--sort', '--sort', [CompletionResultType]::ParameterName, 'task sorting order'),
|
||||
[CompletionResult]::new('--status', '--status', [CompletionResultType]::ParameterName, 'check task status'),
|
||||
[CompletionResult]::new('--summary', '--summary', [CompletionResultType]::ParameterName, 'show task summary'),
|
||||
[CompletionResult]::new('-t', '-t', [CompletionResultType]::ParameterName, 'choose Taskfile'),
|
||||
[CompletionResult]::new('--taskfile', '--taskfile', [CompletionResultType]::ParameterName, 'choose Taskfile'),
|
||||
[CompletionResult]::new('-v', '-v', [CompletionResultType]::ParameterName, 'verbose output'),
|
||||
[CompletionResult]::new('--verbose', '--verbose', [CompletionResultType]::ParameterName, 'verbose output'),
|
||||
[CompletionResult]::new('--version', '--version', [CompletionResultType]::ParameterName, 'show version'),
|
||||
[CompletionResult]::new('-w', '-w', [CompletionResultType]::ParameterName, 'watch mode'),
|
||||
[CompletionResult]::new('--watch', '--watch', [CompletionResultType]::ParameterName, 'watch mode'),
|
||||
[CompletionResult]::new('-y', '-y', [CompletionResultType]::ParameterName, 'assume yes'),
|
||||
[CompletionResult]::new('--yes', '--yes', [CompletionResultType]::ParameterName, 'assume yes')
|
||||
)
|
||||
$TaskExe = if ($env:TASK_EXE) { $env:TASK_EXE } else { 'task' }
|
||||
|
||||
# Experimental flags (dynamically added based on enabled experiments)
|
||||
$experiments = & task --experiments 2>$null | Out-String
|
||||
|
||||
if ($experiments -match '\* GENTLE_FORCE:.*on') {
|
||||
$completions += [CompletionResult]::new('--force-all', '--force-all', [CompletionResultType]::ParameterName, 'force all dependencies')
|
||||
# Words after the program name, truncated to the cursor.
|
||||
$argsToPass = @()
|
||||
$elements = $commandAst.CommandElements
|
||||
if ($elements.Count -gt 1) {
|
||||
for ($i = 1; $i -lt $elements.Count; $i++) {
|
||||
$el = $elements[$i]
|
||||
if ($el.Extent.StartOffset -ge $cursorPosition) { break }
|
||||
$argsToPass += $el.ToString()
|
||||
}
|
||||
|
||||
if ($experiments -match '\* REMOTE_TASKFILES:.*on') {
|
||||
# Options
|
||||
$completions += [CompletionResult]::new('--offline', '--offline', [CompletionResultType]::ParameterName, 'use cached Taskfiles')
|
||||
$completions += [CompletionResult]::new('--timeout', '--timeout', [CompletionResultType]::ParameterName, 'download timeout')
|
||||
$completions += [CompletionResult]::new('--expiry', '--expiry', [CompletionResultType]::ParameterName, 'cache expiry')
|
||||
$completions += [CompletionResult]::new('--remote-cache-dir', '--remote-cache-dir', [CompletionResultType]::ParameterName, 'cache directory')
|
||||
$completions += [CompletionResult]::new('--cacert', '--cacert', [CompletionResultType]::ParameterName, 'custom CA certificate')
|
||||
$completions += [CompletionResult]::new('--cert', '--cert', [CompletionResultType]::ParameterName, 'client certificate')
|
||||
$completions += [CompletionResult]::new('--cert-key', '--cert-key', [CompletionResultType]::ParameterName, 'client private key')
|
||||
# Operations
|
||||
$completions += [CompletionResult]::new('--download', '--download', [CompletionResultType]::ParameterName, 'download remote Taskfile')
|
||||
$completions += [CompletionResult]::new('--clear-cache', '--clear-cache', [CompletionResultType]::ParameterName, 'clear cache')
|
||||
}
|
||||
|
||||
return $completions.Where{ $_.CompletionText.StartsWith($commandName) }
|
||||
}
|
||||
# The trailing word (possibly empty) must reach the engine so it knows
|
||||
# the cursor sits on a fresh word.
|
||||
if ($argsToPass.Count -gt 0 -and $argsToPass[-1] -eq $wordToComplete) {
|
||||
$argsToPass[-1] = $wordToComplete
|
||||
} else {
|
||||
$argsToPass += $wordToComplete
|
||||
}
|
||||
|
||||
return $(task --list-all --silent) | Where-Object { $_.StartsWith($commandName) } | ForEach-Object { return $_ + " " }
|
||||
$output = & $TaskExe __complete @argsToPass 2>$null
|
||||
if (-not $output) { return }
|
||||
|
||||
$lines = @($output)
|
||||
if ($lines.Count -eq 0) { return }
|
||||
$last = $lines[-1]
|
||||
if (-not $last.StartsWith(':')) { return }
|
||||
|
||||
$directive = [int]($last.Substring(1))
|
||||
$data = if ($lines.Count -gt 1) { $lines[0..($lines.Count - 2)] } else { @() }
|
||||
|
||||
# FilterFileExt
|
||||
if ($directive -band 8) {
|
||||
$patterns = $data | ForEach-Object { "*.$_" }
|
||||
return Get-ChildItem -Path . -Include $patterns -File -ErrorAction SilentlyContinue |
|
||||
ForEach-Object { [CompletionResult]::new($_.Name, $_.Name, [CompletionResultType]::ProviderItem, $_.Name) }
|
||||
}
|
||||
|
||||
# FilterDirs
|
||||
if ($directive -band 16) {
|
||||
return Get-ChildItem -Path . -Directory -ErrorAction SilentlyContinue |
|
||||
ForEach-Object { [CompletionResult]::new($_.Name, $_.Name, [CompletionResultType]::ProviderContainer, $_.Name) }
|
||||
}
|
||||
|
||||
return $data | ForEach-Object {
|
||||
$parts = $_ -split "`t", 2
|
||||
$value = $parts[0]
|
||||
$desc = if ($parts.Count -gt 1 -and $parts[1]) { $parts[1] } else { $value }
|
||||
[CompletionResult]::new($value, $value, [CompletionResultType]::ParameterValue, $desc)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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