diff options
| author | Paul Buetow <paul@buetow.org> | 2026-03-26 22:15:39 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-03-26 22:15:39 +0200 |
| commit | f6436fe4cfdd989d95607931314a7c47147ee1e2 (patch) | |
| tree | 835ccadab7ae9bfc65832848e6394be064e79070 | |
| parent | c8108deb77f1872e6a55925d5abf9c0ae12813c6 (diff) | |
ask: add fish UUID completions
| -rw-r--r-- | assets/ask.fish | 71 | ||||
| -rw-r--r-- | docs/fish-completion.md | 3 | ||||
| -rw-r--r-- | internal/askcli/completion.go | 134 | ||||
| -rw-r--r-- | internal/askcli/completion_test.go | 6 |
4 files changed, 204 insertions, 10 deletions
diff --git a/assets/ask.fish b/assets/ask.fish index e4d4654..6c5f904 100644 --- a/assets/ask.fish +++ b/assets/ask.fish @@ -37,6 +37,64 @@ function __ask_in_dep_context test $seen_dep -eq 1 end +function __ask_in_uuid_context + set -l tokens (commandline -opc) + set -l positional + for token in $tokens[2..-1] + if string match -qr '^-' -- $token + continue + end + set -a positional $token + end + if test (count $positional) -eq 0 + return 1 + end + if test (count $positional) -gt 2 + return 1 + end + switch $positional[1] + case info annotate start stop done priority tag modify denotate delete + return 0 + case '*' + return 1 + end + return 1 +end + +function __ask_in_dep_uuid_context + set -l tokens (commandline -opc) + set -l positional + for token in $tokens[2..-1] + if string match -qr '^-' -- $token + continue + end + set -a positional $token + end + if test (count $positional) -lt 2 + return 1 + end + if test $positional[1] != dep + return 1 + end + switch $positional[2] + case add rm + if test (count $positional) -le 4 + return 0 + end + case list + if test (count $positional) -le 3 + return 0 + end + case '*' + return 1 + end + return 1 +end + +function __ask_task_uuids + command ask all --json 2>/dev/null | string match -r -a -g '"uuid":"([^"]+)"' +end + complete -c ask -f complete -c ask -s j -l json -d 'Emit JSON output' complete -c ask -n '__ask_needs_root_completion' -a 'add' -d 'Create a new task' @@ -59,3 +117,16 @@ complete -c ask -n '__ask_needs_root_completion' -a 'help' -d 'Show help' complete -c ask -n '__ask_in_dep_context' -a 'add' -d 'Add a dependency' complete -c ask -n '__ask_in_dep_context' -a 'rm' -d 'Remove a dependency' complete -c ask -n '__ask_in_dep_context' -a 'list' -d 'List dependencies' +complete -c ask -n '__ask_in_uuid_context' -a '(__ask_task_uuids)' -d 'Show task details' +complete -c ask -n '__ask_in_uuid_context' -a '(__ask_task_uuids)' -d 'Add an annotation' +complete -c ask -n '__ask_in_uuid_context' -a '(__ask_task_uuids)' -d 'Start a task' +complete -c ask -n '__ask_in_uuid_context' -a '(__ask_task_uuids)' -d 'Stop a task' +complete -c ask -n '__ask_in_uuid_context' -a '(__ask_task_uuids)' -d 'Mark a task complete' +complete -c ask -n '__ask_in_uuid_context' -a '(__ask_task_uuids)' -d 'Set priority' +complete -c ask -n '__ask_in_uuid_context' -a '(__ask_task_uuids)' -d 'Add or remove a tag' +complete -c ask -n '__ask_in_uuid_context' -a '(__ask_task_uuids)' -d 'Modify task fields' +complete -c ask -n '__ask_in_uuid_context' -a '(__ask_task_uuids)' -d 'Remove an annotation' +complete -c ask -n '__ask_in_uuid_context' -a '(__ask_task_uuids)' -d 'Delete a task' +complete -c ask -n '__ask_in_dep_uuid_context' -a '(__ask_task_uuids)' -d 'Add a dependency' +complete -c ask -n '__ask_in_dep_uuid_context' -a '(__ask_task_uuids)' -d 'Remove a dependency' +complete -c ask -n '__ask_in_dep_uuid_context' -a '(__ask_task_uuids)' -d 'List dependencies' diff --git a/docs/fish-completion.md b/docs/fish-completion.md index 116a0a6..b37b79d 100644 --- a/docs/fish-completion.md +++ b/docs/fish-completion.md @@ -3,7 +3,8 @@ Hexai ships a Fish completion file for the `ask` task-management CLI at [`assets/ask.fish`](../assets/ask.fish). It completes the top-level `ask` subcommands and the nested `ask dep` operations. -The script also preserves the global `--json` flag. +It also completes task UUIDs for UUID-taking commands by reading the current project from `ask all --json`. +The script preserves the global `--json` flag. Install it into Fish's completion directory: diff --git a/internal/askcli/completion.go b/internal/askcli/completion.go index f787849..28ad306 100644 --- a/internal/askcli/completion.go +++ b/internal/askcli/completion.go @@ -35,11 +35,61 @@ var askDepCompletionItems = []fishCompletionItem{ {name: "list", description: "List dependencies"}, } +var askUUIDCompletionItems = []fishCompletionItem{ + {name: "info", description: "Show task details"}, + {name: "annotate", description: "Add an annotation"}, + {name: "start", description: "Start a task"}, + {name: "stop", description: "Stop a task"}, + {name: "done", description: "Mark a task complete"}, + {name: "priority", description: "Set priority"}, + {name: "tag", description: "Add or remove a tag"}, + {name: "modify", description: "Modify task fields"}, + {name: "denotate", description: "Remove an annotation"}, + {name: "delete", description: "Delete a task"}, +} + +var askDepUUIDCompletionItems = []fishCompletionItem{ + {name: "add", description: "Add a dependency"}, + {name: "rm", description: "Remove a dependency"}, + {name: "list", description: "List dependencies"}, +} + func FishCompletion() string { var b strings.Builder + writeFishPreamble(&b) + writeFishContextFunctions(&b) + writeFishTaskUUIDFunction(&b) + b.WriteString("complete -c ask -f\n") + b.WriteString("complete -c ask -s j -l json -d 'Emit JSON output'\n") + for _, item := range askRootCompletionItems { + writeFishCompletionLine(&b, "__ask_needs_root_completion", item) + } + for _, item := range askDepCompletionItems { + writeFishCompletionLine(&b, "__ask_in_dep_context", item) + } + for _, item := range askUUIDCompletionItems { + writeFishUUIDCompletionLine(&b, "__ask_in_uuid_context", item.description) + } + for _, item := range askDepUUIDCompletionItems { + writeFishUUIDCompletionLine(&b, "__ask_in_dep_uuid_context", item.description) + } + return b.String() +} + +func writeFishPreamble(b *strings.Builder) { b.WriteString("# Fish completion for ask.\n") b.WriteString("# Install as ~/.config/fish/completions/ask.fish or") b.WriteString(" $XDG_CONFIG_HOME/fish/completions/ask.fish.\n\n") +} + +func writeFishContextFunctions(b *strings.Builder) { + writeFishNeedsRootCompletionFunction(b) + writeFishDepContextFunction(b) + writeFishUUIDContextFunction(b) + writeFishDepUUIDContextFunction(b) +} + +func writeFishNeedsRootCompletionFunction(b *strings.Builder) { b.WriteString("function __ask_needs_root_completion\n") b.WriteString(" set -l tokens (commandline -opc)\n") b.WriteString(" if test (count $tokens) -le 1\n") @@ -52,6 +102,9 @@ func FishCompletion() string { b.WriteString(" end\n") b.WriteString(" return 0\n") b.WriteString("end\n\n") +} + +func writeFishDepContextFunction(b *strings.Builder) { b.WriteString("function __ask_in_dep_context\n") b.WriteString(" set -l tokens (commandline -opc)\n") b.WriteString(" if test (count $tokens) -lt 2\n") @@ -74,15 +127,70 @@ func FishCompletion() string { b.WriteString(" end\n") b.WriteString(" test $seen_dep -eq 1\n") b.WriteString("end\n\n") - b.WriteString("complete -c ask -f\n") - b.WriteString("complete -c ask -s j -l json -d 'Emit JSON output'\n") - for _, item := range askRootCompletionItems { - writeFishCompletionLine(&b, "__ask_needs_root_completion", item) - } - for _, item := range askDepCompletionItems { - writeFishCompletionLine(&b, "__ask_in_dep_context", item) - } - return b.String() +} + +func writeFishUUIDContextFunction(b *strings.Builder) { + b.WriteString("function __ask_in_uuid_context\n") + b.WriteString(" set -l tokens (commandline -opc)\n") + b.WriteString(" set -l positional\n") + b.WriteString(" for token in $tokens[2..-1]\n") + b.WriteString(" if string match -qr '^-' -- $token\n") + b.WriteString(" continue\n") + b.WriteString(" end\n") + b.WriteString(" set -a positional $token\n") + b.WriteString(" end\n") + b.WriteString(" if test (count $positional) -eq 0\n") + b.WriteString(" return 1\n") + b.WriteString(" end\n") + b.WriteString(" if test (count $positional) -gt 2\n") + b.WriteString(" return 1\n") + b.WriteString(" end\n") + b.WriteString(" switch $positional[1]\n") + b.WriteString(" case info annotate start stop done priority tag modify denotate delete\n") + b.WriteString(" return 0\n") + b.WriteString(" case '*'\n") + b.WriteString(" return 1\n") + b.WriteString(" end\n") + b.WriteString(" return 1\n") + b.WriteString("end\n\n") +} + +func writeFishDepUUIDContextFunction(b *strings.Builder) { + b.WriteString("function __ask_in_dep_uuid_context\n") + b.WriteString(" set -l tokens (commandline -opc)\n") + b.WriteString(" set -l positional\n") + b.WriteString(" for token in $tokens[2..-1]\n") + b.WriteString(" if string match -qr '^-' -- $token\n") + b.WriteString(" continue\n") + b.WriteString(" end\n") + b.WriteString(" set -a positional $token\n") + b.WriteString(" end\n") + b.WriteString(" if test (count $positional) -lt 2\n") + b.WriteString(" return 1\n") + b.WriteString(" end\n") + b.WriteString(" if test $positional[1] != dep\n") + b.WriteString(" return 1\n") + b.WriteString(" end\n") + b.WriteString(" switch $positional[2]\n") + b.WriteString(" case add rm\n") + b.WriteString(" if test (count $positional) -le 4\n") + b.WriteString(" return 0\n") + b.WriteString(" end\n") + b.WriteString(" case list\n") + b.WriteString(" if test (count $positional) -le 3\n") + b.WriteString(" return 0\n") + b.WriteString(" end\n") + b.WriteString(" case '*'\n") + b.WriteString(" return 1\n") + b.WriteString(" end\n") + b.WriteString(" return 1\n") + b.WriteString("end\n\n") +} + +func writeFishTaskUUIDFunction(b *strings.Builder) { + b.WriteString("function __ask_task_uuids\n") + b.WriteString(" command ask all --json 2>/dev/null | string match -r -a -g '\"uuid\":\"([^\"]+)\"'\n") + b.WriteString("end\n\n") } func writeFishCompletionLine(b *strings.Builder, condition string, item fishCompletionItem) { @@ -94,3 +202,11 @@ func writeFishCompletionLine(b *strings.Builder, condition string, item fishComp b.WriteString(strings.ReplaceAll(item.description, "'", "\\'")) b.WriteString("'\n") } + +func writeFishUUIDCompletionLine(b *strings.Builder, condition, description string) { + b.WriteString("complete -c ask -n '") + b.WriteString(condition) + b.WriteString("' -a '(__ask_task_uuids)' -d '") + b.WriteString(strings.ReplaceAll(description, "'", "\\'")) + b.WriteString("'\n") +} diff --git a/internal/askcli/completion_test.go b/internal/askcli/completion_test.go index e717575..439e483 100644 --- a/internal/askcli/completion_test.go +++ b/internal/askcli/completion_test.go @@ -35,6 +35,12 @@ func TestFishCompletion_IncludesCommandsAndExcludesExport(t *testing.T) { "complete -c ask -n '__ask_in_dep_context' -a 'add' -d 'Add a dependency'", "complete -c ask -n '__ask_in_dep_context' -a 'rm' -d 'Remove a dependency'", "complete -c ask -n '__ask_in_dep_context' -a 'list' -d 'List dependencies'", + "function __ask_task_uuids", + "command ask all --json 2>/dev/null", + "complete -c ask -n '__ask_in_uuid_context' -a '(__ask_task_uuids)' -d 'Show task details'", + "complete -c ask -n '__ask_in_dep_uuid_context' -a '(__ask_task_uuids)' -d 'Add a dependency'", + "complete -c ask -n '__ask_in_dep_uuid_context' -a '(__ask_task_uuids)' -d 'Remove a dependency'", + "complete -c ask -n '__ask_in_dep_uuid_context' -a '(__ask_task_uuids)' -d 'List dependencies'", } { if !strings.Contains(script, line) { t.Fatalf("script missing dep completion line %q", line) |
