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};