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,5 +1,5 @@
|
|
|
1
|
-
const require_subcommand_router = require('./subcommand-router-
|
|
2
|
-
const require_lazy = require('./lazy-
|
|
1
|
+
const require_subcommand_router = require('./subcommand-router-sZHhUP7b.cjs');
|
|
2
|
+
const require_lazy = require('./lazy-DHlvJiQQ.cjs');
|
|
3
3
|
let zod = require("zod");
|
|
4
4
|
let node_child_process = require("node:child_process");
|
|
5
5
|
|
|
@@ -17,6 +17,30 @@ function defineCommand(config) {
|
|
|
17
17
|
examples: config.examples
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
|
+
/**
|
|
21
|
+
* Create a typed defineCommand factory with pre-bound global args type.
|
|
22
|
+
* This is the recommended pattern for type-safe global options.
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* // global-args.ts
|
|
27
|
+
* type GlobalArgsType = { verbose: boolean; config?: string };
|
|
28
|
+
* export const defineAppCommand = createDefineCommand<GlobalArgsType>();
|
|
29
|
+
*
|
|
30
|
+
* // commands/build.ts
|
|
31
|
+
* export const buildCommand = defineAppCommand({
|
|
32
|
+
* name: "build",
|
|
33
|
+
* args: z.object({ output: arg(z.string().default("dist")) }),
|
|
34
|
+
* run: (args) => {
|
|
35
|
+
* args.verbose; // typed via GlobalArgsType
|
|
36
|
+
* args.output; // typed via local args
|
|
37
|
+
* },
|
|
38
|
+
* });
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
function createDefineCommand() {
|
|
42
|
+
return defineCommand;
|
|
43
|
+
}
|
|
20
44
|
|
|
21
45
|
//#endregion
|
|
22
46
|
//#region src/completion/value-completion-resolver.ts
|
|
@@ -69,6 +93,13 @@ function resolveValueCompletion(field) {
|
|
|
69
93
|
/**
|
|
70
94
|
* Sanitize a name for use as a shell function/variable identifier.
|
|
71
95
|
* Replaces any character that is not alphanumeric or underscore with underscore.
|
|
96
|
+
*
|
|
97
|
+
* Note: This is not injective -- distinct names may produce the same output
|
|
98
|
+
* (e.g., "foo-bar" and "foo_bar" both become "foo_bar"). When used for nested
|
|
99
|
+
* path encoding (`path.map(sanitize).join("_")`), cross-level collisions are
|
|
100
|
+
* theoretically possible (e.g., "foo-bar:baz" vs "foo:bar-baz") but extremely
|
|
101
|
+
* unlikely in real CLI designs. If collision-safety is needed, sanitize must be
|
|
102
|
+
* replaced with an injective encoding.
|
|
72
103
|
*/
|
|
73
104
|
function sanitize(name) {
|
|
74
105
|
return name.replace(/[^a-zA-Z0-9_]/g, "_");
|
|
@@ -148,29 +179,80 @@ function extractSubcommand(name, command) {
|
|
|
148
179
|
positionals: extractCompletablePositionals(command)
|
|
149
180
|
};
|
|
150
181
|
}
|
|
182
|
+
/** Join parent and child with a separator, omitting separator when parent is empty. */
|
|
183
|
+
function joinPrefix(parent, child, sep) {
|
|
184
|
+
return parent ? `${parent}${sep}${child}` : child;
|
|
185
|
+
}
|
|
151
186
|
/**
|
|
152
187
|
* Collect opt-takes-value case entries for a subcommand tree.
|
|
153
|
-
* Used by bash and zsh generators (identical case syntax: `
|
|
188
|
+
* Used by bash and zsh generators (identical case syntax: `path:--opt) return 0 ;;`).
|
|
189
|
+
* parentPath is a colon-delimited path (e.g., "" for root, "workspace:user" for nested).
|
|
154
190
|
*/
|
|
155
|
-
function optTakesValueEntries(sub,
|
|
191
|
+
function optTakesValueEntries(sub, parentPath) {
|
|
156
192
|
const lines = [];
|
|
157
193
|
for (const opt of sub.options) if (opt.takesValue) {
|
|
158
|
-
const patterns = [`${
|
|
159
|
-
if (opt.alias) patterns.push(`${
|
|
194
|
+
const patterns = [`${parentPath}:--${opt.cliName}`];
|
|
195
|
+
if (opt.alias) patterns.push(`${parentPath}:-${opt.alias}`);
|
|
160
196
|
lines.push(` ${patterns.join("|")}) return 0 ;;`);
|
|
161
197
|
}
|
|
162
|
-
for (const child of getVisibleSubs(sub.subcommands)) lines.push(...optTakesValueEntries(child, child.name));
|
|
198
|
+
for (const child of getVisibleSubs(sub.subcommands)) lines.push(...optTakesValueEntries(child, joinPrefix(parentPath, child.name, ":")));
|
|
163
199
|
return lines;
|
|
164
200
|
}
|
|
165
201
|
/**
|
|
202
|
+
* Recursively collect all subcommand route entries.
|
|
203
|
+
* Returns entries used by all shell generators for both dispatch routing
|
|
204
|
+
* and subcommand lookup (is_subcmd) tables.
|
|
205
|
+
*/
|
|
206
|
+
function collectRouteEntries(sub, parentPath = "", parentFunc = "") {
|
|
207
|
+
const entries = [];
|
|
208
|
+
for (const child of getVisibleSubs(sub.subcommands)) {
|
|
209
|
+
const pathStr = joinPrefix(parentPath, child.name, ":");
|
|
210
|
+
const funcSuffix = joinPrefix(parentFunc, sanitize(child.name), "_");
|
|
211
|
+
entries.push(...collectRouteEntries(child, pathStr, funcSuffix));
|
|
212
|
+
entries.push({
|
|
213
|
+
pathStr,
|
|
214
|
+
funcSuffix,
|
|
215
|
+
lookupPattern: `${parentPath}:${child.name}`
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
return entries;
|
|
219
|
+
}
|
|
220
|
+
/**
|
|
221
|
+
* Generate is_subcmd case/switch body lines (bash/zsh case syntax).
|
|
222
|
+
* Returns lines for the case statement body only (caller wraps in function).
|
|
223
|
+
*/
|
|
224
|
+
function isSubcmdCaseLines(routeEntries) {
|
|
225
|
+
return routeEntries.map((r) => ` ${r.lookupPattern}) return 0 ;;`);
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Recursively merge global options into a subcommand and all its descendants.
|
|
229
|
+
* Avoids duplicates by checking existing option names.
|
|
230
|
+
*/
|
|
231
|
+
function propagateGlobalOptions(sub, globalOptions) {
|
|
232
|
+
const existingNames = new Set(sub.options.map((o) => o.name));
|
|
233
|
+
const newOpts = globalOptions.filter((o) => !existingNames.has(o.name));
|
|
234
|
+
sub.options = [...sub.options, ...newOpts];
|
|
235
|
+
for (const child of sub.subcommands) propagateGlobalOptions(child, globalOptions);
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
166
238
|
* Extract completion data from a command tree
|
|
239
|
+
*
|
|
240
|
+
* @param command - The root command
|
|
241
|
+
* @param programName - Program name for completion scripts
|
|
242
|
+
* @param globalArgsSchema - Optional global args schema. When provided, global options
|
|
243
|
+
* are derived from this schema instead of the root command's options.
|
|
167
244
|
*/
|
|
168
|
-
function extractCompletionData(command, programName) {
|
|
245
|
+
function extractCompletionData(command, programName, globalArgsSchema) {
|
|
169
246
|
const rootSubcommand = extractSubcommand(programName, command);
|
|
247
|
+
let globalOptions;
|
|
248
|
+
if (globalArgsSchema) {
|
|
249
|
+
globalOptions = require_lazy.extractFields(globalArgsSchema).fields.filter((field) => !field.positional).map(fieldToOption);
|
|
250
|
+
propagateGlobalOptions(rootSubcommand, globalOptions);
|
|
251
|
+
} else globalOptions = rootSubcommand.options;
|
|
170
252
|
return {
|
|
171
253
|
command: rootSubcommand,
|
|
172
254
|
programName,
|
|
173
|
-
globalOptions
|
|
255
|
+
globalOptions
|
|
174
256
|
};
|
|
175
257
|
}
|
|
176
258
|
|
|
@@ -318,8 +400,9 @@ function generateSubHandler$2(sub, fn, path) {
|
|
|
318
400
|
for (const child of visibleSubs) lines.push(...generateSubHandler$2(child, fn, fullPath));
|
|
319
401
|
lines.push(`${funcName}() {`);
|
|
320
402
|
lines.push(...valueCompletionBlocks(sub.options));
|
|
321
|
-
|
|
322
|
-
lines.push(` if [[ -
|
|
403
|
+
const fullPathStr = fullPath.join(":");
|
|
404
|
+
lines.push(` if [[ -z "$_inline_prefix" ]] && __${fn}_opt_takes_value "${fullPathStr}" "$_prev"; then return; fi`);
|
|
405
|
+
lines.push(` if [[ -n "$_inline_prefix" ]] && __${fn}_opt_takes_value "${fullPathStr}" "\${_inline_prefix%=}"; then return; fi`);
|
|
323
406
|
if (sub.positionals.length > 0) {
|
|
324
407
|
lines.push(` if (( _after_dd )); then`);
|
|
325
408
|
lines.push(...positionalBlock$2(sub.positionals).map((l) => ` ${l}`));
|
|
@@ -344,7 +427,7 @@ function generateSubHandler$2(sub, fn, path) {
|
|
|
344
427
|
}
|
|
345
428
|
function generateBashCompletion(command, options) {
|
|
346
429
|
const { programName } = options;
|
|
347
|
-
const data = extractCompletionData(command, programName);
|
|
430
|
+
const data = extractCompletionData(command, programName, options.globalArgsSchema);
|
|
348
431
|
const fn = sanitize(programName);
|
|
349
432
|
const root = data.command;
|
|
350
433
|
const visibleSubs = getVisibleSubs(root.subcommands);
|
|
@@ -368,6 +451,16 @@ function generateBashCompletion(command, options) {
|
|
|
368
451
|
lines.push(` return 1`);
|
|
369
452
|
lines.push(`}`);
|
|
370
453
|
lines.push(``);
|
|
454
|
+
const routeEntries = collectRouteEntries(root);
|
|
455
|
+
if (routeEntries.length > 0) {
|
|
456
|
+
lines.push(`__${fn}_is_subcmd() {`);
|
|
457
|
+
lines.push(` case "$1:$2" in`);
|
|
458
|
+
lines.push(...isSubcmdCaseLines(routeEntries));
|
|
459
|
+
lines.push(` esac`);
|
|
460
|
+
lines.push(` return 1`);
|
|
461
|
+
lines.push(`}`);
|
|
462
|
+
lines.push(``);
|
|
463
|
+
}
|
|
371
464
|
for (const sub of visibleSubs) lines.push(...generateSubHandler$2(sub, fn, []));
|
|
372
465
|
lines.push(`__${fn}_complete_root() {`);
|
|
373
466
|
lines.push(...valueCompletionBlocks(root.options));
|
|
@@ -396,7 +489,7 @@ function generateBashCompletion(command, options) {
|
|
|
396
489
|
lines.push(` fi`);
|
|
397
490
|
lines.push(`}`);
|
|
398
491
|
lines.push(``);
|
|
399
|
-
const subRouting =
|
|
492
|
+
const subRouting = routeEntries.map((r) => ` ${r.pathStr}) __${fn}_complete_${r.funcSuffix} ;;`).join("\n");
|
|
400
493
|
lines.push(`_${fn}_completions() {`);
|
|
401
494
|
lines.push(` COMPREPLY=()`);
|
|
402
495
|
lines.push(``);
|
|
@@ -440,7 +533,7 @@ function generateBashCompletion(command, options) {
|
|
|
440
533
|
lines.push(` __${fn}_opt_takes_value "$_subcmd" "$_w" && _skip_next=1`);
|
|
441
534
|
lines.push(` (( _j++ )); continue`);
|
|
442
535
|
lines.push(` fi`);
|
|
443
|
-
if (
|
|
536
|
+
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`);
|
|
444
537
|
else lines.push(` (( _pos_count++ ))`);
|
|
445
538
|
lines.push(` (( _j++ ))`);
|
|
446
539
|
lines.push(` done`);
|
|
@@ -1113,7 +1206,8 @@ function generateSubHandler$1(sub, fn, path) {
|
|
|
1113
1206
|
for (const child of visibleSubs) lines.push(...generateSubHandler$1(child, fn, fullPath));
|
|
1114
1207
|
lines.push(`function ${funcName} --no-scope-shadowing`);
|
|
1115
1208
|
lines.push(...valueCompletionBlock$1(sub.options));
|
|
1116
|
-
|
|
1209
|
+
const fullPathStr = fullPath.join(":");
|
|
1210
|
+
lines.push(` if __${fn}_opt_takes_value "${fullPathStr}" "$_prev"; return; end`);
|
|
1117
1211
|
if (sub.positionals.length > 0) {
|
|
1118
1212
|
lines.push(` if test $_after_dd -eq 1`);
|
|
1119
1213
|
lines.push(...positionalBlock$1(sub.positionals).map((l) => ` ${l}`));
|
|
@@ -1134,20 +1228,23 @@ function generateSubHandler$1(sub, fn, path) {
|
|
|
1134
1228
|
return lines;
|
|
1135
1229
|
}
|
|
1136
1230
|
/** Generate opt-takes-value entries for fish switch cases */
|
|
1137
|
-
function optTakesValueCases(sub,
|
|
1231
|
+
function optTakesValueCases(sub, parentPath) {
|
|
1138
1232
|
const lines = [];
|
|
1139
1233
|
for (const opt of sub.options) if (opt.takesValue) {
|
|
1140
|
-
const patterns = [`"${
|
|
1141
|
-
if (opt.alias) patterns.push(`"${
|
|
1234
|
+
const patterns = [`"${parentPath}:--${opt.cliName}"`];
|
|
1235
|
+
if (opt.alias) patterns.push(`"${parentPath}:-${opt.alias}"`);
|
|
1142
1236
|
lines.push(` case ${patterns.join(" ")}`);
|
|
1143
1237
|
lines.push(` return 0`);
|
|
1144
1238
|
}
|
|
1145
|
-
for (const child of getVisibleSubs(sub.subcommands))
|
|
1239
|
+
for (const child of getVisibleSubs(sub.subcommands)) {
|
|
1240
|
+
const childPath = parentPath ? `${parentPath}:${child.name}` : child.name;
|
|
1241
|
+
lines.push(...optTakesValueCases(child, childPath));
|
|
1242
|
+
}
|
|
1146
1243
|
return lines;
|
|
1147
1244
|
}
|
|
1148
1245
|
function generateFishCompletion(command, options) {
|
|
1149
1246
|
const { programName } = options;
|
|
1150
|
-
const data = extractCompletionData(command, programName);
|
|
1247
|
+
const data = extractCompletionData(command, programName, options.globalArgsSchema);
|
|
1151
1248
|
const fn = sanitize(programName);
|
|
1152
1249
|
const root = data.command;
|
|
1153
1250
|
const visibleSubs = getVisibleSubs(root.subcommands);
|
|
@@ -1171,6 +1268,19 @@ function generateFishCompletion(command, options) {
|
|
|
1171
1268
|
lines.push(` return 1`);
|
|
1172
1269
|
lines.push(`end`);
|
|
1173
1270
|
lines.push(``);
|
|
1271
|
+
const routeEntries = collectRouteEntries(root);
|
|
1272
|
+
if (routeEntries.length > 0) {
|
|
1273
|
+
lines.push(`function __${fn}_is_subcmd`);
|
|
1274
|
+
lines.push(` switch "$argv[1]:$argv[2]"`);
|
|
1275
|
+
for (const r of routeEntries) {
|
|
1276
|
+
lines.push(` case "${r.lookupPattern}"`);
|
|
1277
|
+
lines.push(` return 0`);
|
|
1278
|
+
}
|
|
1279
|
+
lines.push(` end`);
|
|
1280
|
+
lines.push(` return 1`);
|
|
1281
|
+
lines.push(`end`);
|
|
1282
|
+
lines.push(``);
|
|
1283
|
+
}
|
|
1174
1284
|
for (const sub of visibleSubs) lines.push(...generateSubHandler$1(sub, fn, []));
|
|
1175
1285
|
lines.push(`function __${fn}_complete_root --no-scope-shadowing`);
|
|
1176
1286
|
lines.push(...valueCompletionBlock$1(root.options));
|
|
@@ -1233,13 +1343,13 @@ function generateFishCompletion(command, options) {
|
|
|
1233
1343
|
lines.push(` __${fn}_opt_takes_value "$_subcmd" "$_w"; and set _skip_next 1`);
|
|
1234
1344
|
lines.push(` set _j (math $_j + 1); continue`);
|
|
1235
1345
|
lines.push(` end`);
|
|
1236
|
-
if (
|
|
1346
|
+
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`);
|
|
1237
1347
|
else lines.push(` set _pos_count (math $_pos_count + 1)`);
|
|
1238
1348
|
lines.push(` set _j (math $_j + 1)`);
|
|
1239
1349
|
lines.push(` end`);
|
|
1240
1350
|
lines.push(``);
|
|
1241
1351
|
lines.push(` switch "$_subcmd"`);
|
|
1242
|
-
for (const
|
|
1352
|
+
for (const r of routeEntries) lines.push(` case "${r.pathStr}"; __${fn}_complete_${r.funcSuffix}`);
|
|
1243
1353
|
lines.push(` case '*'; __${fn}_complete_root`);
|
|
1244
1354
|
lines.push(` end`);
|
|
1245
1355
|
lines.push(`end`);
|
|
@@ -1358,7 +1468,8 @@ function generateSubHandler(sub, fn, path) {
|
|
|
1358
1468
|
lines.push(`${funcName}() {`);
|
|
1359
1469
|
lines.push(` local -a _vals=()`);
|
|
1360
1470
|
lines.push(...valueCompletionBlock(sub.options, fn));
|
|
1361
|
-
|
|
1471
|
+
const fullPathStr = fullPath.join(":");
|
|
1472
|
+
lines.push(` if __${fn}_opt_takes_value "${fullPathStr}" "\${words[CURRENT-1]}"; then return 0; fi`);
|
|
1362
1473
|
if (sub.positionals.length > 0) {
|
|
1363
1474
|
lines.push(` if (( _after_dd )); then`);
|
|
1364
1475
|
lines.push(...positionalBlock(sub.positionals, fn).map((l) => ` ${l}`));
|
|
@@ -1385,7 +1496,7 @@ function generateSubHandler(sub, fn, path) {
|
|
|
1385
1496
|
}
|
|
1386
1497
|
function generateZshCompletion(command, options) {
|
|
1387
1498
|
const { programName } = options;
|
|
1388
|
-
const data = extractCompletionData(command, programName);
|
|
1499
|
+
const data = extractCompletionData(command, programName, options.globalArgsSchema);
|
|
1389
1500
|
const fn = sanitize(programName);
|
|
1390
1501
|
const root = data.command;
|
|
1391
1502
|
const visibleSubs = getVisibleSubs(root.subcommands);
|
|
@@ -1420,6 +1531,16 @@ function generateZshCompletion(command, options) {
|
|
|
1420
1531
|
lines.push(` return 1`);
|
|
1421
1532
|
lines.push(`}`);
|
|
1422
1533
|
lines.push(``);
|
|
1534
|
+
const routeEntries = collectRouteEntries(root);
|
|
1535
|
+
if (routeEntries.length > 0) {
|
|
1536
|
+
lines.push(`__${fn}_is_subcmd() {`);
|
|
1537
|
+
lines.push(` case "$1:$2" in`);
|
|
1538
|
+
lines.push(...isSubcmdCaseLines(routeEntries));
|
|
1539
|
+
lines.push(` esac`);
|
|
1540
|
+
lines.push(` return 1`);
|
|
1541
|
+
lines.push(`}`);
|
|
1542
|
+
lines.push(``);
|
|
1543
|
+
}
|
|
1423
1544
|
for (const sub of visibleSubs) lines.push(...generateSubHandler(sub, fn, []));
|
|
1424
1545
|
lines.push(`__${fn}_complete_root() {`);
|
|
1425
1546
|
lines.push(` local -a _vals=()`);
|
|
@@ -1450,7 +1571,7 @@ function generateZshCompletion(command, options) {
|
|
|
1450
1571
|
lines.push(` fi`);
|
|
1451
1572
|
lines.push(`}`);
|
|
1452
1573
|
lines.push(``);
|
|
1453
|
-
const subRouting =
|
|
1574
|
+
const subRouting = routeEntries.map((r) => ` ${r.pathStr}) __${fn}_complete_${r.funcSuffix} ;;`).join("\n");
|
|
1454
1575
|
lines.push(`_${fn}() {`);
|
|
1455
1576
|
lines.push(` (( CURRENT )) || CURRENT=\${#words}`);
|
|
1456
1577
|
lines.push(``);
|
|
@@ -1469,7 +1590,7 @@ function generateZshCompletion(command, options) {
|
|
|
1469
1590
|
lines.push(` __${fn}_opt_takes_value "$_subcmd" "$_w" && _skip_next=1`);
|
|
1470
1591
|
lines.push(` (( _j++ )); continue`);
|
|
1471
1592
|
lines.push(` fi`);
|
|
1472
|
-
if (
|
|
1593
|
+
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`);
|
|
1473
1594
|
else lines.push(` (( _pos_count++ ))`);
|
|
1474
1595
|
lines.push(` (( _j++ ))`);
|
|
1475
1596
|
lines.push(` done`);
|
|
@@ -1504,6 +1625,156 @@ source ~/.zshrc`
|
|
|
1504
1625
|
};
|
|
1505
1626
|
}
|
|
1506
1627
|
|
|
1628
|
+
//#endregion
|
|
1629
|
+
//#region src/completion/index.ts
|
|
1630
|
+
/**
|
|
1631
|
+
* Shell completion generation module
|
|
1632
|
+
*
|
|
1633
|
+
* Provides utilities to generate shell completion scripts for bash, zsh, and fish.
|
|
1634
|
+
*
|
|
1635
|
+
* @example
|
|
1636
|
+
* ```typescript
|
|
1637
|
+
* import { generateCompletion, createCompletionCommand } from "politty/completion";
|
|
1638
|
+
*
|
|
1639
|
+
* // Generate completion script directly
|
|
1640
|
+
* const result = generateCompletion(myCommand, {
|
|
1641
|
+
* shell: "bash",
|
|
1642
|
+
* programName: "mycli"
|
|
1643
|
+
* });
|
|
1644
|
+
* console.log(result.script);
|
|
1645
|
+
*
|
|
1646
|
+
* // Or add a completion subcommand to your CLI
|
|
1647
|
+
* const mainCommand = withCompletionCommand(
|
|
1648
|
+
* defineCommand({
|
|
1649
|
+
* name: "mycli",
|
|
1650
|
+
* subCommands: { ... },
|
|
1651
|
+
* }),
|
|
1652
|
+
* );
|
|
1653
|
+
* ```
|
|
1654
|
+
*/
|
|
1655
|
+
/**
|
|
1656
|
+
* Generate completion script for the specified shell
|
|
1657
|
+
*/
|
|
1658
|
+
function generateCompletion(command, options) {
|
|
1659
|
+
switch (options.shell) {
|
|
1660
|
+
case "bash": return generateBashCompletion(command, options);
|
|
1661
|
+
case "zsh": return generateZshCompletion(command, options);
|
|
1662
|
+
case "fish": return generateFishCompletion(command, options);
|
|
1663
|
+
default: throw new Error(`Unsupported shell: ${options.shell}`);
|
|
1664
|
+
}
|
|
1665
|
+
}
|
|
1666
|
+
/**
|
|
1667
|
+
* Get the list of supported shells
|
|
1668
|
+
*/
|
|
1669
|
+
function getSupportedShells() {
|
|
1670
|
+
return [
|
|
1671
|
+
"bash",
|
|
1672
|
+
"zsh",
|
|
1673
|
+
"fish"
|
|
1674
|
+
];
|
|
1675
|
+
}
|
|
1676
|
+
/**
|
|
1677
|
+
* Detect the current shell from environment
|
|
1678
|
+
*/
|
|
1679
|
+
function detectShell() {
|
|
1680
|
+
const shellName = (process.env.SHELL || "").split("/").pop()?.toLowerCase() || "";
|
|
1681
|
+
if (shellName.includes("bash")) return "bash";
|
|
1682
|
+
if (shellName.includes("zsh")) return "zsh";
|
|
1683
|
+
if (shellName.includes("fish")) return "fish";
|
|
1684
|
+
return null;
|
|
1685
|
+
}
|
|
1686
|
+
/**
|
|
1687
|
+
* Schema for the completion command arguments
|
|
1688
|
+
*/
|
|
1689
|
+
const completionArgsSchema = zod.z.object({
|
|
1690
|
+
shell: require_lazy.arg(zod.z.enum([
|
|
1691
|
+
"bash",
|
|
1692
|
+
"zsh",
|
|
1693
|
+
"fish"
|
|
1694
|
+
]).optional().describe("Shell type (auto-detected if not specified)"), {
|
|
1695
|
+
positional: true,
|
|
1696
|
+
description: "Shell type (bash, zsh, or fish)",
|
|
1697
|
+
placeholder: "SHELL"
|
|
1698
|
+
}),
|
|
1699
|
+
instructions: require_lazy.arg(zod.z.boolean().default(false), {
|
|
1700
|
+
alias: "i",
|
|
1701
|
+
description: "Show installation instructions"
|
|
1702
|
+
})
|
|
1703
|
+
});
|
|
1704
|
+
/**
|
|
1705
|
+
* Create a completion subcommand for your CLI
|
|
1706
|
+
*
|
|
1707
|
+
* This creates a ready-to-use subcommand that generates completion scripts.
|
|
1708
|
+
*
|
|
1709
|
+
* @example
|
|
1710
|
+
* ```typescript
|
|
1711
|
+
* const mainCommand = defineCommand({
|
|
1712
|
+
* name: "mycli",
|
|
1713
|
+
* subCommands: {
|
|
1714
|
+
* completion: createCompletionCommand(mainCommand)
|
|
1715
|
+
* }
|
|
1716
|
+
* });
|
|
1717
|
+
* ```
|
|
1718
|
+
*/
|
|
1719
|
+
function createCompletionCommand(rootCommand, programName, globalArgsSchema) {
|
|
1720
|
+
const resolvedProgramName = programName ?? rootCommand.name;
|
|
1721
|
+
if (!rootCommand.subCommands?.__complete) rootCommand.subCommands = {
|
|
1722
|
+
...rootCommand.subCommands,
|
|
1723
|
+
__complete: createDynamicCompleteCommand(rootCommand, resolvedProgramName)
|
|
1724
|
+
};
|
|
1725
|
+
return defineCommand({
|
|
1726
|
+
name: "completion",
|
|
1727
|
+
description: "Generate shell completion script",
|
|
1728
|
+
args: completionArgsSchema,
|
|
1729
|
+
run(args) {
|
|
1730
|
+
const shellType = args.shell || detectShell();
|
|
1731
|
+
if (!shellType) {
|
|
1732
|
+
console.error("Could not detect shell type. Please specify one of: bash, zsh, fish");
|
|
1733
|
+
process.exitCode = 1;
|
|
1734
|
+
return;
|
|
1735
|
+
}
|
|
1736
|
+
const result = generateCompletion(rootCommand, {
|
|
1737
|
+
shell: shellType,
|
|
1738
|
+
programName: resolvedProgramName,
|
|
1739
|
+
includeDescriptions: true,
|
|
1740
|
+
...globalArgsSchema !== void 0 && { globalArgsSchema }
|
|
1741
|
+
});
|
|
1742
|
+
if (args.instructions) console.log(result.installInstructions);
|
|
1743
|
+
else console.log(result.script);
|
|
1744
|
+
}
|
|
1745
|
+
});
|
|
1746
|
+
}
|
|
1747
|
+
/**
|
|
1748
|
+
* Wrap a command with a completion subcommand
|
|
1749
|
+
*
|
|
1750
|
+
* This avoids circular references that occur when a command references itself
|
|
1751
|
+
* in its subCommands (e.g., for completion generation).
|
|
1752
|
+
*
|
|
1753
|
+
* @param command - The command to wrap
|
|
1754
|
+
* @param options - Options including programName
|
|
1755
|
+
* @returns A new command with the completion subcommand added
|
|
1756
|
+
*
|
|
1757
|
+
* @example
|
|
1758
|
+
* ```typescript
|
|
1759
|
+
* const mainCommand = withCompletionCommand(
|
|
1760
|
+
* defineCommand({
|
|
1761
|
+
* name: "mycli",
|
|
1762
|
+
* subCommands: { ... },
|
|
1763
|
+
* }),
|
|
1764
|
+
* );
|
|
1765
|
+
* ```
|
|
1766
|
+
*/
|
|
1767
|
+
function withCompletionCommand(command, options) {
|
|
1768
|
+
const { programName, globalArgsSchema } = typeof options === "string" ? { programName: options } : options ?? {};
|
|
1769
|
+
const wrappedCommand = { ...command };
|
|
1770
|
+
wrappedCommand.subCommands = {
|
|
1771
|
+
...command.subCommands,
|
|
1772
|
+
completion: createCompletionCommand(wrappedCommand, programName, globalArgsSchema),
|
|
1773
|
+
__complete: createDynamicCompleteCommand(wrappedCommand, programName)
|
|
1774
|
+
};
|
|
1775
|
+
return wrappedCommand;
|
|
1776
|
+
}
|
|
1777
|
+
|
|
1507
1778
|
//#endregion
|
|
1508
1779
|
Object.defineProperty(exports, 'CompletionDirective', {
|
|
1509
1780
|
enumerable: true,
|
|
@@ -1511,6 +1782,18 @@ Object.defineProperty(exports, 'CompletionDirective', {
|
|
|
1511
1782
|
return CompletionDirective;
|
|
1512
1783
|
}
|
|
1513
1784
|
});
|
|
1785
|
+
Object.defineProperty(exports, 'createCompletionCommand', {
|
|
1786
|
+
enumerable: true,
|
|
1787
|
+
get: function () {
|
|
1788
|
+
return createCompletionCommand;
|
|
1789
|
+
}
|
|
1790
|
+
});
|
|
1791
|
+
Object.defineProperty(exports, 'createDefineCommand', {
|
|
1792
|
+
enumerable: true,
|
|
1793
|
+
get: function () {
|
|
1794
|
+
return createDefineCommand;
|
|
1795
|
+
}
|
|
1796
|
+
});
|
|
1514
1797
|
Object.defineProperty(exports, 'createDynamicCompleteCommand', {
|
|
1515
1798
|
enumerable: true,
|
|
1516
1799
|
get: function () {
|
|
@@ -1523,6 +1806,12 @@ Object.defineProperty(exports, 'defineCommand', {
|
|
|
1523
1806
|
return defineCommand;
|
|
1524
1807
|
}
|
|
1525
1808
|
});
|
|
1809
|
+
Object.defineProperty(exports, 'detectShell', {
|
|
1810
|
+
enumerable: true,
|
|
1811
|
+
get: function () {
|
|
1812
|
+
return detectShell;
|
|
1813
|
+
}
|
|
1814
|
+
});
|
|
1526
1815
|
Object.defineProperty(exports, 'extractCompletionData', {
|
|
1527
1816
|
enumerable: true,
|
|
1528
1817
|
get: function () {
|
|
@@ -1541,28 +1830,22 @@ Object.defineProperty(exports, 'formatForShell', {
|
|
|
1541
1830
|
return formatForShell;
|
|
1542
1831
|
}
|
|
1543
1832
|
});
|
|
1544
|
-
Object.defineProperty(exports, 'generateBashCompletion', {
|
|
1545
|
-
enumerable: true,
|
|
1546
|
-
get: function () {
|
|
1547
|
-
return generateBashCompletion;
|
|
1548
|
-
}
|
|
1549
|
-
});
|
|
1550
1833
|
Object.defineProperty(exports, 'generateCandidates', {
|
|
1551
1834
|
enumerable: true,
|
|
1552
1835
|
get: function () {
|
|
1553
1836
|
return generateCandidates;
|
|
1554
1837
|
}
|
|
1555
1838
|
});
|
|
1556
|
-
Object.defineProperty(exports, '
|
|
1839
|
+
Object.defineProperty(exports, 'generateCompletion', {
|
|
1557
1840
|
enumerable: true,
|
|
1558
1841
|
get: function () {
|
|
1559
|
-
return
|
|
1842
|
+
return generateCompletion;
|
|
1560
1843
|
}
|
|
1561
1844
|
});
|
|
1562
|
-
Object.defineProperty(exports, '
|
|
1845
|
+
Object.defineProperty(exports, 'getSupportedShells', {
|
|
1563
1846
|
enumerable: true,
|
|
1564
1847
|
get: function () {
|
|
1565
|
-
return
|
|
1848
|
+
return getSupportedShells;
|
|
1566
1849
|
}
|
|
1567
1850
|
});
|
|
1568
1851
|
Object.defineProperty(exports, 'hasCompleteCommand', {
|
|
@@ -1583,4 +1866,10 @@ Object.defineProperty(exports, 'resolveValueCompletion', {
|
|
|
1583
1866
|
return resolveValueCompletion;
|
|
1584
1867
|
}
|
|
1585
1868
|
});
|
|
1586
|
-
|
|
1869
|
+
Object.defineProperty(exports, 'withCompletionCommand', {
|
|
1870
|
+
enumerable: true,
|
|
1871
|
+
get: function () {
|
|
1872
|
+
return withCompletionCommand;
|
|
1873
|
+
}
|
|
1874
|
+
});
|
|
1875
|
+
//# sourceMappingURL=completion-Df0eZ70u.cjs.map
|