flake-update-20260505
1/**
2 * Fish shell completion provider.
3 *
4 * Uses fish's native `complete -C` command which provides excellent completions
5 * for most tools automatically.
6 *
7 * Fish always has completions available (it's a core feature), so this never
8 * returns null for "user hasn't configured completions".
9 */
10
11import type { AutocompleteItem } from "@mariozechner/pi-tui";
12import { spawnSync } from "node:child_process";
13import type { CompletionResult, ShellCompletionProvider } from "./types.js";
14
15/**
16 * Get completions using fish's native `complete -C` command.
17 * Fish completions are excellent and cover most tools automatically.
18 */
19export function getFishCompletions(
20 commandLine: string,
21 cwd: string,
22 fishPath: string
23): CompletionResult | null {
24 // Extract prefix
25 const trimmed = commandLine.trimStart();
26 let prefix = "";
27 if (!trimmed.endsWith(" ")) {
28 const words = trimmed.split(/\s+/);
29 prefix = words[words.length - 1] || "";
30 }
31
32 try {
33 // Fish's complete -C gives us completions directly
34 const result = spawnSync(
35 fishPath,
36 ["-c", `complete -C ${JSON.stringify(commandLine)}`],
37 {
38 encoding: "utf-8",
39 timeout: 500,
40 maxBuffer: 1024 * 100,
41 cwd,
42 }
43 );
44
45 if (result.error || !result.stdout) {
46 return null;
47 }
48
49 // Fish output format: "completion\tdescription" (tab-separated)
50 const lines = result.stdout.trim().split("\n").filter(Boolean);
51 const items: AutocompleteItem[] = [];
52
53 for (const line of lines) {
54 const tabIndex = line.indexOf("\t");
55 if (tabIndex >= 0) {
56 const value = line.slice(0, tabIndex).trim();
57 const description = line.slice(tabIndex + 1).trim();
58 if (value) {
59 items.push({ value, label: value, description });
60 }
61 } else {
62 const value = line.trim();
63 if (value) {
64 items.push({ value, label: value });
65 }
66 }
67 }
68
69 if (items.length === 0) {
70 return null;
71 }
72
73 return {
74 items: items.slice(0, 30),
75 prefix,
76 };
77 } catch {
78 return null;
79 }
80}
81
82export const fishCompletionProvider: ShellCompletionProvider = {
83 getCompletions: getFishCompletions,
84};