@guanghechen/commander 4.7.2 → 4.7.4
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/CHANGELOG.md +16 -0
- package/lib/cjs/browser.cjs +136 -19
- package/lib/cjs/index.cjs +176 -1028
- package/lib/cjs/node.cjs +205 -51
- package/lib/esm/browser.mjs +136 -19
- package/lib/esm/index.mjs +175 -1021
- package/lib/esm/node.mjs +205 -51
- package/lib/types/browser.d.ts +6 -3
- package/lib/types/index.d.ts +15 -191
- package/lib/types/node.d.ts +6 -3
- package/package.json +3 -3
package/lib/cjs/node.cjs
CHANGED
|
@@ -307,6 +307,30 @@ function parsePrimitiveNumber(rawValue) {
|
|
|
307
307
|
}
|
|
308
308
|
return value;
|
|
309
309
|
}
|
|
310
|
+
function normalizeSubcommandNameForDistance(name) {
|
|
311
|
+
return camelToKebabCase$1(name).toLowerCase();
|
|
312
|
+
}
|
|
313
|
+
function levenshteinDistance(left, right) {
|
|
314
|
+
if (left === right) {
|
|
315
|
+
return 0;
|
|
316
|
+
}
|
|
317
|
+
if (left.length === 0) {
|
|
318
|
+
return right.length;
|
|
319
|
+
}
|
|
320
|
+
if (right.length === 0) {
|
|
321
|
+
return left.length;
|
|
322
|
+
}
|
|
323
|
+
let prev = Array.from({ length: right.length + 1 }, (_, i) => i);
|
|
324
|
+
for (let i = 0; i < left.length; i += 1) {
|
|
325
|
+
const current = [i + 1];
|
|
326
|
+
for (let j = 0; j < right.length; j += 1) {
|
|
327
|
+
const substitutionCost = left[i] === right[j] ? 0 : 1;
|
|
328
|
+
current[j + 1] = Math.min(current[j] + 1, prev[j + 1] + 1, prev[j] + substitutionCost);
|
|
329
|
+
}
|
|
330
|
+
prev = current;
|
|
331
|
+
}
|
|
332
|
+
return prev[right.length];
|
|
333
|
+
}
|
|
310
334
|
function tokenizeLongOption(arg, commandPath) {
|
|
311
335
|
const eqIdx = arg.indexOf('=');
|
|
312
336
|
const namePart = eqIdx !== -1 ? arg.slice(0, eqIdx) : arg;
|
|
@@ -709,7 +733,10 @@ class Command {
|
|
|
709
733
|
const kebabLong = camelToKebabCase$1(opt.long);
|
|
710
734
|
let sig = opt.short ? `-${opt.short}, ` : ' ';
|
|
711
735
|
sig += `--${kebabLong}`;
|
|
712
|
-
if (opt.args
|
|
736
|
+
if (opt.args === 'optional') {
|
|
737
|
+
sig += ' [value]';
|
|
738
|
+
}
|
|
739
|
+
else if (opt.args !== 'none') {
|
|
713
740
|
sig += ' <value>';
|
|
714
741
|
}
|
|
715
742
|
let desc = opt.desc;
|
|
@@ -850,7 +877,15 @@ class Command {
|
|
|
850
877
|
return ` ${outputLabel} ${desc}`;
|
|
851
878
|
}
|
|
852
879
|
getCompletionMeta() {
|
|
853
|
-
const
|
|
880
|
+
const optionMap = new Map();
|
|
881
|
+
for (const option of this.#resolveOptionPolicy().mergedOptions) {
|
|
882
|
+
optionMap.set(option.long, option);
|
|
883
|
+
}
|
|
884
|
+
optionMap.set('help', BUILTIN_HELP_OPTION);
|
|
885
|
+
if (this.#supportsBuiltinVersion()) {
|
|
886
|
+
optionMap.set('version', BUILTIN_VERSION_OPTION);
|
|
887
|
+
}
|
|
888
|
+
const allOptions = Array.from(optionMap.values());
|
|
854
889
|
const options = [];
|
|
855
890
|
const argumentsMeta = [];
|
|
856
891
|
for (const opt of allOptions) {
|
|
@@ -858,7 +893,8 @@ class Command {
|
|
|
858
893
|
long: opt.long,
|
|
859
894
|
short: opt.short,
|
|
860
895
|
desc: opt.desc,
|
|
861
|
-
|
|
896
|
+
type: opt.type,
|
|
897
|
+
args: opt.args,
|
|
862
898
|
choices: opt.choices?.map(choice => String(choice)),
|
|
863
899
|
});
|
|
864
900
|
}
|
|
@@ -1328,6 +1364,14 @@ class Command {
|
|
|
1328
1364
|
consumed.push(tokens[i]);
|
|
1329
1365
|
}
|
|
1330
1366
|
}
|
|
1367
|
+
else if (opt.args === 'optional') {
|
|
1368
|
+
if (!token.resolved.includes('=') &&
|
|
1369
|
+
i + 1 < tokens.length &&
|
|
1370
|
+
tokens[i + 1].type === 'none') {
|
|
1371
|
+
i += 1;
|
|
1372
|
+
consumed.push(tokens[i]);
|
|
1373
|
+
}
|
|
1374
|
+
}
|
|
1331
1375
|
else if (opt.args === 'variadic') {
|
|
1332
1376
|
if (!token.resolved.includes('=')) {
|
|
1333
1377
|
while (i + 1 < tokens.length && tokens[i + 1].type === 'none') {
|
|
@@ -1353,6 +1397,12 @@ class Command {
|
|
|
1353
1397
|
consumed.push(tokens[i]);
|
|
1354
1398
|
}
|
|
1355
1399
|
}
|
|
1400
|
+
else if (opt.args === 'optional') {
|
|
1401
|
+
if (i + 1 < tokens.length && tokens[i + 1].type === 'none') {
|
|
1402
|
+
i += 1;
|
|
1403
|
+
consumed.push(tokens[i]);
|
|
1404
|
+
}
|
|
1405
|
+
}
|
|
1356
1406
|
else if (opt.args === 'variadic') {
|
|
1357
1407
|
while (i + 1 < tokens.length && tokens[i + 1].type === 'none') {
|
|
1358
1408
|
i += 1;
|
|
@@ -1394,6 +1444,7 @@ class Command {
|
|
|
1394
1444
|
leafLocalOpts[opt.long] = leafParsedOpts[opt.long];
|
|
1395
1445
|
}
|
|
1396
1446
|
}
|
|
1447
|
+
leafCommand.#assertUnknownSubcommand(ctx.sources.user.argv);
|
|
1397
1448
|
const rawArgStrings = [...argTokens.map(t => t.original), ...restArgs];
|
|
1398
1449
|
const { args, rawArgs } = leafCommand.#parseArguments(rawArgStrings);
|
|
1399
1450
|
const parseCtx = {
|
|
@@ -1476,6 +1527,23 @@ class Command {
|
|
|
1476
1527
|
i += 1;
|
|
1477
1528
|
continue;
|
|
1478
1529
|
}
|
|
1530
|
+
if (opt.args === 'optional') {
|
|
1531
|
+
const eqIdx = token.resolved.indexOf('=');
|
|
1532
|
+
if (eqIdx !== -1) {
|
|
1533
|
+
opts[opt.long] = this.#convertValue(opt, token.resolved.slice(eqIdx + 1));
|
|
1534
|
+
i += 1;
|
|
1535
|
+
continue;
|
|
1536
|
+
}
|
|
1537
|
+
if (i + 1 < tokens.length && tokens[i + 1].type === 'none') {
|
|
1538
|
+
opts[opt.long] = this.#convertValue(opt, tokens[i + 1].original);
|
|
1539
|
+
i += 1;
|
|
1540
|
+
}
|
|
1541
|
+
else {
|
|
1542
|
+
opts[opt.long] = undefined;
|
|
1543
|
+
}
|
|
1544
|
+
i += 1;
|
|
1545
|
+
continue;
|
|
1546
|
+
}
|
|
1479
1547
|
if (opt.args === 'variadic') {
|
|
1480
1548
|
const values = Array.isArray(opts[opt.long]) ? opts[opt.long] : [];
|
|
1481
1549
|
const eqIdx = token.resolved.indexOf('=');
|
|
@@ -1495,7 +1563,7 @@ class Command {
|
|
|
1495
1563
|
i += 1;
|
|
1496
1564
|
}
|
|
1497
1565
|
for (const opt of allOptions) {
|
|
1498
|
-
if (opt.required && opts
|
|
1566
|
+
if (opt.required && !Object.prototype.hasOwnProperty.call(opts, opt.long)) {
|
|
1499
1567
|
throw new CommanderError('MissingRequired', `missing required option "--${camelToKebabCase$1(opt.long)}"`, this.#getCommandPath());
|
|
1500
1568
|
}
|
|
1501
1569
|
}
|
|
@@ -1532,6 +1600,9 @@ class Command {
|
|
|
1532
1600
|
#parseArguments(rawArgs) {
|
|
1533
1601
|
const argumentDefs = this.#arguments;
|
|
1534
1602
|
const args = {};
|
|
1603
|
+
if (argumentDefs.length === 0 && rawArgs.length > 0) {
|
|
1604
|
+
throw new CommanderError('UnexpectedArgument', `unexpected argument "${rawArgs[0]}"`, this.#getCommandPath());
|
|
1605
|
+
}
|
|
1535
1606
|
const missing = [];
|
|
1536
1607
|
let remaining = rawArgs.length;
|
|
1537
1608
|
for (const def of argumentDefs) {
|
|
@@ -1572,25 +1643,23 @@ class Command {
|
|
|
1572
1643
|
}
|
|
1573
1644
|
if (def.kind === 'some') {
|
|
1574
1645
|
const rest = rawArgs.slice(index);
|
|
1575
|
-
if (rest.length === 0) {
|
|
1576
|
-
throw new CommanderError('MissingRequiredArgument', `missing required argument(s): ${def.name}`, this.#getCommandPath());
|
|
1577
|
-
}
|
|
1578
1646
|
args[def.name] = rest.map(raw => this.#convertArgument(def, raw));
|
|
1579
1647
|
index = rawArgs.length;
|
|
1580
1648
|
break;
|
|
1581
1649
|
}
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
if (
|
|
1650
|
+
if (def.kind === 'optional') {
|
|
1651
|
+
const raw = rawArgs[index];
|
|
1652
|
+
if (raw === undefined) {
|
|
1585
1653
|
args[def.name] = def.default ?? undefined;
|
|
1586
1654
|
continue;
|
|
1587
1655
|
}
|
|
1588
|
-
throw new CommanderError('MissingRequiredArgument', `missing required argument(s): ${def.name}`, this.#getCommandPath());
|
|
1589
|
-
}
|
|
1590
|
-
else {
|
|
1591
1656
|
args[def.name] = this.#convertArgument(def, raw);
|
|
1592
1657
|
index += 1;
|
|
1658
|
+
continue;
|
|
1593
1659
|
}
|
|
1660
|
+
const raw = rawArgs[index];
|
|
1661
|
+
args[def.name] = this.#convertArgument(def, raw);
|
|
1662
|
+
index += 1;
|
|
1594
1663
|
}
|
|
1595
1664
|
const hasRestArgument = argumentDefs.some(a => a.kind === 'variadic' || a.kind === 'some');
|
|
1596
1665
|
if (!hasRestArgument && index < rawArgs.length) {
|
|
@@ -1624,6 +1693,50 @@ class Command {
|
|
|
1624
1693
|
}
|
|
1625
1694
|
return value;
|
|
1626
1695
|
}
|
|
1696
|
+
#assertUnknownSubcommand(userTailArgv) {
|
|
1697
|
+
if (this.#subcommandsList.length === 0) {
|
|
1698
|
+
return;
|
|
1699
|
+
}
|
|
1700
|
+
const token = userTailArgv[0];
|
|
1701
|
+
if (token === undefined || token.startsWith('-') || token === 'help') {
|
|
1702
|
+
return;
|
|
1703
|
+
}
|
|
1704
|
+
if (this.#findSubcommandEntry(token) !== undefined) {
|
|
1705
|
+
return;
|
|
1706
|
+
}
|
|
1707
|
+
const hints = [];
|
|
1708
|
+
if (this.#arguments.length === 0) {
|
|
1709
|
+
hints.push(`Hint: command "${this.#getCommandPath()}" does not accept positional arguments.`);
|
|
1710
|
+
}
|
|
1711
|
+
const candidate = this.#resolveDidYouMeanSubcommandName(token);
|
|
1712
|
+
if (candidate !== undefined) {
|
|
1713
|
+
hints.push(`Hint: did you mean "${candidate}"?`);
|
|
1714
|
+
}
|
|
1715
|
+
const details = hints.length > 0 ? `\n${hints.join('\n')}` : '';
|
|
1716
|
+
throw new CommanderError('UnknownSubcommand', `unknown subcommand "${token}" for command "${this.#getCommandPath()}"${details}`, this.#getCommandPath());
|
|
1717
|
+
}
|
|
1718
|
+
#resolveDidYouMeanSubcommandName(token) {
|
|
1719
|
+
const source = normalizeSubcommandNameForDistance(token);
|
|
1720
|
+
let minDistance = Number.POSITIVE_INFINITY;
|
|
1721
|
+
let bestName;
|
|
1722
|
+
let isUniqueBest = false;
|
|
1723
|
+
for (const entry of this.#subcommandsList) {
|
|
1724
|
+
const target = normalizeSubcommandNameForDistance(entry.name);
|
|
1725
|
+
const distance = levenshteinDistance(source, target);
|
|
1726
|
+
if (distance < minDistance) {
|
|
1727
|
+
minDistance = distance;
|
|
1728
|
+
bestName = entry.name;
|
|
1729
|
+
isUniqueBest = true;
|
|
1730
|
+
}
|
|
1731
|
+
else if (distance === minDistance) {
|
|
1732
|
+
isUniqueBest = false;
|
|
1733
|
+
}
|
|
1734
|
+
}
|
|
1735
|
+
if (minDistance <= 2 && isUniqueBest) {
|
|
1736
|
+
return bestName;
|
|
1737
|
+
}
|
|
1738
|
+
return undefined;
|
|
1739
|
+
}
|
|
1627
1740
|
#hasUserOption(long) {
|
|
1628
1741
|
return this.#options.some(option => option.long === long);
|
|
1629
1742
|
}
|
|
@@ -1667,11 +1780,9 @@ class Command {
|
|
|
1667
1780
|
return optionPolicyMap;
|
|
1668
1781
|
}
|
|
1669
1782
|
#mustGetOptionPolicy(optionPolicyMap, cmd) {
|
|
1670
|
-
const policy = optionPolicyMap.get(cmd);
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
}
|
|
1674
|
-
throw new CommanderError('ConfigurationError', `missing option policy for command "${cmd.#getCommandPath()}"`, this.#getCommandPath());
|
|
1783
|
+
const policy = optionPolicyMap.get(cmd) ?? cmd.#resolveOptionPolicy();
|
|
1784
|
+
optionPolicyMap.set(cmd, policy);
|
|
1785
|
+
return policy;
|
|
1675
1786
|
}
|
|
1676
1787
|
#validateMergedShortOptions(chain, optionPolicyMap) {
|
|
1677
1788
|
const mergedByLong = new Map();
|
|
@@ -1700,7 +1811,10 @@ class Command {
|
|
|
1700
1811
|
throw new CommanderError('ConfigurationError', `boolean option "--${opt.long}" must have args: 'none'`, this.#getCommandPath());
|
|
1701
1812
|
}
|
|
1702
1813
|
if ((opt.type === 'string' || opt.type === 'number') && opt.args === 'none') {
|
|
1703
|
-
throw new CommanderError('ConfigurationError', `${opt.type} option "--${opt.long}" must have args: 'required' or 'variadic'`, this.#getCommandPath());
|
|
1814
|
+
throw new CommanderError('ConfigurationError', `${opt.type} option "--${opt.long}" must have args: 'required', 'optional', or 'variadic'`, this.#getCommandPath());
|
|
1815
|
+
}
|
|
1816
|
+
if (opt.type === 'number' && opt.args === 'optional') {
|
|
1817
|
+
throw new CommanderError('ConfigurationError', `number option "--${opt.long}" does not support args: 'optional'`, this.#getCommandPath());
|
|
1704
1818
|
}
|
|
1705
1819
|
if (opt.long.startsWith('no')) {
|
|
1706
1820
|
throw new CommanderError('ConfigurationError', `option long name cannot start with "no": "${opt.long}"`, this.#getCommandPath());
|
|
@@ -1717,6 +1831,9 @@ class Command {
|
|
|
1717
1831
|
if (opt.type === 'boolean' && opt.required) {
|
|
1718
1832
|
throw new CommanderError('ConfigurationError', `boolean option "--${opt.long}" cannot be required`, this.#getCommandPath());
|
|
1719
1833
|
}
|
|
1834
|
+
if (opt.required && opt.args !== 'required') {
|
|
1835
|
+
throw new CommanderError('ConfigurationError', `required option "--${opt.long}" must use args: 'required'`, this.#getCommandPath());
|
|
1836
|
+
}
|
|
1720
1837
|
}
|
|
1721
1838
|
#checkOptionUniqueness(opt) {
|
|
1722
1839
|
if (this.#options.some(o => o.long === opt.long)) {
|
|
@@ -2025,6 +2142,41 @@ class Coerce {
|
|
|
2025
2142
|
function camelToKebabCase(str) {
|
|
2026
2143
|
return str.replace(/[A-Z]/g, m => '-' + m.toLowerCase());
|
|
2027
2144
|
}
|
|
2145
|
+
function canGenerateNegativeCompletion(opt) {
|
|
2146
|
+
return (opt.type === 'boolean' && opt.args === 'none' && opt.long !== 'help' && opt.long !== 'version');
|
|
2147
|
+
}
|
|
2148
|
+
function optionTakesValue(opt) {
|
|
2149
|
+
return opt.args !== 'none';
|
|
2150
|
+
}
|
|
2151
|
+
const COMPLETION_SHELL_STATE = Symbol('completion-shell-state');
|
|
2152
|
+
function getCommandPath(ctx) {
|
|
2153
|
+
const names = ctx.chain
|
|
2154
|
+
.map(command => command.name)
|
|
2155
|
+
.filter((name) => Boolean(name));
|
|
2156
|
+
if (names.length > 0) {
|
|
2157
|
+
return names.join(' ');
|
|
2158
|
+
}
|
|
2159
|
+
return ctx.cmd.name ?? 'command';
|
|
2160
|
+
}
|
|
2161
|
+
function getCompletionShellState(ctx) {
|
|
2162
|
+
const host = ctx;
|
|
2163
|
+
host[COMPLETION_SHELL_STATE] ??= {};
|
|
2164
|
+
return host[COMPLETION_SHELL_STATE];
|
|
2165
|
+
}
|
|
2166
|
+
function registerCompletionShell(ctx, shell) {
|
|
2167
|
+
const state = getCompletionShellState(ctx);
|
|
2168
|
+
if (state.shell !== undefined && state.shell !== shell) {
|
|
2169
|
+
throw new CommanderError('OptionConflict', 'options "--bash", "--fish", and "--pwsh" are mutually exclusive', getCommandPath(ctx));
|
|
2170
|
+
}
|
|
2171
|
+
state.shell = shell;
|
|
2172
|
+
}
|
|
2173
|
+
function mustGetCompletionShell(ctx) {
|
|
2174
|
+
const state = getCompletionShellState(ctx);
|
|
2175
|
+
if (state.shell === undefined) {
|
|
2176
|
+
throw new CommanderError('MissingRequired', 'missing required option: one of "--bash", "--fish", or "--pwsh"', getCommandPath(ctx));
|
|
2177
|
+
}
|
|
2178
|
+
return state.shell;
|
|
2179
|
+
}
|
|
2028
2180
|
class CompletionCommand extends Command {
|
|
2029
2181
|
constructor(root, config = {}) {
|
|
2030
2182
|
const programName = config.programName ?? root.name ?? 'program';
|
|
@@ -2038,45 +2190,45 @@ class CompletionCommand extends Command {
|
|
|
2038
2190
|
type: 'boolean',
|
|
2039
2191
|
args: 'none',
|
|
2040
2192
|
desc: 'Generate Bash completion script',
|
|
2193
|
+
apply: (value, ctx) => {
|
|
2194
|
+
if (value === true) {
|
|
2195
|
+
registerCompletionShell(ctx, 'bash');
|
|
2196
|
+
}
|
|
2197
|
+
},
|
|
2041
2198
|
})
|
|
2042
2199
|
.option({
|
|
2043
2200
|
long: 'fish',
|
|
2044
2201
|
type: 'boolean',
|
|
2045
2202
|
args: 'none',
|
|
2046
2203
|
desc: 'Generate Fish completion script',
|
|
2204
|
+
apply: (value, ctx) => {
|
|
2205
|
+
if (value === true) {
|
|
2206
|
+
registerCompletionShell(ctx, 'fish');
|
|
2207
|
+
}
|
|
2208
|
+
},
|
|
2047
2209
|
})
|
|
2048
2210
|
.option({
|
|
2049
2211
|
long: 'pwsh',
|
|
2050
2212
|
type: 'boolean',
|
|
2051
2213
|
args: 'none',
|
|
2052
2214
|
desc: 'Generate PowerShell completion script',
|
|
2215
|
+
apply: (value, ctx) => {
|
|
2216
|
+
if (value === true) {
|
|
2217
|
+
registerCompletionShell(ctx, 'pwsh');
|
|
2218
|
+
}
|
|
2219
|
+
mustGetCompletionShell(ctx);
|
|
2220
|
+
},
|
|
2053
2221
|
})
|
|
2054
2222
|
.option({
|
|
2055
2223
|
long: 'write',
|
|
2056
2224
|
short: 'w',
|
|
2057
2225
|
type: 'string',
|
|
2058
|
-
args: '
|
|
2059
|
-
desc: 'Write to file (use shell default path
|
|
2060
|
-
default: undefined,
|
|
2226
|
+
args: 'optional',
|
|
2227
|
+
desc: 'Write to file (use shell default path when value is omitted or empty)',
|
|
2061
2228
|
})
|
|
2062
|
-
.action(({ opts }) => {
|
|
2229
|
+
.action(({ opts, ctx }) => {
|
|
2063
2230
|
const meta = root.getCompletionMeta();
|
|
2064
|
-
const
|
|
2065
|
-
opts['bash'] && 'bash',
|
|
2066
|
-
opts['fish'] && 'fish',
|
|
2067
|
-
opts['pwsh'] && 'pwsh',
|
|
2068
|
-
].filter(Boolean);
|
|
2069
|
-
if (selectedShells.length === 0) {
|
|
2070
|
-
console.error('Please specify a shell: --bash, --fish, or --pwsh');
|
|
2071
|
-
process.exit(1);
|
|
2072
|
-
return;
|
|
2073
|
-
}
|
|
2074
|
-
if (selectedShells.length > 1) {
|
|
2075
|
-
console.error('Please specify only one shell option');
|
|
2076
|
-
process.exit(1);
|
|
2077
|
-
return;
|
|
2078
|
-
}
|
|
2079
|
-
const shell = selectedShells[0];
|
|
2231
|
+
const shell = mustGetCompletionShell(ctx);
|
|
2080
2232
|
let script;
|
|
2081
2233
|
switch (shell) {
|
|
2082
2234
|
case 'bash':
|
|
@@ -2089,8 +2241,9 @@ class CompletionCommand extends Command {
|
|
|
2089
2241
|
script = new PwshCompletion(meta, programName).generate();
|
|
2090
2242
|
break;
|
|
2091
2243
|
}
|
|
2092
|
-
const
|
|
2093
|
-
if (
|
|
2244
|
+
const hasWrite = Object.prototype.hasOwnProperty.call(opts, 'write');
|
|
2245
|
+
if (hasWrite) {
|
|
2246
|
+
const writeOpt = opts['write'];
|
|
2094
2247
|
const filePath = typeof writeOpt === 'string' && writeOpt !== '' ? writeOpt : paths[shell];
|
|
2095
2248
|
const expandedPath = expandHome(filePath);
|
|
2096
2249
|
const dir = path.dirname(expandedPath);
|
|
@@ -2157,7 +2310,7 @@ class BashCompletion {
|
|
|
2157
2310
|
if (opt.short)
|
|
2158
2311
|
optParts.push(this.#escapeWord(`-${opt.short}`));
|
|
2159
2312
|
optParts.push(this.#escapeWord(`--${kebabLong}`));
|
|
2160
|
-
if (
|
|
2313
|
+
if (canGenerateNegativeCompletion(opt)) {
|
|
2161
2314
|
optParts.push(this.#escapeWord(`--no-${kebabLong}`));
|
|
2162
2315
|
}
|
|
2163
2316
|
}
|
|
@@ -2189,7 +2342,7 @@ class BashCompletion {
|
|
|
2189
2342
|
return words.map(choice => this.#escapeWord(choice)).join(' ');
|
|
2190
2343
|
}
|
|
2191
2344
|
#appendChoiceLogicForCommand(lines, indent, cmd, depth) {
|
|
2192
|
-
const valueOptions = cmd.options.filter(
|
|
2345
|
+
const valueOptions = cmd.options.filter(optionTakesValue);
|
|
2193
2346
|
const valueOptionsWithChoices = valueOptions.filter(opt => opt.choices && opt.choices.length > 0);
|
|
2194
2347
|
const valueLongPatterns = valueOptions.map(opt => `--${camelToKebabCase(opt.long)}`);
|
|
2195
2348
|
const valueShortPatterns = valueOptions
|
|
@@ -2316,7 +2469,7 @@ class FishCompletion {
|
|
|
2316
2469
|
line += ` -xa '${opt.choices.map(choice => this.#escapeChoice(choice)).join(' ')}'`;
|
|
2317
2470
|
}
|
|
2318
2471
|
lines.push(line);
|
|
2319
|
-
if (
|
|
2472
|
+
if (canGenerateNegativeCompletion(opt)) {
|
|
2320
2473
|
let noLine = `complete -c ${this.#programName}`;
|
|
2321
2474
|
if (condition)
|
|
2322
2475
|
noLine += ` -n '${condition}'`;
|
|
@@ -2326,11 +2479,11 @@ class FishCompletion {
|
|
|
2326
2479
|
}
|
|
2327
2480
|
}
|
|
2328
2481
|
const valueOptionLongs = cmd.options
|
|
2329
|
-
.filter(
|
|
2482
|
+
.filter(optionTakesValue)
|
|
2330
2483
|
.map(opt => camelToKebabCase(opt.long))
|
|
2331
2484
|
.join(',');
|
|
2332
2485
|
const valueOptionShorts = cmd.options
|
|
2333
|
-
.filter(opt => opt
|
|
2486
|
+
.filter(opt => optionTakesValue(opt) && opt.short)
|
|
2334
2487
|
.map(opt => opt.short)
|
|
2335
2488
|
.join(',');
|
|
2336
2489
|
const argCount = cmd.arguments.length;
|
|
@@ -2529,7 +2682,7 @@ class PwshCompletion {
|
|
|
2529
2682
|
' if ($token.StartsWith("--")) {',
|
|
2530
2683
|
' if ($token.Contains("=")) { continue }',
|
|
2531
2684
|
' foreach ($opt in $cmd.options) {',
|
|
2532
|
-
' if ($token -eq "--$($opt.long)" -and $opt.
|
|
2685
|
+
' if ($token -eq "--$($opt.long)" -and $opt.args -ne "none") {',
|
|
2533
2686
|
' $expectValue = $true',
|
|
2534
2687
|
' break',
|
|
2535
2688
|
' }',
|
|
@@ -2539,7 +2692,7 @@ class PwshCompletion {
|
|
|
2539
2692
|
' if ($token.StartsWith("-") -and $token -ne "-") {',
|
|
2540
2693
|
' if ($token.Length -eq 2) {',
|
|
2541
2694
|
' foreach ($opt in $cmd.options) {',
|
|
2542
|
-
' if ($opt.short -and $token -eq "-$($opt.short)" -and $opt.
|
|
2695
|
+
' if ($opt.short -and $token -eq "-$($opt.short)" -and $opt.args -ne "none") {',
|
|
2543
2696
|
' $expectValue = $true',
|
|
2544
2697
|
' break',
|
|
2545
2698
|
' }',
|
|
@@ -2591,7 +2744,7 @@ class PwshCompletion {
|
|
|
2591
2744
|
' $opt.description',
|
|
2592
2745
|
' )',
|
|
2593
2746
|
' }',
|
|
2594
|
-
' if ($opt.
|
|
2747
|
+
' if ($opt.canNegate -and "--no-$($opt.long)" -like "$current*") {',
|
|
2595
2748
|
' $completions += [System.Management.Automation.CompletionResult]::new(',
|
|
2596
2749
|
' "--no-$($opt.long)",',
|
|
2597
2750
|
' "no-$($opt.long)",',
|
|
@@ -2641,8 +2794,9 @@ class PwshCompletion {
|
|
|
2641
2794
|
lines.push(`${indent} short = '${opt.short}'`);
|
|
2642
2795
|
lines.push(`${indent} long = '${kebabLong}'`);
|
|
2643
2796
|
lines.push(`${indent} description = '${this.#escape(opt.desc)}'`);
|
|
2644
|
-
lines.push(`${indent}
|
|
2645
|
-
lines.push(`${indent}
|
|
2797
|
+
lines.push(`${indent} type = '${opt.type}'`);
|
|
2798
|
+
lines.push(`${indent} args = '${opt.args}'`);
|
|
2799
|
+
lines.push(`${indent} canNegate = $${canGenerateNegativeCompletion(opt)}`);
|
|
2646
2800
|
if (opt.choices) {
|
|
2647
2801
|
lines.push(`${indent} choices = @('${opt.choices
|
|
2648
2802
|
.map(choice => this.#escape(choice))
|