mirror of
https://github.com/go-task/task.git
synced 2026-08-29 10:08:27 +02:00
feat(completion): add Nushell completions (#2966)
This commit is contained in:
@@ -79,6 +79,8 @@ nfpms:
|
|||||||
dst: /usr/share/fish/vendor_completions.d/task.fish
|
dst: /usr/share/fish/vendor_completions.d/task.fish
|
||||||
- src: completion/zsh/_task
|
- src: completion/zsh/_task
|
||||||
dst: /usr/local/share/zsh/site-functions/_task
|
dst: /usr/local/share/zsh/site-functions/_task
|
||||||
|
- src: completion/nu/task-completions.nu
|
||||||
|
dst: /usr/share/nushell/vendor/autoload/task-completions.nu
|
||||||
|
|
||||||
brews:
|
brews:
|
||||||
- name: go-task
|
- name: go-task
|
||||||
@@ -96,6 +98,7 @@ brews:
|
|||||||
bash_completion.install "completion/bash/task.bash" => "task"
|
bash_completion.install "completion/bash/task.bash" => "task"
|
||||||
zsh_completion.install "completion/zsh/_task" => "_task"
|
zsh_completion.install "completion/zsh/_task" => "_task"
|
||||||
fish_completion.install "completion/fish/task.fish"
|
fish_completion.install "completion/fish/task.fish"
|
||||||
|
(share/"nushell/vendor/autoload").install "completion/nu/task-completions.nu"
|
||||||
commit_author:
|
commit_author:
|
||||||
name: task-bot
|
name: task-bot
|
||||||
email: 106601941+task-bot@users.noreply.github.com
|
email: 106601941+task-bot@users.noreply.github.com
|
||||||
|
|||||||
@@ -41,6 +41,9 @@
|
|||||||
stopping without a trace (#2240 by @Drino).
|
stopping without a trace (#2240 by @Drino).
|
||||||
- Fixed pressing `Esc` at an interactive variable prompt not cancelling the run
|
- Fixed pressing `Esc` at an interactive variable prompt not cancelling the run
|
||||||
(#2942 by @anilnatha).
|
(#2942 by @anilnatha).
|
||||||
|
- Added Nushell completions, available via `task --completion nu`. They complete
|
||||||
|
task names and aliases, every flag with its description, and the values of
|
||||||
|
`--completion`, `--output` and `--sort` (#2966 by @vmaerten).
|
||||||
|
|
||||||
## v3.52.0 - 2026-07-02
|
## v3.52.0 - 2026-07-02
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,9 @@ var completionBash string
|
|||||||
//go:embed completion/fish/task.fish
|
//go:embed completion/fish/task.fish
|
||||||
var completionFish string
|
var completionFish string
|
||||||
|
|
||||||
|
//go:embed completion/nu/task-completions.nu
|
||||||
|
var completionNu string
|
||||||
|
|
||||||
//go:embed completion/ps/task.ps1
|
//go:embed completion/ps/task.ps1
|
||||||
var completionPowershell string
|
var completionPowershell string
|
||||||
|
|
||||||
@@ -24,6 +27,8 @@ func Completion(completion string) (string, error) {
|
|||||||
return completionBash, nil
|
return completionBash, nil
|
||||||
case "fish":
|
case "fish":
|
||||||
return completionFish, nil
|
return completionFish, nil
|
||||||
|
case "nu", "nushell":
|
||||||
|
return completionNu, nil
|
||||||
case "powershell":
|
case "powershell":
|
||||||
return completionPowershell, nil
|
return completionPowershell, nil
|
||||||
case "zsh":
|
case "zsh":
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ complete -c $GO_TASK_PROGNAME \
|
|||||||
complete -c $GO_TASK_PROGNAME -s a -l list-all -d 'list all tasks'
|
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 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 -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 -l completion -d 'generate shell completion script' -xa "bash zsh fish powershell nu"
|
||||||
complete -c $GO_TASK_PROGNAME -s d -l dir -d 'set directory of execution'
|
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 -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 n -l dry -d 'compile and print tasks without executing'
|
||||||
|
|||||||
180
completion/nu/task-completions.nu
Normal file
180
completion/nu/task-completions.nu
Normal file
@@ -0,0 +1,180 @@
|
|||||||
|
# Nushell completions for Task (https://taskfile.dev).
|
||||||
|
#
|
||||||
|
# Nushell cannot source a script from stdin, so save this file where Nushell
|
||||||
|
# picks it up automatically:
|
||||||
|
# mkdir ($nu.data-dir | path join "vendor/autoload")
|
||||||
|
# task --completion nu | save --force ($nu.data-dir | path join "vendor/autoload/task-completions.nu")
|
||||||
|
#
|
||||||
|
# The file must not be named task.nu: Nushell refuses to export a known external
|
||||||
|
# named like its module, which would break `use task-completions.nu *`.
|
||||||
|
|
||||||
|
# Name or path of the Task executable, like the other completion scripts.
|
||||||
|
# The *completed* command is always `task`: an `extern` declaration requires a
|
||||||
|
# literal name. For a renamed executable, alias it instead: `alias go-task = task`.
|
||||||
|
def "nu-complete task-exe" [] {
|
||||||
|
$env.TASK_EXE? | default "task"
|
||||||
|
}
|
||||||
|
|
||||||
|
def "nu-complete task-words" [context: string] {
|
||||||
|
$context | split row --regex '\s+' | where {|word| $word != "" }
|
||||||
|
}
|
||||||
|
|
||||||
|
# Strips the quotes the user may have typed around a value and expands `~`,
|
||||||
|
# which Nushell does not do for a value coming from a variable.
|
||||||
|
def "nu-complete task-value" [value: string] {
|
||||||
|
let unquoted = ($value | str trim --char '"' | str trim --char "'")
|
||||||
|
if ($unquoted | str starts-with "~") {
|
||||||
|
$unquoted | path expand --no-symlink
|
||||||
|
} else {
|
||||||
|
$unquoted
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Rebuilds the flags deciding *which* Taskfile is read, so the task list follows
|
||||||
|
# the `-t/--taskfile`, `-d/--dir` and `-g/--global` already on the command line.
|
||||||
|
def "nu-complete task-scope" [words: list<string>] {
|
||||||
|
mut scope: list<string> = []
|
||||||
|
mut pending = ""
|
||||||
|
|
||||||
|
for word in ($words | skip 1) {
|
||||||
|
if $pending != "" {
|
||||||
|
$scope = ($scope | append [$pending, (nu-complete task-value $word)])
|
||||||
|
$pending = ""
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
let parts = ($word | split row "=")
|
||||||
|
let name = ($parts | first)
|
||||||
|
let inline = if ($parts | length) > 1 { $parts | skip 1 | str join "=" } else { null }
|
||||||
|
|
||||||
|
if $name in ["-g", "--global"] {
|
||||||
|
$scope = ($scope | append "--global")
|
||||||
|
} else if $name in ["-t", "--taskfile", "-d", "--dir"] {
|
||||||
|
let long = if $name in ["-t", "--taskfile"] { "--taskfile" } else { "--dir" }
|
||||||
|
if $inline != null {
|
||||||
|
$scope = ($scope | append [$long, (nu-complete task-value $inline)])
|
||||||
|
} else {
|
||||||
|
$pending = $long
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$scope
|
||||||
|
}
|
||||||
|
|
||||||
|
# Lists the tasks of the targeted Taskfile. `--no-status` keeps completion fast:
|
||||||
|
# without it Task fingerprints every task's sources on each keystroke. Returns an
|
||||||
|
# empty list when Task exits non-zero (no Taskfile, invalid Taskfile).
|
||||||
|
def "nu-complete task-list" [words: list<string>] {
|
||||||
|
let exe = (nu-complete task-exe)
|
||||||
|
let args = [...(nu-complete task-scope $words) "--list-all" "--json" "--no-status"]
|
||||||
|
let result = (try { do { ^$exe ...$args } | complete } catch { null })
|
||||||
|
|
||||||
|
if ($result | is-empty) or $result.exit_code != 0 {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
|
||||||
|
try { $result.stdout | from json | get tasks } catch { [] }
|
||||||
|
}
|
||||||
|
|
||||||
|
def "nu-complete task" [context: string] {
|
||||||
|
let words = (nu-complete task-words $context)
|
||||||
|
|
||||||
|
# Words after `--` are forwarded to the task as CLI_ARGS: stop offering task
|
||||||
|
# names and let Nushell fall back to its own file completion.
|
||||||
|
if "--" in $words {
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
let completions = (
|
||||||
|
nu-complete task-list $words
|
||||||
|
| each {|item|
|
||||||
|
# `task` is the invocable name; `name` may be a display-only label.
|
||||||
|
let name = ($item.task | str trim --right --char ':')
|
||||||
|
let desc = ($item.desc? | default "")
|
||||||
|
let aliases = (
|
||||||
|
$item.aliases?
|
||||||
|
| default []
|
||||||
|
| each {|alias| {
|
||||||
|
value: ($alias | str trim --right --char ':')
|
||||||
|
description: (if ($desc | is-empty) { $"alias of ($name)" } else { $"($desc) \(alias of ($name)\)" })
|
||||||
|
} }
|
||||||
|
)
|
||||||
|
[{ value: $name, description: $desc }] | append $aliases
|
||||||
|
}
|
||||||
|
| flatten
|
||||||
|
)
|
||||||
|
|
||||||
|
# `sort: false` keeps the order Task chose, which honours --sort and .taskrc.
|
||||||
|
{ options: { sort: false }, completions: $completions }
|
||||||
|
}
|
||||||
|
|
||||||
|
def "nu-complete task-shells" [] {
|
||||||
|
["bash", "zsh", "fish", "powershell", "nu"]
|
||||||
|
}
|
||||||
|
|
||||||
|
def "nu-complete task-output" [] {
|
||||||
|
["interleaved", "group", "prefixed"]
|
||||||
|
}
|
||||||
|
|
||||||
|
def "nu-complete task-sort" [] {
|
||||||
|
["default", "alphanumeric", "none"]
|
||||||
|
}
|
||||||
|
|
||||||
|
# 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.
|
||||||
|
#
|
||||||
|
# An `extern` signature is static, so the experimental flags at the bottom are
|
||||||
|
# always offered; Task rejects them when the experiment is off. Run
|
||||||
|
# `task --experiments` to see which ones are enabled.
|
||||||
|
export extern "task" [
|
||||||
|
...tasks: string@"nu-complete task" # task(s) to run
|
||||||
|
--list(-l) # list tasks with a description
|
||||||
|
--list-all(-a) # list all tasks, with or without a description
|
||||||
|
--json(-j) # format the task list as JSON
|
||||||
|
--no-status # ignore status when listing tasks as JSON
|
||||||
|
--nested # nest namespaces when listing tasks as JSON
|
||||||
|
--sort: string@"nu-complete task-sort" # change the order of the tasks when listed
|
||||||
|
--init(-i) # create a new Taskfile.yml in the current folder
|
||||||
|
--completion: string@"nu-complete task-shells" # generate a shell completion script
|
||||||
|
--taskfile(-t): glob # choose which Taskfile to run
|
||||||
|
--dir(-d): directory # set the directory in which Task will execute
|
||||||
|
--global(-g) # run the global Taskfile from $HOME
|
||||||
|
--temp-dir: directory # directory used to store Task temporary files
|
||||||
|
--force(-f) # force execution even when the task is up-to-date
|
||||||
|
--status # exit with a non-zero code if tasks are not up-to-date
|
||||||
|
--dry(-n) # compile and print the tasks without executing them
|
||||||
|
--summary # show the summary of a task instead of running it
|
||||||
|
--watch(-w) # watch the given tasks and re-run them on changes
|
||||||
|
--interval(-I): string # interval to watch for changes, e.g. 500ms
|
||||||
|
--parallel(-p) # run the tasks given on the command line in parallel
|
||||||
|
--concurrency(-C): int # limit the number of tasks run concurrently
|
||||||
|
--failfast(-F) # when running in parallel, stop everything if one task fails
|
||||||
|
--exit-code(-x) # pass through the exit code of the task command
|
||||||
|
--interactive # prompt for missing required variables
|
||||||
|
--yes(-y) # assume "yes" as the answer to all prompts
|
||||||
|
--output(-o): string@"nu-complete task-output" # set the output style
|
||||||
|
--output-group-begin: string # message template printed before a task's grouped output
|
||||||
|
--output-group-end: string # message template printed after a task's grouped output
|
||||||
|
--output-group-error-only # swallow the output of successful tasks
|
||||||
|
--color(-c) # colored output, enabled by default
|
||||||
|
--silent(-s) # disable echoing
|
||||||
|
--verbose(-v) # enable verbose mode
|
||||||
|
--disable-fuzzy # disable fuzzy matching for task names
|
||||||
|
--insecure # allow Taskfiles to be downloaded over insecure connections
|
||||||
|
--experiments # list the available experiments and whether they are enabled
|
||||||
|
--version # show the Task version
|
||||||
|
--help(-h) # show Task usage
|
||||||
|
|
||||||
|
--force-all # [GENTLE_FORCE] force the called task and all its dependencies
|
||||||
|
--download # [REMOTE_TASKFILES] download a cached version of a remote Taskfile
|
||||||
|
--offline # [REMOTE_TASKFILES] only use local or cached Taskfiles
|
||||||
|
--clear-cache # [REMOTE_TASKFILES] clear the remote Taskfile cache
|
||||||
|
--trusted-hosts: string # [REMOTE_TASKFILES] trusted hosts for remote Taskfiles (comma-separated)
|
||||||
|
--timeout: string # [REMOTE_TASKFILES] timeout for downloading remote Taskfiles
|
||||||
|
--expiry: string # [REMOTE_TASKFILES] expiry duration for cached remote Taskfiles
|
||||||
|
--remote-cache-dir: directory # [REMOTE_TASKFILES] directory used to cache remote Taskfiles
|
||||||
|
--cacert: path # [REMOTE_TASKFILES] custom CA certificate for HTTPS connections
|
||||||
|
--cert: path # [REMOTE_TASKFILES] client certificate for HTTPS connections
|
||||||
|
--cert-key: path # [REMOTE_TASKFILES] client certificate key for HTTPS connections
|
||||||
|
]
|
||||||
@@ -94,7 +94,7 @@ _task() {
|
|||||||
'(-F --failfast)'{-F,--failfast}'[when running tasks in parallel, stop all tasks if one fails]'
|
'(-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]'
|
'(-f --force)'{-f,--force}'[run even if task is up-to-date]'
|
||||||
'(-c --color)'{-c,--color}'[colored output]'
|
'(-c --color)'{-c,--color}'[colored output]'
|
||||||
'(--completion)--completion[generate shell completion script]:shell:(bash zsh fish powershell)'
|
'(--completion)--completion[generate shell completion script]:shell:(bash zsh fish powershell nu)'
|
||||||
'(-d --dir)'{-d,--dir}'[dir to run in]:execution dir:_dirs'
|
'(-d --dir)'{-d,--dir}'[dir to run in]:execution dir:_dirs'
|
||||||
'(--disable-fuzzy)--disable-fuzzy[disable fuzzy matching for task names]'
|
'(--disable-fuzzy)--disable-fuzzy[disable fuzzy matching for task names]'
|
||||||
'(-n --dry)'{-n,--dry}'[compiles and prints tasks without executing]'
|
'(-n --dry)'{-n,--dry}'[compiles and prints tasks without executing]'
|
||||||
|
|||||||
@@ -407,6 +407,16 @@ task --completion fish | source
|
|||||||
Invoke-Expression (&task --completion powershell | Out-String)
|
Invoke-Expression (&task --completion powershell | Out-String)
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```nu [nushell]
|
||||||
|
# ~/.config/nushell/config.nu
|
||||||
|
#
|
||||||
|
# Nushell cannot source a script from stdin, so the script is saved where
|
||||||
|
# Nushell auto-loads it at startup. Autoload directories are read after
|
||||||
|
# config.nu, so the completions become available in the next shell.
|
||||||
|
mkdir ($nu.data-dir | path join "vendor/autoload")
|
||||||
|
task --completion nu | save --force ($nu.data-dir | path join "vendor/autoload/task-completions.nu")
|
||||||
|
```
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
### Option 2. Copy the script to your shell's completions directory
|
### Option 2. Copy the script to your shell's completions directory
|
||||||
@@ -428,6 +438,10 @@ task --completion zsh > /usr/local/share/zsh/site-functions/_task
|
|||||||
task --completion fish > ~/.config/fish/completions/task.fish
|
task --completion fish > ~/.config/fish/completions/task.fish
|
||||||
```
|
```
|
||||||
|
|
||||||
|
```nu [nushell]
|
||||||
|
task --completion nu | save --force ($nu.data-dir | path join "vendor/autoload/task-completions.nu")
|
||||||
|
```
|
||||||
|
|
||||||
:::
|
:::
|
||||||
|
|
||||||
### Zsh customization
|
### Zsh customization
|
||||||
@@ -446,3 +460,29 @@ canonical task names, add the `show-aliases` zstyle:
|
|||||||
```shell
|
```shell
|
||||||
zstyle ':completion:*:*:task:*' show-aliases false
|
zstyle ':completion:*:*:task:*' show-aliases false
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Nushell caveats
|
||||||
|
|
||||||
|
Nushell cannot source a script from stdin, so both options above write the script
|
||||||
|
to an autoload directory. Option 1 rewrites it at every startup, which keeps it
|
||||||
|
in sync with the installed version of Task — the refreshed completions are picked
|
||||||
|
up by the next shell. With option 2, re-run the command after upgrading Task.
|
||||||
|
|
||||||
|
The completions are attached to an `extern "task"` declaration, which Nushell
|
||||||
|
requires to be static. Three consequences are worth knowing:
|
||||||
|
|
||||||
|
- The experimental flags (`--force-all`, `--download`, `--offline`, …) are always
|
||||||
|
offered, even when the corresponding experiment is disabled. Their description
|
||||||
|
is prefixed with the experiment name, and `task --experiments` lists the ones
|
||||||
|
that are enabled.
|
||||||
|
- Passing a value to a boolean flag with `=` does not work: Nushell forwards
|
||||||
|
`--color=false` as two arguments, so Task reads `false` as a task name. Use
|
||||||
|
`NO_COLOR=1`, or bypass the declaration with `^task --color=false`.
|
||||||
|
- `TASK_EXE` selects the executable that is run, but not the command name the
|
||||||
|
completions are attached to, which is always `task`. For a renamed executable,
|
||||||
|
alias it instead:
|
||||||
|
|
||||||
|
```nu
|
||||||
|
use ($nu.data-dir | path join "vendor/autoload/task-completions.nu") *
|
||||||
|
alias go-task = task
|
||||||
|
```
|
||||||
|
|||||||
Reference in New Issue
Block a user