diff options
| author | Paul Buetow <paul@buetow.org> | 2026-03-27 06:23:24 +0200 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-03-27 06:23:24 +0200 |
| commit | 2edeaa610553b667776010bf2e9f5596aaa8ddbd (patch) | |
| tree | b32b5c3946061f3e06ae274b5c4260358985f04f | |
| parent | 6b964400deb653d2c47aa8932ab5444346833b0d (diff) | |
task 73fabcf6-d4a1-4bf3-aae1-a390a734e517: extend ask selector completion
| -rw-r--r-- | docs/fish-completion.md | 3 | ||||
| -rw-r--r-- | internal/askcli/command_complete_uuids.go | 19 | ||||
| -rw-r--r-- | internal/askcli/command_complete_uuids_test.go | 24 | ||||
| -rw-r--r-- | internal/askcli/completion.go | 24 | ||||
| -rw-r--r-- | internal/askcli/completion_test.go | 10 | ||||
| -rw-r--r-- | internal/askcli/dispatch_test.go | 4 |
6 files changed, 58 insertions, 26 deletions
diff --git a/docs/fish-completion.md b/docs/fish-completion.md index 195d29a..d7e7289 100644 --- a/docs/fish-completion.md +++ b/docs/fish-completion.md @@ -3,7 +3,8 @@ The `ask` task-management CLI embeds its Fish completion script in the binary and prints it with `ask fish`. It completes the top-level `ask` subcommands and the nested `ask dep` operations. -It also completes task UUIDs for UUID-taking commands by reading the current project from `ask all --json` and filtering out completed and deleted tasks. +It also completes task selectors for UUID-taking commands by reading pending tasks through `ask complete-uuids`, which uses the local alias cache for stable short IDs. +Fish suggests each task's alias ID first and also keeps the raw UUID available as a fallback selector. The script preserves the global `--json` flag. Load it into the current Fish session: diff --git a/internal/askcli/command_complete_uuids.go b/internal/askcli/command_complete_uuids.go index 755c3bb..8d37a80 100644 --- a/internal/askcli/command_complete_uuids.go +++ b/internal/askcli/command_complete_uuids.go @@ -18,14 +18,27 @@ func (d Dispatcher) handleCompleteUUIDs(ctx context.Context, stdout, stderr io.W fmt.Fprintf(stderr, "error: failed to parse task data: %v\n", err) return 1, nil } - if _, err := ensureTaskAliases(tasks); err != nil { + aliases, err := ensureTaskAliases(tasks) + if err != nil { fmt.Fprintf(stderr, "warning: failed to update task alias cache: %v\n", err) + aliases = nil + } + for _, selector := range taskCompletionSelectors(tasks, aliases) { + _, _ = io.WriteString(stdout, selector+"\n") } + return 0, nil +} + +func taskCompletionSelectors(tasks []TaskExport, aliases map[string]string) []string { + selectors := make([]string, 0, len(tasks)*2) for _, task := range tasks { if task.UUID == "" { continue } - _, _ = io.WriteString(stdout, task.UUID+"\n") + if alias := displayTaskAlias(task.UUID, aliases); alias != "" && alias != task.UUID { + selectors = append(selectors, alias) + } + selectors = append(selectors, task.UUID) } - return 0, nil + return selectors } diff --git a/internal/askcli/command_complete_uuids_test.go b/internal/askcli/command_complete_uuids_test.go index 2c8fc34..4a68e51 100644 --- a/internal/askcli/command_complete_uuids_test.go +++ b/internal/askcli/command_complete_uuids_test.go @@ -39,8 +39,8 @@ func TestHandleCompleteUUIDs_PrintsPendingUUIDs(t *testing.T) { if code != 0 { t.Fatalf("handleCompleteUUIDs code = %d, want 0", code) } - if got := stdout.String(); got != "uuid-1\nuuid-2\n" { - t.Fatalf("stdout = %q, want UUID list", got) + if got := stdout.String(); got != "0\nuuid-1\n1\nuuid-2\n" { + t.Fatalf("stdout = %q, want alias-first selector list", got) } if stderr.Len() != 0 { t.Fatalf("stderr = %q, want empty", stderr.String()) @@ -109,9 +109,27 @@ func TestHandleCompleteUUIDs_WarnsOnInvalidAliasCache(t *testing.T) { t.Fatalf("handleCompleteUUIDs code = %d, want 0", code) } if got := stdout.String(); got != "uuid-1\n" { - t.Fatalf("stdout = %q, want UUID list", got) + t.Fatalf("stdout = %q, want UUID-only fallback list", got) } if !strings.Contains(stderr.String(), "failed to update task alias cache") { t.Fatalf("stderr = %q, want cache warning", stderr.String()) } } + +func TestTaskCompletionSelectors_SkipsMissingAndDuplicateAliases(t *testing.T) { + tasks := []TaskExport{ + {UUID: "uuid-1"}, + {UUID: ""}, + {UUID: "uuid-2"}, + } + aliases := map[string]string{ + "uuid-1": "0", + "uuid-2": "uuid-2", + } + + got := taskCompletionSelectors(tasks, aliases) + want := []string{"0", "uuid-1", "uuid-2"} + if strings.Join(got, "\n") != strings.Join(want, "\n") { + t.Fatalf("taskCompletionSelectors = %v, want %v", got, want) + } +} diff --git a/internal/askcli/completion.go b/internal/askcli/completion.go index 5684e33..ec7631e 100644 --- a/internal/askcli/completion.go +++ b/internal/askcli/completion.go @@ -57,7 +57,7 @@ func FishCompletionFor(binaryPath string) string { var b strings.Builder writeFishPreamble(&b) writeFishContextFunctions(&b) - writeFishTaskUUIDFunction(&b, binaryPath) + writeFishTaskSelectorFunction(&b, binaryPath) 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 { @@ -66,8 +66,8 @@ func FishCompletionFor(binaryPath string) string { for _, item := range askDepCompletionItems { writeFishCompletionLine(&b, "__ask_in_dep_context", item) } - writeFishUUIDCompletionLine(&b, "__ask_in_uuid_context", "Task UUID") - writeFishUUIDCompletionLine(&b, "__ask_in_dep_uuid_context", "Task UUID") + writeFishUUIDCompletionLine(&b, "__ask_in_uuid_context", "Task selector") + writeFishUUIDCompletionLine(&b, "__ask_in_dep_uuid_context", "Task selector") return b.String() } @@ -181,23 +181,23 @@ func writeFishDepUUIDContextFunction(b *strings.Builder) { b.WriteString("end\n\n") } -func writeFishTaskUUIDFunction(b *strings.Builder, binaryPath string) { - b.WriteString("function __ask_task_uuids\n") +func writeFishTaskSelectorFunction(b *strings.Builder, binaryPath string) { + b.WriteString("function __ask_task_selectors\n") b.WriteString(" set -l ask_bin ") b.WriteString(quoteFishString(binaryPath)) b.WriteString("\n") b.WriteString(" set -l now (date +%s)\n") - b.WriteString(" if set -q __ask_task_uuid_cache_until; and test $__ask_task_uuid_cache_until -ge $now\n") - b.WriteString(" printf '%s\\n' $__ask_task_uuid_cache\n") + b.WriteString(" if set -q __ask_task_selector_cache_until; and test $__ask_task_selector_cache_until -ge $now\n") + b.WriteString(" printf '%s\\n' $__ask_task_selector_cache\n") b.WriteString(" return 0\n") b.WriteString(" end\n") - b.WriteString(" set -l uuids (command $ask_bin complete-uuids 2>/dev/null)\n") + b.WriteString(" set -l selectors (command $ask_bin complete-uuids 2>/dev/null)\n") b.WriteString(" if test $status -ne 0\n") b.WriteString(" return 1\n") b.WriteString(" end\n") - b.WriteString(" set -g __ask_task_uuid_cache $uuids\n") - b.WriteString(" set -g __ask_task_uuid_cache_until (math $now + 2)\n") - b.WriteString(" printf '%s\\n' $uuids\n") + b.WriteString(" set -g __ask_task_selector_cache $selectors\n") + b.WriteString(" set -g __ask_task_selector_cache_until (math $now + 2)\n") + b.WriteString(" printf '%s\\n' $selectors\n") b.WriteString("end\n\n") } @@ -214,7 +214,7 @@ func writeFishCompletionLine(b *strings.Builder, condition string, item fishComp 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("' -a '(__ask_task_selectors)' -d '") b.WriteString(strings.ReplaceAll(description, "'", "\\'")) b.WriteString("'\n") } diff --git a/internal/askcli/completion_test.go b/internal/askcli/completion_test.go index ad25cbd..87a3ca9 100644 --- a/internal/askcli/completion_test.go +++ b/internal/askcli/completion_test.go @@ -17,11 +17,11 @@ 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", + "function __ask_task_selectors", `set -l ask_bin "ask"`, - "set -l uuids (command $ask_bin complete-uuids 2>/dev/null)", - "complete -c ask -n '__ask_in_uuid_context' -a '(__ask_task_uuids)' -d 'Task UUID'", - "complete -c ask -n '__ask_in_dep_uuid_context' -a '(__ask_task_uuids)' -d 'Task UUID'", + "set -l selectors (command $ask_bin complete-uuids 2>/dev/null)", + "complete -c ask -n '__ask_in_uuid_context' -a '(__ask_task_selectors)' -d 'Task selector'", + "complete -c ask -n '__ask_in_dep_uuid_context' -a '(__ask_task_selectors)' -d 'Task selector'", } { if !strings.Contains(script, line) { t.Fatalf("script missing dep completion line %q", line) @@ -39,7 +39,7 @@ func TestFishCompletionFor_EmbedsBinaryPath(t *testing.T) { script := FishCompletionFor(`/tmp/ask "$HOME"`) for _, line := range []string{ `set -l ask_bin "/tmp/ask \"\$HOME\""`, - "set -l uuids (command $ask_bin complete-uuids 2>/dev/null)", + "set -l selectors (command $ask_bin complete-uuids 2>/dev/null)", } { if !strings.Contains(script, line) { t.Fatalf("script missing %q", line) diff --git a/internal/askcli/dispatch_test.go b/internal/askcli/dispatch_test.go index 040f305..cd83e48 100644 --- a/internal/askcli/dispatch_test.go +++ b/internal/askcli/dispatch_test.go @@ -66,8 +66,8 @@ func TestDispatcher_CompleteUUIDsSubcommand(t *testing.T) { if code != 0 { t.Fatalf("complete-uuids code = %d, want 0", code) } - if got := stdout.String(); got != "uuid-1\n" { - t.Fatalf("stdout = %q, want UUID list", got) + if got := stdout.String(); got != "0\nuuid-1\n" { + t.Fatalf("stdout = %q, want selector list", got) } } |
