politty 0.4.3 → 0.4.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{arg-registry-BUUhZ7JR.d.ts → arg-registry-2m40k1Et.d.ts} +1 -1
- package/dist/{arg-registry-BUUhZ7JR.d.ts.map → arg-registry-2m40k1Et.d.ts.map} +1 -1
- package/dist/augment.d.ts +1 -1
- package/dist/completion/index.cjs +16 -168
- package/dist/completion/index.d.cts +2 -77
- package/dist/completion/index.d.ts +2 -77
- package/dist/completion/index.js +3 -154
- package/dist/{zsh-CASZWn0o.cjs → completion-Df0eZ70u.cjs} +326 -37
- package/dist/completion-Df0eZ70u.cjs.map +1 -0
- package/dist/{zsh-hjvdI8uZ.js → completion-_AnQsWh9.js} +298 -27
- package/dist/completion-_AnQsWh9.js.map +1 -0
- package/dist/docs/index.cjs +276 -29
- package/dist/docs/index.cjs.map +1 -1
- package/dist/docs/index.d.cts +62 -7
- package/dist/docs/index.d.cts.map +1 -1
- package/dist/docs/index.d.ts +62 -7
- package/dist/docs/index.d.ts.map +1 -1
- package/dist/docs/index.js +271 -30
- package/dist/docs/index.js.map +1 -1
- package/dist/{value-completion-resolver-BQgHsX7b.d.cts → index-BZalbMeu.d.ts} +83 -4
- package/dist/index-BZalbMeu.d.ts.map +1 -0
- package/dist/{value-completion-resolver-C9LTGr0O.d.ts → index-C5-0RXiH.d.cts} +83 -4
- package/dist/index-C5-0RXiH.d.cts.map +1 -0
- package/dist/index.cjs +8 -7
- package/dist/index.d.cts +67 -13
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +68 -14
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -5
- package/dist/{lazy-BEDnSR0m.cjs → lazy-DHlvJiQQ.cjs} +44 -8
- package/dist/lazy-DHlvJiQQ.cjs.map +1 -0
- package/dist/{lazy-BrEg8SgI.js → lazy-DSyfzR-F.js} +38 -8
- package/dist/lazy-DSyfzR-F.js.map +1 -0
- package/dist/{runner-C4fSHJMe.cjs → runner-C1Aah5c5.cjs} +365 -23
- package/dist/runner-C1Aah5c5.cjs.map +1 -0
- package/dist/{runner-D6k4BgB4.js → runner-DjG0uBxQ.js} +365 -23
- package/dist/runner-DjG0uBxQ.js.map +1 -0
- package/dist/{schema-extractor-n9288WJ6.d.ts → schema-extractor-C9APqeSL.d.ts} +30 -3
- package/dist/schema-extractor-C9APqeSL.d.ts.map +1 -0
- package/dist/{schema-extractor-DFaAZzaY.d.cts → schema-extractor-CVf0J4An.d.cts} +29 -2
- package/dist/schema-extractor-CVf0J4An.d.cts.map +1 -0
- package/dist/{subcommand-router-CAzBsLSI.js → subcommand-router-CKuy6D2b.js} +2 -2
- package/dist/{subcommand-router-CAzBsLSI.js.map → subcommand-router-CKuy6D2b.js.map} +1 -1
- package/dist/{subcommand-router-ZjNjFaUL.cjs → subcommand-router-sZHhUP7b.cjs} +2 -2
- package/dist/{subcommand-router-ZjNjFaUL.cjs.map → subcommand-router-sZHhUP7b.cjs.map} +1 -1
- package/package.json +9 -9
- package/dist/completion/index.cjs.map +0 -1
- package/dist/completion/index.d.cts.map +0 -1
- package/dist/completion/index.d.ts.map +0 -1
- package/dist/completion/index.js.map +0 -1
- package/dist/lazy-BEDnSR0m.cjs.map +0 -1
- package/dist/lazy-BrEg8SgI.js.map +0 -1
- package/dist/runner-C4fSHJMe.cjs.map +0 -1
- package/dist/runner-D6k4BgB4.js.map +0 -1
- package/dist/schema-extractor-DFaAZzaY.d.cts.map +0 -1
- package/dist/schema-extractor-n9288WJ6.d.ts.map +0 -1
- package/dist/value-completion-resolver-BQgHsX7b.d.cts.map +0 -1
- package/dist/value-completion-resolver-C9LTGr0O.d.ts.map +0 -1
- package/dist/zsh-CASZWn0o.cjs.map +0 -1
- package/dist/zsh-hjvdI8uZ.js.map +0 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { i as extractFields, l as arg, r as resolveSubCommandMeta } from "./lazy-DSyfzR-F.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { execSync } from "node:child_process";
|
|
4
4
|
|
|
@@ -16,6 +16,30 @@ function defineCommand(config) {
|
|
|
16
16
|
examples: config.examples
|
|
17
17
|
};
|
|
18
18
|
}
|
|
19
|
+
/**
|
|
20
|
+
* Create a typed defineCommand factory with pre-bound global args type.
|
|
21
|
+
* This is the recommended pattern for type-safe global options.
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* // global-args.ts
|
|
26
|
+
* type GlobalArgsType = { verbose: boolean; config?: string };
|
|
27
|
+
* export const defineAppCommand = createDefineCommand<GlobalArgsType>();
|
|
28
|
+
*
|
|
29
|
+
* // commands/build.ts
|
|
30
|
+
* export const buildCommand = defineAppCommand({
|
|
31
|
+
* name: "build",
|
|
32
|
+
* args: z.object({ output: arg(z.string().default("dist")) }),
|
|
33
|
+
* run: (args) => {
|
|
34
|
+
* args.verbose; // typed via GlobalArgsType
|
|
35
|
+
* args.output; // typed via local args
|
|
36
|
+
* },
|
|
37
|
+
* });
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
function createDefineCommand() {
|
|
41
|
+
return defineCommand;
|
|
42
|
+
}
|
|
19
43
|
|
|
20
44
|
//#endregion
|
|
21
45
|
//#region src/completion/value-completion-resolver.ts
|
|
@@ -68,6 +92,13 @@ function resolveValueCompletion(field) {
|
|
|
68
92
|
/**
|
|
69
93
|
* Sanitize a name for use as a shell function/variable identifier.
|
|
70
94
|
* Replaces any character that is not alphanumeric or underscore with underscore.
|
|
95
|
+
*
|
|
96
|
+
* Note: This is not injective -- distinct names may produce the same output
|
|
97
|
+
* (e.g., "foo-bar" and "foo_bar" both become "foo_bar"). When used for nested
|
|
98
|
+
* path encoding (`path.map(sanitize).join("_")`), cross-level collisions are
|
|
99
|
+
* theoretically possible (e.g., "foo-bar:baz" vs "foo:bar-baz") but extremely
|
|
100
|
+
* unlikely in real CLI designs. If collision-safety is needed, sanitize must be
|
|
101
|
+
* replaced with an injective encoding.
|
|
71
102
|
*/
|
|
72
103
|
function sanitize(name) {
|
|
73
104
|
return name.replace(/[^a-zA-Z0-9_]/g, "_");
|
|
@@ -147,29 +178,80 @@ function extractSubcommand(name, command) {
|
|
|
147
178
|
positionals: extractCompletablePositionals(command)
|
|
148
179
|
};
|
|
149
180
|
}
|
|
181
|
+
/** Join parent and child with a separator, omitting separator when parent is empty. */
|
|
182
|
+
function joinPrefix(parent, child, sep) {
|
|
183
|
+
return parent ? `${parent}${sep}${child}` : child;
|
|
184
|
+
}
|
|
150
185
|
/**
|
|
151
186
|
* Collect opt-takes-value case entries for a subcommand tree.
|
|
152
|
-
* Used by bash and zsh generators (identical case syntax: `
|
|
187
|
+
* Used by bash and zsh generators (identical case syntax: `path:--opt) return 0 ;;`).
|
|
188
|
+
* parentPath is a colon-delimited path (e.g., "" for root, "workspace:user" for nested).
|
|
153
189
|
*/
|
|
154
|
-
function optTakesValueEntries(sub,
|
|
190
|
+
function optTakesValueEntries(sub, parentPath) {
|
|
155
191
|
const lines = [];
|
|
156
192
|
for (const opt of sub.options) if (opt.takesValue) {
|
|
157
|
-
const patterns = [`${
|
|
158
|
-
if (opt.alias) patterns.push(`${
|
|
193
|
+
const patterns = [`${parentPath}:--${opt.cliName}`];
|
|
194
|
+
if (opt.alias) patterns.push(`${parentPath}:-${opt.alias}`);
|
|
159
195
|
lines.push(` ${patterns.join("|")}) return 0 ;;`);
|
|
160
196
|
}
|
|
161
|
-
for (const child of getVisibleSubs(sub.subcommands)) lines.push(...optTakesValueEntries(child, child.name));
|
|
197
|
+
for (const child of getVisibleSubs(sub.subcommands)) lines.push(...optTakesValueEntries(child, joinPrefix(parentPath, child.name, ":")));
|
|
162
198
|
return lines;
|
|
163
199
|
}
|
|
164
200
|
/**
|
|
201
|
+
* Recursively collect all subcommand route entries.
|
|
202
|
+
* Returns entries used by all shell generators for both dispatch routing
|
|
203
|
+
* and subcommand lookup (is_subcmd) tables.
|
|
204
|
+
*/
|
|
205
|
+
function collectRouteEntries(sub, parentPath = "", parentFunc = "") {
|
|
206
|
+
const entries = [];
|
|
207
|
+
for (const child of getVisibleSubs(sub.subcommands)) {
|
|
208
|
+
const pathStr = joinPrefix(parentPath, child.name, ":");
|
|
209
|
+
const funcSuffix = joinPrefix(parentFunc, sanitize(child.name), "_");
|
|
210
|
+
entries.push(...collectRouteEntries(child, pathStr, funcSuffix));
|
|
211
|
+
entries.push({
|
|
212
|
+
pathStr,
|
|
213
|
+
funcSuffix,
|
|
214
|
+
lookupPattern: `${parentPath}:${child.name}`
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
return entries;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Generate is_subcmd case/switch body lines (bash/zsh case syntax).
|
|
221
|
+
* Returns lines for the case statement body only (caller wraps in function).
|
|
222
|
+
*/
|
|
223
|
+
function isSubcmdCaseLines(routeEntries) {
|
|
224
|
+
return routeEntries.map((r) => ` ${r.lookupPattern}) return 0 ;;`);
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* Recursively merge global options into a subcommand and all its descendants.
|
|
228
|
+
* Avoids duplicates by checking existing option names.
|
|
229
|
+
*/
|
|
230
|
+
function propagateGlobalOptions(sub, globalOptions) {
|
|
231
|
+
const existingNames = new Set(sub.options.map((o) => o.name));
|
|
232
|
+
const newOpts = globalOptions.filter((o) => !existingNames.has(o.name));
|
|
233
|
+
sub.options = [...sub.options, ...newOpts];
|
|
234
|
+
for (const child of sub.subcommands) propagateGlobalOptions(child, globalOptions);
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
165
237
|
* Extract completion data from a command tree
|
|
238
|
+
*
|
|
239
|
+
* @param command - The root command
|
|
240
|
+
* @param programName - Program name for completion scripts
|
|
241
|
+
* @param globalArgsSchema - Optional global args schema. When provided, global options
|
|
242
|
+
* are derived from this schema instead of the root command's options.
|
|
166
243
|
*/
|
|
167
|
-
function extractCompletionData(command, programName) {
|
|
244
|
+
function extractCompletionData(command, programName, globalArgsSchema) {
|
|
168
245
|
const rootSubcommand = extractSubcommand(programName, command);
|
|
246
|
+
let globalOptions;
|
|
247
|
+
if (globalArgsSchema) {
|
|
248
|
+
globalOptions = extractFields(globalArgsSchema).fields.filter((field) => !field.positional).map(fieldToOption);
|
|
249
|
+
propagateGlobalOptions(rootSubcommand, globalOptions);
|
|
250
|
+
} else globalOptions = rootSubcommand.options;
|
|
169
251
|
return {
|
|
170
252
|
command: rootSubcommand,
|
|
171
253
|
programName,
|
|
172
|
-
globalOptions
|
|
254
|
+
globalOptions
|
|
173
255
|
};
|
|
174
256
|
}
|
|
175
257
|
|
|
@@ -317,8 +399,9 @@ function generateSubHandler$2(sub, fn, path) {
|
|
|
317
399
|
for (const child of visibleSubs) lines.push(...generateSubHandler$2(child, fn, fullPath));
|
|
318
400
|
lines.push(`${funcName}() {`);
|
|
319
401
|
lines.push(...valueCompletionBlocks(sub.options));
|
|
320
|
-
|
|
321
|
-
lines.push(` if [[ -
|
|
402
|
+
const fullPathStr = fullPath.join(":");
|
|
403
|
+
lines.push(` if [[ -z "$_inline_prefix" ]] && __${fn}_opt_takes_value "${fullPathStr}" "$_prev"; then return; fi`);
|
|
404
|
+
lines.push(` if [[ -n "$_inline_prefix" ]] && __${fn}_opt_takes_value "${fullPathStr}" "\${_inline_prefix%=}"; then return; fi`);
|
|
322
405
|
if (sub.positionals.length > 0) {
|
|
323
406
|
lines.push(` if (( _after_dd )); then`);
|
|
324
407
|
lines.push(...positionalBlock$2(sub.positionals).map((l) => ` ${l}`));
|
|
@@ -343,7 +426,7 @@ function generateSubHandler$2(sub, fn, path) {
|
|
|
343
426
|
}
|
|
344
427
|
function generateBashCompletion(command, options) {
|
|
345
428
|
const { programName } = options;
|
|
346
|
-
const data = extractCompletionData(command, programName);
|
|
429
|
+
const data = extractCompletionData(command, programName, options.globalArgsSchema);
|
|
347
430
|
const fn = sanitize(programName);
|
|
348
431
|
const root = data.command;
|
|
349
432
|
const visibleSubs = getVisibleSubs(root.subcommands);
|
|
@@ -367,6 +450,16 @@ function generateBashCompletion(command, options) {
|
|
|
367
450
|
lines.push(` return 1`);
|
|
368
451
|
lines.push(`}`);
|
|
369
452
|
lines.push(``);
|
|
453
|
+
const routeEntries = collectRouteEntries(root);
|
|
454
|
+
if (routeEntries.length > 0) {
|
|
455
|
+
lines.push(`__${fn}_is_subcmd() {`);
|
|
456
|
+
lines.push(` case "$1:$2" in`);
|
|
457
|
+
lines.push(...isSubcmdCaseLines(routeEntries));
|
|
458
|
+
lines.push(` esac`);
|
|
459
|
+
lines.push(` return 1`);
|
|
460
|
+
lines.push(`}`);
|
|
461
|
+
lines.push(``);
|
|
462
|
+
}
|
|
370
463
|
for (const sub of visibleSubs) lines.push(...generateSubHandler$2(sub, fn, []));
|
|
371
464
|
lines.push(`__${fn}_complete_root() {`);
|
|
372
465
|
lines.push(...valueCompletionBlocks(root.options));
|
|
@@ -395,7 +488,7 @@ function generateBashCompletion(command, options) {
|
|
|
395
488
|
lines.push(` fi`);
|
|
396
489
|
lines.push(`}`);
|
|
397
490
|
lines.push(``);
|
|
398
|
-
const subRouting =
|
|
491
|
+
const subRouting = routeEntries.map((r) => ` ${r.pathStr}) __${fn}_complete_${r.funcSuffix} ;;`).join("\n");
|
|
399
492
|
lines.push(`_${fn}_completions() {`);
|
|
400
493
|
lines.push(` COMPREPLY=()`);
|
|
401
494
|
lines.push(``);
|
|
@@ -439,7 +532,7 @@ function generateBashCompletion(command, options) {
|
|
|
439
532
|
lines.push(` __${fn}_opt_takes_value "$_subcmd" "$_w" && _skip_next=1`);
|
|
440
533
|
lines.push(` (( _j++ )); continue`);
|
|
441
534
|
lines.push(` fi`);
|
|
442
|
-
if (
|
|
535
|
+
if (routeEntries.length > 0) lines.push(` if __${fn}_is_subcmd "$_subcmd" "$_w"; then _subcmd="\${_subcmd:+\${_subcmd}:}$_w"; _used_opts=(); _pos_count=0; else (( _pos_count++ )); fi`);
|
|
443
536
|
else lines.push(` (( _pos_count++ ))`);
|
|
444
537
|
lines.push(` (( _j++ ))`);
|
|
445
538
|
lines.push(` done`);
|
|
@@ -1112,7 +1205,8 @@ function generateSubHandler$1(sub, fn, path) {
|
|
|
1112
1205
|
for (const child of visibleSubs) lines.push(...generateSubHandler$1(child, fn, fullPath));
|
|
1113
1206
|
lines.push(`function ${funcName} --no-scope-shadowing`);
|
|
1114
1207
|
lines.push(...valueCompletionBlock$1(sub.options));
|
|
1115
|
-
|
|
1208
|
+
const fullPathStr = fullPath.join(":");
|
|
1209
|
+
lines.push(` if __${fn}_opt_takes_value "${fullPathStr}" "$_prev"; return; end`);
|
|
1116
1210
|
if (sub.positionals.length > 0) {
|
|
1117
1211
|
lines.push(` if test $_after_dd -eq 1`);
|
|
1118
1212
|
lines.push(...positionalBlock$1(sub.positionals).map((l) => ` ${l}`));
|
|
@@ -1133,20 +1227,23 @@ function generateSubHandler$1(sub, fn, path) {
|
|
|
1133
1227
|
return lines;
|
|
1134
1228
|
}
|
|
1135
1229
|
/** Generate opt-takes-value entries for fish switch cases */
|
|
1136
|
-
function optTakesValueCases(sub,
|
|
1230
|
+
function optTakesValueCases(sub, parentPath) {
|
|
1137
1231
|
const lines = [];
|
|
1138
1232
|
for (const opt of sub.options) if (opt.takesValue) {
|
|
1139
|
-
const patterns = [`"${
|
|
1140
|
-
if (opt.alias) patterns.push(`"${
|
|
1233
|
+
const patterns = [`"${parentPath}:--${opt.cliName}"`];
|
|
1234
|
+
if (opt.alias) patterns.push(`"${parentPath}:-${opt.alias}"`);
|
|
1141
1235
|
lines.push(` case ${patterns.join(" ")}`);
|
|
1142
1236
|
lines.push(` return 0`);
|
|
1143
1237
|
}
|
|
1144
|
-
for (const child of getVisibleSubs(sub.subcommands))
|
|
1238
|
+
for (const child of getVisibleSubs(sub.subcommands)) {
|
|
1239
|
+
const childPath = parentPath ? `${parentPath}:${child.name}` : child.name;
|
|
1240
|
+
lines.push(...optTakesValueCases(child, childPath));
|
|
1241
|
+
}
|
|
1145
1242
|
return lines;
|
|
1146
1243
|
}
|
|
1147
1244
|
function generateFishCompletion(command, options) {
|
|
1148
1245
|
const { programName } = options;
|
|
1149
|
-
const data = extractCompletionData(command, programName);
|
|
1246
|
+
const data = extractCompletionData(command, programName, options.globalArgsSchema);
|
|
1150
1247
|
const fn = sanitize(programName);
|
|
1151
1248
|
const root = data.command;
|
|
1152
1249
|
const visibleSubs = getVisibleSubs(root.subcommands);
|
|
@@ -1170,6 +1267,19 @@ function generateFishCompletion(command, options) {
|
|
|
1170
1267
|
lines.push(` return 1`);
|
|
1171
1268
|
lines.push(`end`);
|
|
1172
1269
|
lines.push(``);
|
|
1270
|
+
const routeEntries = collectRouteEntries(root);
|
|
1271
|
+
if (routeEntries.length > 0) {
|
|
1272
|
+
lines.push(`function __${fn}_is_subcmd`);
|
|
1273
|
+
lines.push(` switch "$argv[1]:$argv[2]"`);
|
|
1274
|
+
for (const r of routeEntries) {
|
|
1275
|
+
lines.push(` case "${r.lookupPattern}"`);
|
|
1276
|
+
lines.push(` return 0`);
|
|
1277
|
+
}
|
|
1278
|
+
lines.push(` end`);
|
|
1279
|
+
lines.push(` return 1`);
|
|
1280
|
+
lines.push(`end`);
|
|
1281
|
+
lines.push(``);
|
|
1282
|
+
}
|
|
1173
1283
|
for (const sub of visibleSubs) lines.push(...generateSubHandler$1(sub, fn, []));
|
|
1174
1284
|
lines.push(`function __${fn}_complete_root --no-scope-shadowing`);
|
|
1175
1285
|
lines.push(...valueCompletionBlock$1(root.options));
|
|
@@ -1232,13 +1342,13 @@ function generateFishCompletion(command, options) {
|
|
|
1232
1342
|
lines.push(` __${fn}_opt_takes_value "$_subcmd" "$_w"; and set _skip_next 1`);
|
|
1233
1343
|
lines.push(` set _j (math $_j + 1); continue`);
|
|
1234
1344
|
lines.push(` end`);
|
|
1235
|
-
if (
|
|
1345
|
+
if (routeEntries.length > 0) lines.push(` if __${fn}_is_subcmd "$_subcmd" "$_w"; test -n "$_subcmd"; and set _subcmd "$_subcmd:$_w"; or set _subcmd "$_w"; set _used_opts; set _pos_count 0; else; set _pos_count (math $_pos_count + 1); end`);
|
|
1236
1346
|
else lines.push(` set _pos_count (math $_pos_count + 1)`);
|
|
1237
1347
|
lines.push(` set _j (math $_j + 1)`);
|
|
1238
1348
|
lines.push(` end`);
|
|
1239
1349
|
lines.push(``);
|
|
1240
1350
|
lines.push(` switch "$_subcmd"`);
|
|
1241
|
-
for (const
|
|
1351
|
+
for (const r of routeEntries) lines.push(` case "${r.pathStr}"; __${fn}_complete_${r.funcSuffix}`);
|
|
1242
1352
|
lines.push(` case '*'; __${fn}_complete_root`);
|
|
1243
1353
|
lines.push(` end`);
|
|
1244
1354
|
lines.push(`end`);
|
|
@@ -1357,7 +1467,8 @@ function generateSubHandler(sub, fn, path) {
|
|
|
1357
1467
|
lines.push(`${funcName}() {`);
|
|
1358
1468
|
lines.push(` local -a _vals=()`);
|
|
1359
1469
|
lines.push(...valueCompletionBlock(sub.options, fn));
|
|
1360
|
-
|
|
1470
|
+
const fullPathStr = fullPath.join(":");
|
|
1471
|
+
lines.push(` if __${fn}_opt_takes_value "${fullPathStr}" "\${words[CURRENT-1]}"; then return 0; fi`);
|
|
1361
1472
|
if (sub.positionals.length > 0) {
|
|
1362
1473
|
lines.push(` if (( _after_dd )); then`);
|
|
1363
1474
|
lines.push(...positionalBlock(sub.positionals, fn).map((l) => ` ${l}`));
|
|
@@ -1384,7 +1495,7 @@ function generateSubHandler(sub, fn, path) {
|
|
|
1384
1495
|
}
|
|
1385
1496
|
function generateZshCompletion(command, options) {
|
|
1386
1497
|
const { programName } = options;
|
|
1387
|
-
const data = extractCompletionData(command, programName);
|
|
1498
|
+
const data = extractCompletionData(command, programName, options.globalArgsSchema);
|
|
1388
1499
|
const fn = sanitize(programName);
|
|
1389
1500
|
const root = data.command;
|
|
1390
1501
|
const visibleSubs = getVisibleSubs(root.subcommands);
|
|
@@ -1419,6 +1530,16 @@ function generateZshCompletion(command, options) {
|
|
|
1419
1530
|
lines.push(` return 1`);
|
|
1420
1531
|
lines.push(`}`);
|
|
1421
1532
|
lines.push(``);
|
|
1533
|
+
const routeEntries = collectRouteEntries(root);
|
|
1534
|
+
if (routeEntries.length > 0) {
|
|
1535
|
+
lines.push(`__${fn}_is_subcmd() {`);
|
|
1536
|
+
lines.push(` case "$1:$2" in`);
|
|
1537
|
+
lines.push(...isSubcmdCaseLines(routeEntries));
|
|
1538
|
+
lines.push(` esac`);
|
|
1539
|
+
lines.push(` return 1`);
|
|
1540
|
+
lines.push(`}`);
|
|
1541
|
+
lines.push(``);
|
|
1542
|
+
}
|
|
1422
1543
|
for (const sub of visibleSubs) lines.push(...generateSubHandler(sub, fn, []));
|
|
1423
1544
|
lines.push(`__${fn}_complete_root() {`);
|
|
1424
1545
|
lines.push(` local -a _vals=()`);
|
|
@@ -1449,7 +1570,7 @@ function generateZshCompletion(command, options) {
|
|
|
1449
1570
|
lines.push(` fi`);
|
|
1450
1571
|
lines.push(`}`);
|
|
1451
1572
|
lines.push(``);
|
|
1452
|
-
const subRouting =
|
|
1573
|
+
const subRouting = routeEntries.map((r) => ` ${r.pathStr}) __${fn}_complete_${r.funcSuffix} ;;`).join("\n");
|
|
1453
1574
|
lines.push(`_${fn}() {`);
|
|
1454
1575
|
lines.push(` (( CURRENT )) || CURRENT=\${#words}`);
|
|
1455
1576
|
lines.push(``);
|
|
@@ -1468,7 +1589,7 @@ function generateZshCompletion(command, options) {
|
|
|
1468
1589
|
lines.push(` __${fn}_opt_takes_value "$_subcmd" "$_w" && _skip_next=1`);
|
|
1469
1590
|
lines.push(` (( _j++ )); continue`);
|
|
1470
1591
|
lines.push(` fi`);
|
|
1471
|
-
if (
|
|
1592
|
+
if (routeEntries.length > 0) lines.push(` if __${fn}_is_subcmd "$_subcmd" "$_w"; then _subcmd="\${_subcmd:+\${_subcmd}:}$_w"; _used_opts=(); _pos_count=0; else (( _pos_count++ )); fi`);
|
|
1472
1593
|
else lines.push(` (( _pos_count++ ))`);
|
|
1473
1594
|
lines.push(` (( _j++ ))`);
|
|
1474
1595
|
lines.push(` done`);
|
|
@@ -1504,5 +1625,155 @@ source ~/.zshrc`
|
|
|
1504
1625
|
}
|
|
1505
1626
|
|
|
1506
1627
|
//#endregion
|
|
1507
|
-
|
|
1508
|
-
|
|
1628
|
+
//#region src/completion/index.ts
|
|
1629
|
+
/**
|
|
1630
|
+
* Shell completion generation module
|
|
1631
|
+
*
|
|
1632
|
+
* Provides utilities to generate shell completion scripts for bash, zsh, and fish.
|
|
1633
|
+
*
|
|
1634
|
+
* @example
|
|
1635
|
+
* ```typescript
|
|
1636
|
+
* import { generateCompletion, createCompletionCommand } from "politty/completion";
|
|
1637
|
+
*
|
|
1638
|
+
* // Generate completion script directly
|
|
1639
|
+
* const result = generateCompletion(myCommand, {
|
|
1640
|
+
* shell: "bash",
|
|
1641
|
+
* programName: "mycli"
|
|
1642
|
+
* });
|
|
1643
|
+
* console.log(result.script);
|
|
1644
|
+
*
|
|
1645
|
+
* // Or add a completion subcommand to your CLI
|
|
1646
|
+
* const mainCommand = withCompletionCommand(
|
|
1647
|
+
* defineCommand({
|
|
1648
|
+
* name: "mycli",
|
|
1649
|
+
* subCommands: { ... },
|
|
1650
|
+
* }),
|
|
1651
|
+
* );
|
|
1652
|
+
* ```
|
|
1653
|
+
*/
|
|
1654
|
+
/**
|
|
1655
|
+
* Generate completion script for the specified shell
|
|
1656
|
+
*/
|
|
1657
|
+
function generateCompletion(command, options) {
|
|
1658
|
+
switch (options.shell) {
|
|
1659
|
+
case "bash": return generateBashCompletion(command, options);
|
|
1660
|
+
case "zsh": return generateZshCompletion(command, options);
|
|
1661
|
+
case "fish": return generateFishCompletion(command, options);
|
|
1662
|
+
default: throw new Error(`Unsupported shell: ${options.shell}`);
|
|
1663
|
+
}
|
|
1664
|
+
}
|
|
1665
|
+
/**
|
|
1666
|
+
* Get the list of supported shells
|
|
1667
|
+
*/
|
|
1668
|
+
function getSupportedShells() {
|
|
1669
|
+
return [
|
|
1670
|
+
"bash",
|
|
1671
|
+
"zsh",
|
|
1672
|
+
"fish"
|
|
1673
|
+
];
|
|
1674
|
+
}
|
|
1675
|
+
/**
|
|
1676
|
+
* Detect the current shell from environment
|
|
1677
|
+
*/
|
|
1678
|
+
function detectShell() {
|
|
1679
|
+
const shellName = (process.env.SHELL || "").split("/").pop()?.toLowerCase() || "";
|
|
1680
|
+
if (shellName.includes("bash")) return "bash";
|
|
1681
|
+
if (shellName.includes("zsh")) return "zsh";
|
|
1682
|
+
if (shellName.includes("fish")) return "fish";
|
|
1683
|
+
return null;
|
|
1684
|
+
}
|
|
1685
|
+
/**
|
|
1686
|
+
* Schema for the completion command arguments
|
|
1687
|
+
*/
|
|
1688
|
+
const completionArgsSchema = z.object({
|
|
1689
|
+
shell: arg(z.enum([
|
|
1690
|
+
"bash",
|
|
1691
|
+
"zsh",
|
|
1692
|
+
"fish"
|
|
1693
|
+
]).optional().describe("Shell type (auto-detected if not specified)"), {
|
|
1694
|
+
positional: true,
|
|
1695
|
+
description: "Shell type (bash, zsh, or fish)",
|
|
1696
|
+
placeholder: "SHELL"
|
|
1697
|
+
}),
|
|
1698
|
+
instructions: arg(z.boolean().default(false), {
|
|
1699
|
+
alias: "i",
|
|
1700
|
+
description: "Show installation instructions"
|
|
1701
|
+
})
|
|
1702
|
+
});
|
|
1703
|
+
/**
|
|
1704
|
+
* Create a completion subcommand for your CLI
|
|
1705
|
+
*
|
|
1706
|
+
* This creates a ready-to-use subcommand that generates completion scripts.
|
|
1707
|
+
*
|
|
1708
|
+
* @example
|
|
1709
|
+
* ```typescript
|
|
1710
|
+
* const mainCommand = defineCommand({
|
|
1711
|
+
* name: "mycli",
|
|
1712
|
+
* subCommands: {
|
|
1713
|
+
* completion: createCompletionCommand(mainCommand)
|
|
1714
|
+
* }
|
|
1715
|
+
* });
|
|
1716
|
+
* ```
|
|
1717
|
+
*/
|
|
1718
|
+
function createCompletionCommand(rootCommand, programName, globalArgsSchema) {
|
|
1719
|
+
const resolvedProgramName = programName ?? rootCommand.name;
|
|
1720
|
+
if (!rootCommand.subCommands?.__complete) rootCommand.subCommands = {
|
|
1721
|
+
...rootCommand.subCommands,
|
|
1722
|
+
__complete: createDynamicCompleteCommand(rootCommand, resolvedProgramName)
|
|
1723
|
+
};
|
|
1724
|
+
return defineCommand({
|
|
1725
|
+
name: "completion",
|
|
1726
|
+
description: "Generate shell completion script",
|
|
1727
|
+
args: completionArgsSchema,
|
|
1728
|
+
run(args) {
|
|
1729
|
+
const shellType = args.shell || detectShell();
|
|
1730
|
+
if (!shellType) {
|
|
1731
|
+
console.error("Could not detect shell type. Please specify one of: bash, zsh, fish");
|
|
1732
|
+
process.exitCode = 1;
|
|
1733
|
+
return;
|
|
1734
|
+
}
|
|
1735
|
+
const result = generateCompletion(rootCommand, {
|
|
1736
|
+
shell: shellType,
|
|
1737
|
+
programName: resolvedProgramName,
|
|
1738
|
+
includeDescriptions: true,
|
|
1739
|
+
...globalArgsSchema !== void 0 && { globalArgsSchema }
|
|
1740
|
+
});
|
|
1741
|
+
if (args.instructions) console.log(result.installInstructions);
|
|
1742
|
+
else console.log(result.script);
|
|
1743
|
+
}
|
|
1744
|
+
});
|
|
1745
|
+
}
|
|
1746
|
+
/**
|
|
1747
|
+
* Wrap a command with a completion subcommand
|
|
1748
|
+
*
|
|
1749
|
+
* This avoids circular references that occur when a command references itself
|
|
1750
|
+
* in its subCommands (e.g., for completion generation).
|
|
1751
|
+
*
|
|
1752
|
+
* @param command - The command to wrap
|
|
1753
|
+
* @param options - Options including programName
|
|
1754
|
+
* @returns A new command with the completion subcommand added
|
|
1755
|
+
*
|
|
1756
|
+
* @example
|
|
1757
|
+
* ```typescript
|
|
1758
|
+
* const mainCommand = withCompletionCommand(
|
|
1759
|
+
* defineCommand({
|
|
1760
|
+
* name: "mycli",
|
|
1761
|
+
* subCommands: { ... },
|
|
1762
|
+
* }),
|
|
1763
|
+
* );
|
|
1764
|
+
* ```
|
|
1765
|
+
*/
|
|
1766
|
+
function withCompletionCommand(command, options) {
|
|
1767
|
+
const { programName, globalArgsSchema } = typeof options === "string" ? { programName: options } : options ?? {};
|
|
1768
|
+
const wrappedCommand = { ...command };
|
|
1769
|
+
wrappedCommand.subCommands = {
|
|
1770
|
+
...command.subCommands,
|
|
1771
|
+
completion: createCompletionCommand(wrappedCommand, programName, globalArgsSchema),
|
|
1772
|
+
__complete: createDynamicCompleteCommand(wrappedCommand, programName)
|
|
1773
|
+
};
|
|
1774
|
+
return wrappedCommand;
|
|
1775
|
+
}
|
|
1776
|
+
|
|
1777
|
+
//#endregion
|
|
1778
|
+
export { withCompletionCommand as a, formatForShell as c, generateCandidates as d, extractCompletionData as f, defineCommand as g, createDefineCommand as h, getSupportedShells as i, parseCompletionContext as l, resolveValueCompletion as m, detectShell as n, createDynamicCompleteCommand as o, extractPositionals as p, generateCompletion as r, hasCompleteCommand as s, createCompletionCommand as t, CompletionDirective as u };
|
|
1779
|
+
//# sourceMappingURL=completion-_AnQsWh9.js.map
|