mirror of
https://github.com/go-task/task.git
synced 2026-09-02 04:02:08 +02:00
The engine shipped opt-in behind --new-completion, with the old scripts still on --completion. That split was never released, so flip it now rather than carry a third flag through a deprecation later. --completion serves the engine wrappers; the hand-written scripts move to completion/legacy/ and stay reachable via --legacy-completion for a release or two. .goreleaser.yml needed to follow: it packages the static files from completion/ into the deb/rpm/apk contents and the Homebrew cask, so leaving it untouched would have shipped the engine to anyone running `eval "$(task --completion zsh)"` and the old scripts to everyone installing from a package. The paths it references are unchanged and now resolve to the wrappers. Its archive glob is narrowed at the same time, so the test harness under completion/tests/ stops being shipped in the release archives.
87 lines
3.5 KiB
Nu
87 lines
3.5 KiB
Nu
# Thin wrapper around `task __complete`: all suggestion logic lives in the Go engine.
|
|
|
|
# The `{completions, options}` record documented for `def` completers is
|
|
# rejected for an external one: return records or null, nothing else.
|
|
def task-external-completer [spans: list<string>] {
|
|
let exe = ($env.TASK_EXE? | default "task")
|
|
|
|
# The trailing empty word tells the engine the cursor is on a fresh word.
|
|
let words = ($spans | skip 1)
|
|
let args = (if ($words | is-empty) { [""] } else { $words })
|
|
let current = ($args | last)
|
|
|
|
# `complete` keeps stderr off the prompt; a missing binary raises, hence `try`.
|
|
let result = (try { do { ^$exe "__complete" ...$args } | complete } catch { null })
|
|
if ($result | is-empty) or $result.exit_code != 0 {
|
|
return null
|
|
}
|
|
|
|
let lines = ($result.stdout | lines)
|
|
let last = ($lines | last)
|
|
# Protocol violation: offer nothing rather than garbage.
|
|
if ($last | is-empty) or (not ($last | str starts-with ":")) {
|
|
return null
|
|
}
|
|
let directive = (try { $last | str substring 1.. | into int } catch { 0 })
|
|
let data = ($lines | drop 1)
|
|
|
|
# Completion directives, mirroring internal/complete/complete.go. NoSpace (2)
|
|
# and KeepOrder (32) need none: no space is appended, order is kept.
|
|
let no_file_comp = (($directive | bits and 4) != 0)
|
|
let filter_file_ext = (($directive | bits and 8) != 0)
|
|
let filter_dirs = (($directive | bits and 16) != 0)
|
|
|
|
# Nushell replaces the whole token, so an inline `--flag=` must be re-applied.
|
|
let inline = ($current | parse --regex '^(?<flag>--?[^=]+=)(?<path>.*)$')
|
|
let flag_prefix = (if ($inline | is-empty) { "" } else { $inline.0.flag })
|
|
let path_arg = (if ($inline | is-empty) { $current } else { $inline.0.path })
|
|
|
|
if $filter_file_ext or $filter_dirs {
|
|
# `into glob` turns the literal path into a pattern; matching nothing raises.
|
|
let entries = (try { ls ($"($path_arg)*" | into glob) } catch { [] })
|
|
let matched = (if $filter_file_ext {
|
|
$entries | where {|entry| $entry.type == "dir" or ($entry.name | path parse | get extension) in $data }
|
|
} else {
|
|
$entries | where type == "dir"
|
|
})
|
|
return ($matched | each {|entry|
|
|
# Without a trailing separator a second <tab> matches the dir again.
|
|
let name = (if $entry.type == "dir" { $"($entry.name)(char path_sep)" } else { $entry.name })
|
|
{ value: $"($flag_prefix)($name)" }
|
|
})
|
|
}
|
|
|
|
# Nushell does not filter an external completer's results.
|
|
let candidates = ($data
|
|
| each {|line|
|
|
let parts = ($line | split row --number 2 "\t")
|
|
let value = ($parts | first)
|
|
if ($parts | length) > 1 { { value: $value, description: ($parts | last) } } else { { value: $value } }
|
|
}
|
|
| where {|candidate| $candidate.value | str starts-with --ignore-case $current })
|
|
|
|
if ($candidates | is-empty) and (not $no_file_comp) {
|
|
return null
|
|
}
|
|
|
|
$candidates
|
|
}
|
|
|
|
# Nushell shares one external completer between every command, so chain to the
|
|
# installed one instead of breaking every other tool.
|
|
let task_previous_completer = ($env.config.completions.external.completer? | default null)
|
|
|
|
$env.config.completions.external.completer = {|spans|
|
|
let exe = ($env.TASK_EXE? | default "task")
|
|
# Compare basenames so `./task`, `/usr/local/bin/task` and `task.exe` match.
|
|
let head = ($spans | first | path basename | str replace --regex '(?i)\.exe$' '')
|
|
let name = ($exe | path basename | str replace --regex '(?i)\.exe$' '')
|
|
if $head == $name {
|
|
task-external-completer $spans
|
|
} else if $task_previous_completer != null {
|
|
do $task_previous_completer $spans
|
|
} else {
|
|
null
|
|
}
|
|
}
|