@optique/core 1.1.12 → 1.1.13
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/completion.cjs +1709 -230
- package/dist/completion.js +1709 -230
- package/dist/constructs.cjs +7 -1
- package/dist/constructs.d.cts +2 -0
- package/dist/constructs.d.ts +2 -0
- package/dist/constructs.js +7 -1
- package/dist/internal/joined-file-suggestion.cjs +73 -0
- package/dist/internal/joined-file-suggestion.js +70 -0
- package/dist/internal/parser.d.cts +8 -0
- package/dist/internal/parser.d.ts +8 -0
- package/dist/modifiers.cjs +14 -8
- package/dist/modifiers.js +14 -8
- package/dist/primitives.cjs +19 -22
- package/dist/primitives.js +19 -22
- package/dist/suggestion.cjs +12 -5
- package/dist/suggestion.d.cts +1 -1
- package/dist/suggestion.d.ts +1 -1
- package/dist/suggestion.js +12 -5
- package/dist/valueparser.cjs +2 -1
- package/dist/valueparser.js +2 -1
- package/package.json +2 -2
package/dist/constructs.cjs
CHANGED
|
@@ -513,12 +513,16 @@ function analyzeNoMatchContext(parsers) {
|
|
|
513
513
|
* construction. This is a programmer error, not a user error.
|
|
514
514
|
*/
|
|
515
515
|
var DuplicateOptionError = class extends Error {
|
|
516
|
+
/** The option name shared by multiple fields. */
|
|
517
|
+
optionName;
|
|
518
|
+
/** The fields declaring the option. */
|
|
519
|
+
sources;
|
|
516
520
|
constructor(optionName$1, sources) {
|
|
517
521
|
const sourceNames = sources.map((s) => typeof s === "symbol" ? s.description ?? s.toString() : s);
|
|
518
522
|
super(`Duplicate option name "${optionName$1}" found in fields: ${sourceNames.join(", ")}. Each option name must be unique within a parser combinator.`);
|
|
523
|
+
this.name = "DuplicateOptionError";
|
|
519
524
|
this.optionName = optionName$1;
|
|
520
525
|
this.sources = sources;
|
|
521
|
-
this.name = "DuplicateOptionError";
|
|
522
526
|
}
|
|
523
527
|
};
|
|
524
528
|
/**
|
|
@@ -526,6 +530,8 @@ var DuplicateOptionError = class extends Error {
|
|
|
526
530
|
* during parser construction. This is a programmer error, not a user error.
|
|
527
531
|
*/
|
|
528
532
|
var DuplicateCommandNameError = class extends TypeError {
|
|
533
|
+
commandName;
|
|
534
|
+
sources;
|
|
529
535
|
constructor(commandName, sources) {
|
|
530
536
|
const sourceNames = sources.map((s) => typeof s === "symbol" ? s.description ?? s.toString() : s);
|
|
531
537
|
super(`Duplicate command name "${commandName}" found in parsers: ${sourceNames.join(", ")}. Each command name or alias must be unique within active parser alternatives.`);
|
package/dist/constructs.d.cts
CHANGED
|
@@ -102,7 +102,9 @@ interface OrErrorOptions {
|
|
|
102
102
|
* construction. This is a programmer error, not a user error.
|
|
103
103
|
*/
|
|
104
104
|
declare class DuplicateOptionError extends Error {
|
|
105
|
+
/** The option name shared by multiple fields. */
|
|
105
106
|
readonly optionName: string;
|
|
107
|
+
/** The fields declaring the option. */
|
|
106
108
|
readonly sources: readonly (string | symbol)[];
|
|
107
109
|
constructor(optionName: string, sources: readonly (string | symbol)[]);
|
|
108
110
|
}
|
package/dist/constructs.d.ts
CHANGED
|
@@ -102,7 +102,9 @@ interface OrErrorOptions {
|
|
|
102
102
|
* construction. This is a programmer error, not a user error.
|
|
103
103
|
*/
|
|
104
104
|
declare class DuplicateOptionError extends Error {
|
|
105
|
+
/** The option name shared by multiple fields. */
|
|
105
106
|
readonly optionName: string;
|
|
107
|
+
/** The fields declaring the option. */
|
|
106
108
|
readonly sources: readonly (string | symbol)[];
|
|
107
109
|
constructor(optionName: string, sources: readonly (string | symbol)[]);
|
|
108
110
|
}
|
package/dist/constructs.js
CHANGED
|
@@ -513,12 +513,16 @@ function analyzeNoMatchContext(parsers) {
|
|
|
513
513
|
* construction. This is a programmer error, not a user error.
|
|
514
514
|
*/
|
|
515
515
|
var DuplicateOptionError = class extends Error {
|
|
516
|
+
/** The option name shared by multiple fields. */
|
|
517
|
+
optionName;
|
|
518
|
+
/** The fields declaring the option. */
|
|
519
|
+
sources;
|
|
516
520
|
constructor(optionName$1, sources) {
|
|
517
521
|
const sourceNames = sources.map((s) => typeof s === "symbol" ? s.description ?? s.toString() : s);
|
|
518
522
|
super(`Duplicate option name "${optionName$1}" found in fields: ${sourceNames.join(", ")}. Each option name must be unique within a parser combinator.`);
|
|
523
|
+
this.name = "DuplicateOptionError";
|
|
519
524
|
this.optionName = optionName$1;
|
|
520
525
|
this.sources = sources;
|
|
521
|
-
this.name = "DuplicateOptionError";
|
|
522
526
|
}
|
|
523
527
|
};
|
|
524
528
|
/**
|
|
@@ -526,6 +530,8 @@ var DuplicateOptionError = class extends Error {
|
|
|
526
530
|
* during parser construction. This is a programmer error, not a user error.
|
|
527
531
|
*/
|
|
528
532
|
var DuplicateCommandNameError = class extends TypeError {
|
|
533
|
+
commandName;
|
|
534
|
+
sources;
|
|
529
535
|
constructor(commandName, sources) {
|
|
530
536
|
const sourceNames = sources.map((s) => typeof s === "symbol" ? s.description ?? s.toString() : s);
|
|
531
537
|
super(`Duplicate command name "${commandName}" found in parsers: ${sourceNames.join(", ")}. Each command name or alias must be unique within active parser alternatives.`);
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/internal/joined-file-suggestion.ts
|
|
3
|
+
const joinedFiles = /* @__PURE__ */ new WeakMap();
|
|
4
|
+
/**
|
|
5
|
+
* Keeps a joined file fallback's origin available to the Bash encoder.
|
|
6
|
+
* Other shells retain the existing literal representation.
|
|
7
|
+
* @param prefix The option name and delimiter.
|
|
8
|
+
* @param file The original file suggestion.
|
|
9
|
+
* @param value The value prefix being completed.
|
|
10
|
+
* @returns The literal fallback with private file metadata.
|
|
11
|
+
* @internal
|
|
12
|
+
*/
|
|
13
|
+
function joinedFileSuggestion(prefix, file, value) {
|
|
14
|
+
const literal = {
|
|
15
|
+
kind: "literal",
|
|
16
|
+
text: prefix + (file.pattern ?? ""),
|
|
17
|
+
description: file.description
|
|
18
|
+
};
|
|
19
|
+
joinedFiles.set(literal, {
|
|
20
|
+
files: [{
|
|
21
|
+
prefix,
|
|
22
|
+
file: file.pattern === value && !/[?*]/.test(value) && !(file.prefix != null && file.prefix !== "" && value.startsWith(file.prefix)) ? {
|
|
23
|
+
...file,
|
|
24
|
+
pattern: void 0
|
|
25
|
+
} : file
|
|
26
|
+
}],
|
|
27
|
+
literal: false
|
|
28
|
+
});
|
|
29
|
+
return literal;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Retrieves the originating file suggestion for a joined literal fallback.
|
|
33
|
+
* @param suggestion The suggestion to inspect.
|
|
34
|
+
* @returns Its private file metadata, if present.
|
|
35
|
+
* @internal
|
|
36
|
+
*/
|
|
37
|
+
function getJoinedFileSuggestion(suggestion) {
|
|
38
|
+
return joinedFiles.get(suggestion);
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Preserves file origins when equal literal fallbacks are deduplicated.
|
|
42
|
+
* @param target The retained suggestion.
|
|
43
|
+
* @param source The duplicate suggestion.
|
|
44
|
+
* @internal
|
|
45
|
+
*/
|
|
46
|
+
function mergeJoinedFileSuggestions(target, source) {
|
|
47
|
+
const a = joinedFiles.get(target);
|
|
48
|
+
const b = joinedFiles.get(source);
|
|
49
|
+
if (a === void 0 && b === void 0) return;
|
|
50
|
+
const files = [...a?.files ?? [], ...b?.files ?? []];
|
|
51
|
+
const seen = /* @__PURE__ */ new Set();
|
|
52
|
+
joinedFiles.set(target, {
|
|
53
|
+
literal: (a?.literal ?? true) || (b?.literal ?? true),
|
|
54
|
+
files: files.filter(({ prefix, file }) => {
|
|
55
|
+
const key = JSON.stringify([
|
|
56
|
+
prefix,
|
|
57
|
+
file.type,
|
|
58
|
+
file.pattern,
|
|
59
|
+
file.prefix,
|
|
60
|
+
file.includeHidden === true,
|
|
61
|
+
file.extensions?.toSorted()
|
|
62
|
+
]);
|
|
63
|
+
if (seen.has(key)) return false;
|
|
64
|
+
seen.add(key);
|
|
65
|
+
return true;
|
|
66
|
+
})
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
//#endregion
|
|
71
|
+
exports.getJoinedFileSuggestion = getJoinedFileSuggestion;
|
|
72
|
+
exports.joinedFileSuggestion = joinedFileSuggestion;
|
|
73
|
+
exports.mergeJoinedFileSuggestions = mergeJoinedFileSuggestions;
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
//#region src/internal/joined-file-suggestion.ts
|
|
2
|
+
const joinedFiles = /* @__PURE__ */ new WeakMap();
|
|
3
|
+
/**
|
|
4
|
+
* Keeps a joined file fallback's origin available to the Bash encoder.
|
|
5
|
+
* Other shells retain the existing literal representation.
|
|
6
|
+
* @param prefix The option name and delimiter.
|
|
7
|
+
* @param file The original file suggestion.
|
|
8
|
+
* @param value The value prefix being completed.
|
|
9
|
+
* @returns The literal fallback with private file metadata.
|
|
10
|
+
* @internal
|
|
11
|
+
*/
|
|
12
|
+
function joinedFileSuggestion(prefix, file, value) {
|
|
13
|
+
const literal = {
|
|
14
|
+
kind: "literal",
|
|
15
|
+
text: prefix + (file.pattern ?? ""),
|
|
16
|
+
description: file.description
|
|
17
|
+
};
|
|
18
|
+
joinedFiles.set(literal, {
|
|
19
|
+
files: [{
|
|
20
|
+
prefix,
|
|
21
|
+
file: file.pattern === value && !/[?*]/.test(value) && !(file.prefix != null && file.prefix !== "" && value.startsWith(file.prefix)) ? {
|
|
22
|
+
...file,
|
|
23
|
+
pattern: void 0
|
|
24
|
+
} : file
|
|
25
|
+
}],
|
|
26
|
+
literal: false
|
|
27
|
+
});
|
|
28
|
+
return literal;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* Retrieves the originating file suggestion for a joined literal fallback.
|
|
32
|
+
* @param suggestion The suggestion to inspect.
|
|
33
|
+
* @returns Its private file metadata, if present.
|
|
34
|
+
* @internal
|
|
35
|
+
*/
|
|
36
|
+
function getJoinedFileSuggestion(suggestion) {
|
|
37
|
+
return joinedFiles.get(suggestion);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Preserves file origins when equal literal fallbacks are deduplicated.
|
|
41
|
+
* @param target The retained suggestion.
|
|
42
|
+
* @param source The duplicate suggestion.
|
|
43
|
+
* @internal
|
|
44
|
+
*/
|
|
45
|
+
function mergeJoinedFileSuggestions(target, source) {
|
|
46
|
+
const a = joinedFiles.get(target);
|
|
47
|
+
const b = joinedFiles.get(source);
|
|
48
|
+
if (a === void 0 && b === void 0) return;
|
|
49
|
+
const files = [...a?.files ?? [], ...b?.files ?? []];
|
|
50
|
+
const seen = /* @__PURE__ */ new Set();
|
|
51
|
+
joinedFiles.set(target, {
|
|
52
|
+
literal: (a?.literal ?? true) || (b?.literal ?? true),
|
|
53
|
+
files: files.filter(({ prefix, file }) => {
|
|
54
|
+
const key = JSON.stringify([
|
|
55
|
+
prefix,
|
|
56
|
+
file.type,
|
|
57
|
+
file.pattern,
|
|
58
|
+
file.prefix,
|
|
59
|
+
file.includeHidden === true,
|
|
60
|
+
file.extensions?.toSorted()
|
|
61
|
+
]);
|
|
62
|
+
if (seen.has(key)) return false;
|
|
63
|
+
seen.add(key);
|
|
64
|
+
return true;
|
|
65
|
+
})
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
//#endregion
|
|
70
|
+
export { getJoinedFileSuggestion, joinedFileSuggestion, mergeJoinedFileSuggestions };
|
|
@@ -637,6 +637,14 @@ type Suggestion = {
|
|
|
637
637
|
* The current prefix/pattern for fallback when native completion is unavailable.
|
|
638
638
|
*/
|
|
639
639
|
readonly pattern?: string;
|
|
640
|
+
/**
|
|
641
|
+
* Literal text at the start of `pattern` that is preserved on insertion
|
|
642
|
+
* but excluded from native filesystem matching, such as `output=`.
|
|
643
|
+
* Shell adapters ignore it if it does not lead both the pattern and
|
|
644
|
+
* the current word. Regenerate installed scripts to use this field.
|
|
645
|
+
* @since 1.1.13
|
|
646
|
+
*/
|
|
647
|
+
readonly prefix?: string;
|
|
640
648
|
/**
|
|
641
649
|
* The type of file system entries to complete.
|
|
642
650
|
*/
|
|
@@ -637,6 +637,14 @@ type Suggestion = {
|
|
|
637
637
|
* The current prefix/pattern for fallback when native completion is unavailable.
|
|
638
638
|
*/
|
|
639
639
|
readonly pattern?: string;
|
|
640
|
+
/**
|
|
641
|
+
* Literal text at the start of `pattern` that is preserved on insertion
|
|
642
|
+
* but excluded from native filesystem matching, such as `output=`.
|
|
643
|
+
* Shell adapters ignore it if it does not lead both the pattern and
|
|
644
|
+
* the current word. Regenerate installed scripts to use this field.
|
|
645
|
+
* @since 1.1.13
|
|
646
|
+
*/
|
|
647
|
+
readonly prefix?: string;
|
|
640
648
|
/**
|
|
641
649
|
* The type of file system entries to complete.
|
|
642
650
|
*/
|
package/dist/modifiers.cjs
CHANGED
|
@@ -3,6 +3,7 @@ const require_message = require('./message.cjs');
|
|
|
3
3
|
const require_mode_dispatch = require('./internal/mode-dispatch.cjs');
|
|
4
4
|
const require_internal_parser = require('./internal/parser.cjs');
|
|
5
5
|
const require_annotation_state = require('./annotation-state.cjs');
|
|
6
|
+
const require_joined_file_suggestion = require('./internal/joined-file-suggestion.cjs');
|
|
6
7
|
const require_command_doc_metadata = require('./internal/command-doc-metadata.cjs');
|
|
7
8
|
const require_passthrough = require('./internal/passthrough.cjs');
|
|
8
9
|
const require_execution_context = require('./execution-context.cjs');
|
|
@@ -1311,6 +1312,7 @@ function multiple(parser, options = {}) {
|
|
|
1311
1312
|
"file",
|
|
1312
1313
|
suggestion.type,
|
|
1313
1314
|
suggestion.pattern ?? "",
|
|
1315
|
+
suggestion.prefix ?? "",
|
|
1314
1316
|
suggestion.includeHidden === true,
|
|
1315
1317
|
suggestion.extensions == null ? "" : suggestion.extensions.toSorted().join("\0"),
|
|
1316
1318
|
description
|
|
@@ -1318,14 +1320,16 @@ function multiple(parser, options = {}) {
|
|
|
1318
1320
|
};
|
|
1319
1321
|
return require_mode_dispatch.dispatchIterableByMode(parser.mode, function* () {
|
|
1320
1322
|
const selectedValues = collectSelectedValuesSync();
|
|
1321
|
-
const emitted = /* @__PURE__ */ new
|
|
1323
|
+
const emitted = /* @__PURE__ */ new Map();
|
|
1322
1324
|
const yieldUnique = function* (suggestions) {
|
|
1323
1325
|
for (const s of suggestions) {
|
|
1324
1326
|
const key = suggestionKey(s);
|
|
1325
|
-
if (shouldInclude(selectedValues, s)
|
|
1326
|
-
|
|
1327
|
+
if (!shouldInclude(selectedValues, s)) continue;
|
|
1328
|
+
const existing = emitted.get(key);
|
|
1329
|
+
if (existing === void 0) {
|
|
1330
|
+
emitted.set(key, s);
|
|
1327
1331
|
yield s;
|
|
1328
|
-
}
|
|
1332
|
+
} else require_joined_file_suggestion.mergeJoinedFileSuggestions(existing, s);
|
|
1329
1333
|
}
|
|
1330
1334
|
};
|
|
1331
1335
|
let shouldTryFallback = false;
|
|
@@ -1338,14 +1342,16 @@ function multiple(parser, options = {}) {
|
|
|
1338
1342
|
if (shouldTryFallback) yield* yieldUnique(syncParser.suggest(require_execution_context.withChildContext(context, itemIndex, suggestFallbackState), prefix));
|
|
1339
1343
|
}, async function* () {
|
|
1340
1344
|
const selectedValues = await collectSelectedValuesAsync();
|
|
1341
|
-
const emitted = /* @__PURE__ */ new
|
|
1345
|
+
const emitted = /* @__PURE__ */ new Map();
|
|
1342
1346
|
const yieldUnique = async function* (suggestions) {
|
|
1343
1347
|
for await (const s of suggestions) {
|
|
1344
1348
|
const key = suggestionKey(s);
|
|
1345
|
-
if (shouldInclude(selectedValues, s)
|
|
1346
|
-
|
|
1349
|
+
if (!shouldInclude(selectedValues, s)) continue;
|
|
1350
|
+
const existing = emitted.get(key);
|
|
1351
|
+
if (existing === void 0) {
|
|
1352
|
+
emitted.set(key, s);
|
|
1347
1353
|
yield s;
|
|
1348
|
-
}
|
|
1354
|
+
} else require_joined_file_suggestion.mergeJoinedFileSuggestions(existing, s);
|
|
1349
1355
|
}
|
|
1350
1356
|
};
|
|
1351
1357
|
let shouldTryFallback = false;
|
package/dist/modifiers.js
CHANGED
|
@@ -3,6 +3,7 @@ import { formatMessage, message, text } from "./message.js";
|
|
|
3
3
|
import { dispatchByMode, dispatchIterableByMode, mapModeValue, wrapForMode } from "./internal/mode-dispatch.js";
|
|
4
4
|
import { defineInheritedAnnotationParser, defineParseLanes, defineSourceBindingOnlyAnnotationCompletionParser, getOwnParseLanes, unmatchedNonCliDependencySourceStateMarker } from "./internal/parser.js";
|
|
5
5
|
import { getDelegatedAnnotationState, hasDelegatedAnnotationCarrier, isAnnotationWrappedInitialState, normalizeDelegatedAnnotationState, normalizeNestedDelegatedAnnotationState } from "./annotation-state.js";
|
|
6
|
+
import { mergeJoinedFileSuggestions } from "./internal/joined-file-suggestion.js";
|
|
6
7
|
import { getCommandDocMetadata, withCommandDocMetadata } from "./internal/command-doc-metadata.js";
|
|
7
8
|
import { defineKnownCompletionLookup, defineOptionMatch, definePassThroughPriority, getKnownCompletion, getOptionMatch, getPassThroughPriority } from "./internal/passthrough.js";
|
|
8
9
|
import { mergeChildExec, withChildContext, withChildExecPath } from "./execution-context.js";
|
|
@@ -1311,6 +1312,7 @@ function multiple(parser, options = {}) {
|
|
|
1311
1312
|
"file",
|
|
1312
1313
|
suggestion.type,
|
|
1313
1314
|
suggestion.pattern ?? "",
|
|
1315
|
+
suggestion.prefix ?? "",
|
|
1314
1316
|
suggestion.includeHidden === true,
|
|
1315
1317
|
suggestion.extensions == null ? "" : suggestion.extensions.toSorted().join("\0"),
|
|
1316
1318
|
description
|
|
@@ -1318,14 +1320,16 @@ function multiple(parser, options = {}) {
|
|
|
1318
1320
|
};
|
|
1319
1321
|
return dispatchIterableByMode(parser.mode, function* () {
|
|
1320
1322
|
const selectedValues = collectSelectedValuesSync();
|
|
1321
|
-
const emitted = /* @__PURE__ */ new
|
|
1323
|
+
const emitted = /* @__PURE__ */ new Map();
|
|
1322
1324
|
const yieldUnique = function* (suggestions) {
|
|
1323
1325
|
for (const s of suggestions) {
|
|
1324
1326
|
const key = suggestionKey(s);
|
|
1325
|
-
if (shouldInclude(selectedValues, s)
|
|
1326
|
-
|
|
1327
|
+
if (!shouldInclude(selectedValues, s)) continue;
|
|
1328
|
+
const existing = emitted.get(key);
|
|
1329
|
+
if (existing === void 0) {
|
|
1330
|
+
emitted.set(key, s);
|
|
1327
1331
|
yield s;
|
|
1328
|
-
}
|
|
1332
|
+
} else mergeJoinedFileSuggestions(existing, s);
|
|
1329
1333
|
}
|
|
1330
1334
|
};
|
|
1331
1335
|
let shouldTryFallback = false;
|
|
@@ -1338,14 +1342,16 @@ function multiple(parser, options = {}) {
|
|
|
1338
1342
|
if (shouldTryFallback) yield* yieldUnique(syncParser.suggest(withChildContext(context, itemIndex, suggestFallbackState), prefix));
|
|
1339
1343
|
}, async function* () {
|
|
1340
1344
|
const selectedValues = await collectSelectedValuesAsync();
|
|
1341
|
-
const emitted = /* @__PURE__ */ new
|
|
1345
|
+
const emitted = /* @__PURE__ */ new Map();
|
|
1342
1346
|
const yieldUnique = async function* (suggestions) {
|
|
1343
1347
|
for await (const s of suggestions) {
|
|
1344
1348
|
const key = suggestionKey(s);
|
|
1345
|
-
if (shouldInclude(selectedValues, s)
|
|
1346
|
-
|
|
1349
|
+
if (!shouldInclude(selectedValues, s)) continue;
|
|
1350
|
+
const existing = emitted.get(key);
|
|
1351
|
+
if (existing === void 0) {
|
|
1352
|
+
emitted.set(key, s);
|
|
1347
1353
|
yield s;
|
|
1348
|
-
}
|
|
1354
|
+
} else mergeJoinedFileSuggestions(existing, s);
|
|
1349
1355
|
}
|
|
1350
1356
|
};
|
|
1351
1357
|
let shouldTryFallback = false;
|
package/dist/primitives.cjs
CHANGED
|
@@ -6,6 +6,7 @@ const require_mode_dispatch = require('./internal/mode-dispatch.cjs');
|
|
|
6
6
|
const require_internal_dependency = require('./internal/dependency.cjs');
|
|
7
7
|
const require_dependency_runtime = require('./dependency-runtime.cjs');
|
|
8
8
|
const require_annotation_state = require('./annotation-state.cjs');
|
|
9
|
+
const require_joined_file_suggestion = require('./internal/joined-file-suggestion.cjs');
|
|
9
10
|
const require_command_doc_metadata = require('./internal/command-doc-metadata.cjs');
|
|
10
11
|
const require_passthrough = require('./internal/passthrough.cjs');
|
|
11
12
|
const require_command_alias = require('./internal/command-alias.cjs');
|
|
@@ -246,33 +247,33 @@ function* getSuggestionsWithDependency(valueParser, prefix, dependencyRegistry,
|
|
|
246
247
|
}
|
|
247
248
|
yield* valueParser.suggest(prefix);
|
|
248
249
|
}
|
|
250
|
+
/** Returns the joined prefix accepted by option parsing. */
|
|
251
|
+
function joinedOptionPrefix(names, input) {
|
|
252
|
+
return names.filter((name) => name.startsWith("--") || name.startsWith("/") || name.startsWith("-") && name.length > 2).map((name) => name + (name.startsWith("/") ? ":" : "=")).find((prefix) => input.startsWith(prefix));
|
|
253
|
+
}
|
|
249
254
|
/**
|
|
250
255
|
* Internal sync helper for option suggest functionality.
|
|
251
256
|
* @internal
|
|
252
257
|
*/
|
|
253
258
|
function* suggestOptionSync(optionNames$1, valueParser, hidden, description, context, prefix) {
|
|
254
259
|
if (hidden || context.optionsTerminated) return;
|
|
255
|
-
const
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
const
|
|
260
|
+
const expectingValue = context.buffer.length > 0 && optionNames$1.includes(context.buffer[context.buffer.length - 1]);
|
|
261
|
+
const joinedPrefix = joinedOptionPrefix(optionNames$1, prefix);
|
|
262
|
+
if (joinedPrefix !== void 0 && !expectingValue) {
|
|
263
|
+
const optionPart = joinedPrefix.slice(0, -1);
|
|
264
|
+
const valuePart = prefix.slice(joinedPrefix.length);
|
|
259
265
|
if (optionNames$1.includes(optionPart)) {
|
|
260
266
|
if (valueParser && valueParser.suggest) {
|
|
261
267
|
const valueSuggestions = getSuggestionsWithDependency(valueParser, valuePart, context.dependencyRegistry, context.exec);
|
|
262
268
|
for (const suggestion of valueSuggestions) if (suggestion.kind === "literal") yield {
|
|
263
269
|
kind: "literal",
|
|
264
|
-
text: `${
|
|
265
|
-
description: suggestion.description
|
|
266
|
-
};
|
|
267
|
-
else yield {
|
|
268
|
-
kind: "literal",
|
|
269
|
-
text: `${optionPart}=${suggestion.pattern || ""}`,
|
|
270
|
+
text: `${joinedPrefix}${suggestion.text}`,
|
|
270
271
|
description: suggestion.description
|
|
271
272
|
};
|
|
273
|
+
else yield require_joined_file_suggestion.joinedFileSuggestion(joinedPrefix, suggestion, valuePart);
|
|
272
274
|
}
|
|
273
275
|
}
|
|
274
276
|
} else {
|
|
275
|
-
const expectingValue = context.buffer.length > 0 && optionNames$1.includes(context.buffer[context.buffer.length - 1]);
|
|
276
277
|
if (!expectingValue && (prefix.startsWith("--") || prefix.startsWith("-") || prefix.startsWith("/") || prefix.startsWith("+"))) {
|
|
277
278
|
for (const optionName$1 of optionNames$1) if (optionName$1.startsWith(prefix)) {
|
|
278
279
|
if (prefix === "-" && optionName$1.length !== 2) continue;
|
|
@@ -337,27 +338,23 @@ async function* getSuggestionsWithDependencyAsync(valueParser, prefix, dependenc
|
|
|
337
338
|
*/
|
|
338
339
|
async function* suggestOptionAsync(optionNames$1, valueParser, hidden, description, context, prefix) {
|
|
339
340
|
if (hidden || context.optionsTerminated) return;
|
|
340
|
-
const
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
const
|
|
341
|
+
const expectingValue = context.buffer.length > 0 && optionNames$1.includes(context.buffer[context.buffer.length - 1]);
|
|
342
|
+
const joinedPrefix = joinedOptionPrefix(optionNames$1, prefix);
|
|
343
|
+
if (joinedPrefix !== void 0 && !expectingValue) {
|
|
344
|
+
const optionPart = joinedPrefix.slice(0, -1);
|
|
345
|
+
const valuePart = prefix.slice(joinedPrefix.length);
|
|
344
346
|
if (optionNames$1.includes(optionPart)) {
|
|
345
347
|
if (valueParser && valueParser.suggest) {
|
|
346
348
|
const valueSuggestions = getSuggestionsWithDependencyAsync(valueParser, valuePart, context.dependencyRegistry, context.exec);
|
|
347
349
|
for await (const suggestion of valueSuggestions) if (suggestion.kind === "literal") yield {
|
|
348
350
|
kind: "literal",
|
|
349
|
-
text: `${
|
|
350
|
-
description: suggestion.description
|
|
351
|
-
};
|
|
352
|
-
else yield {
|
|
353
|
-
kind: "literal",
|
|
354
|
-
text: `${optionPart}=${suggestion.pattern || ""}`,
|
|
351
|
+
text: `${joinedPrefix}${suggestion.text}`,
|
|
355
352
|
description: suggestion.description
|
|
356
353
|
};
|
|
354
|
+
else yield require_joined_file_suggestion.joinedFileSuggestion(joinedPrefix, suggestion, valuePart);
|
|
357
355
|
}
|
|
358
356
|
}
|
|
359
357
|
} else {
|
|
360
|
-
const expectingValue = context.buffer.length > 0 && optionNames$1.includes(context.buffer[context.buffer.length - 1]);
|
|
361
358
|
if (!expectingValue && (prefix.startsWith("--") || prefix.startsWith("-") || prefix.startsWith("/") || prefix.startsWith("+"))) {
|
|
362
359
|
for (const optionName$1 of optionNames$1) if (optionName$1.startsWith(prefix)) {
|
|
363
360
|
if (prefix === "-" && optionName$1.length !== 2) continue;
|
package/dist/primitives.js
CHANGED
|
@@ -6,6 +6,7 @@ import { dispatchByMode, dispatchIterableByMode, wrapForMode } from "./internal/
|
|
|
6
6
|
import { getDefaultValuesFunction, getDependencyIds, getSnapshottedDefaultDependencyValues, isDerivedValueParser, suggestWithDependency } from "./internal/dependency.js";
|
|
7
7
|
import { replayDerivedParser, replayDerivedParserAsync } from "./dependency-runtime.js";
|
|
8
8
|
import { getWrappedChildParseState, getWrappedChildState, isAnnotationWrappedInitialState, normalizeInjectedAnnotationState } from "./annotation-state.js";
|
|
9
|
+
import { joinedFileSuggestion } from "./internal/joined-file-suggestion.js";
|
|
9
10
|
import { getCommandDocMetadata, withCommandDocMetadata } from "./internal/command-doc-metadata.js";
|
|
10
11
|
import { defineKnownCompletion, defineOptionMatch, definePassThroughPriority, getOptionMatch, getPassThroughFailure, getPassThroughPriority } from "./internal/passthrough.js";
|
|
11
12
|
import { hiddenCommandAliasesKey } from "./internal/command-alias.js";
|
|
@@ -246,33 +247,33 @@ function* getSuggestionsWithDependency(valueParser, prefix, dependencyRegistry,
|
|
|
246
247
|
}
|
|
247
248
|
yield* valueParser.suggest(prefix);
|
|
248
249
|
}
|
|
250
|
+
/** Returns the joined prefix accepted by option parsing. */
|
|
251
|
+
function joinedOptionPrefix(names, input) {
|
|
252
|
+
return names.filter((name) => name.startsWith("--") || name.startsWith("/") || name.startsWith("-") && name.length > 2).map((name) => name + (name.startsWith("/") ? ":" : "=")).find((prefix) => input.startsWith(prefix));
|
|
253
|
+
}
|
|
249
254
|
/**
|
|
250
255
|
* Internal sync helper for option suggest functionality.
|
|
251
256
|
* @internal
|
|
252
257
|
*/
|
|
253
258
|
function* suggestOptionSync(optionNames$1, valueParser, hidden, description, context, prefix) {
|
|
254
259
|
if (hidden || context.optionsTerminated) return;
|
|
255
|
-
const
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
const
|
|
260
|
+
const expectingValue = context.buffer.length > 0 && optionNames$1.includes(context.buffer[context.buffer.length - 1]);
|
|
261
|
+
const joinedPrefix = joinedOptionPrefix(optionNames$1, prefix);
|
|
262
|
+
if (joinedPrefix !== void 0 && !expectingValue) {
|
|
263
|
+
const optionPart = joinedPrefix.slice(0, -1);
|
|
264
|
+
const valuePart = prefix.slice(joinedPrefix.length);
|
|
259
265
|
if (optionNames$1.includes(optionPart)) {
|
|
260
266
|
if (valueParser && valueParser.suggest) {
|
|
261
267
|
const valueSuggestions = getSuggestionsWithDependency(valueParser, valuePart, context.dependencyRegistry, context.exec);
|
|
262
268
|
for (const suggestion of valueSuggestions) if (suggestion.kind === "literal") yield {
|
|
263
269
|
kind: "literal",
|
|
264
|
-
text: `${
|
|
265
|
-
description: suggestion.description
|
|
266
|
-
};
|
|
267
|
-
else yield {
|
|
268
|
-
kind: "literal",
|
|
269
|
-
text: `${optionPart}=${suggestion.pattern || ""}`,
|
|
270
|
+
text: `${joinedPrefix}${suggestion.text}`,
|
|
270
271
|
description: suggestion.description
|
|
271
272
|
};
|
|
273
|
+
else yield joinedFileSuggestion(joinedPrefix, suggestion, valuePart);
|
|
272
274
|
}
|
|
273
275
|
}
|
|
274
276
|
} else {
|
|
275
|
-
const expectingValue = context.buffer.length > 0 && optionNames$1.includes(context.buffer[context.buffer.length - 1]);
|
|
276
277
|
if (!expectingValue && (prefix.startsWith("--") || prefix.startsWith("-") || prefix.startsWith("/") || prefix.startsWith("+"))) {
|
|
277
278
|
for (const optionName$1 of optionNames$1) if (optionName$1.startsWith(prefix)) {
|
|
278
279
|
if (prefix === "-" && optionName$1.length !== 2) continue;
|
|
@@ -337,27 +338,23 @@ async function* getSuggestionsWithDependencyAsync(valueParser, prefix, dependenc
|
|
|
337
338
|
*/
|
|
338
339
|
async function* suggestOptionAsync(optionNames$1, valueParser, hidden, description, context, prefix) {
|
|
339
340
|
if (hidden || context.optionsTerminated) return;
|
|
340
|
-
const
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
const
|
|
341
|
+
const expectingValue = context.buffer.length > 0 && optionNames$1.includes(context.buffer[context.buffer.length - 1]);
|
|
342
|
+
const joinedPrefix = joinedOptionPrefix(optionNames$1, prefix);
|
|
343
|
+
if (joinedPrefix !== void 0 && !expectingValue) {
|
|
344
|
+
const optionPart = joinedPrefix.slice(0, -1);
|
|
345
|
+
const valuePart = prefix.slice(joinedPrefix.length);
|
|
344
346
|
if (optionNames$1.includes(optionPart)) {
|
|
345
347
|
if (valueParser && valueParser.suggest) {
|
|
346
348
|
const valueSuggestions = getSuggestionsWithDependencyAsync(valueParser, valuePart, context.dependencyRegistry, context.exec);
|
|
347
349
|
for await (const suggestion of valueSuggestions) if (suggestion.kind === "literal") yield {
|
|
348
350
|
kind: "literal",
|
|
349
|
-
text: `${
|
|
350
|
-
description: suggestion.description
|
|
351
|
-
};
|
|
352
|
-
else yield {
|
|
353
|
-
kind: "literal",
|
|
354
|
-
text: `${optionPart}=${suggestion.pattern || ""}`,
|
|
351
|
+
text: `${joinedPrefix}${suggestion.text}`,
|
|
355
352
|
description: suggestion.description
|
|
356
353
|
};
|
|
354
|
+
else yield joinedFileSuggestion(joinedPrefix, suggestion, valuePart);
|
|
357
355
|
}
|
|
358
356
|
}
|
|
359
357
|
} else {
|
|
360
|
-
const expectingValue = context.buffer.length > 0 && optionNames$1.includes(context.buffer[context.buffer.length - 1]);
|
|
361
358
|
if (!expectingValue && (prefix.startsWith("--") || prefix.startsWith("-") || prefix.startsWith("/") || prefix.startsWith("+"))) {
|
|
362
359
|
for (const optionName$1 of optionNames$1) if (optionName$1.startsWith(prefix)) {
|
|
363
360
|
if (prefix === "-" && optionName$1.length !== 2) continue;
|
package/dist/suggestion.cjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const require_message = require('./message.cjs');
|
|
2
2
|
const require_usage = require('./usage.cjs');
|
|
3
|
+
const require_joined_file_suggestion = require('./internal/joined-file-suggestion.cjs');
|
|
3
4
|
|
|
4
5
|
//#region src/suggestion.ts
|
|
5
6
|
/**
|
|
@@ -257,22 +258,27 @@ function createErrorWithSuggestions(baseError, invalidInput, usage, type = "both
|
|
|
257
258
|
*
|
|
258
259
|
* For literal suggestions, the text itself is used as the key.
|
|
259
260
|
* For file suggestions, a composite key is created from the type,
|
|
260
|
-
* extensions, and
|
|
261
|
+
* extensions, pattern, and insertion prefix.
|
|
261
262
|
*
|
|
262
263
|
* @param suggestion The suggestion to create a key for
|
|
263
264
|
* @returns A string key that uniquely identifies this suggestion
|
|
264
265
|
* @internal
|
|
265
266
|
*/
|
|
266
267
|
function getSuggestionKey(suggestion) {
|
|
267
|
-
if (suggestion.kind === "literal") return suggestion.text
|
|
268
|
-
return `
|
|
268
|
+
if (suggestion.kind === "literal") return `literal:${suggestion.text}`;
|
|
269
|
+
return `file:${JSON.stringify([
|
|
270
|
+
suggestion.type,
|
|
271
|
+
suggestion.extensions?.toSorted().join(",") ?? "",
|
|
272
|
+
suggestion.pattern ?? "",
|
|
273
|
+
suggestion.prefix ?? ""
|
|
274
|
+
])}`;
|
|
269
275
|
}
|
|
270
276
|
/**
|
|
271
277
|
* Removes duplicate suggestions from an array while preserving order.
|
|
272
278
|
*
|
|
273
279
|
* Suggestions are considered duplicates if they have the same key:
|
|
274
280
|
* - Literal suggestions: same text
|
|
275
|
-
* - File suggestions: same type, extensions, and
|
|
281
|
+
* - File suggestions: same type, extensions, pattern, and insertion prefix
|
|
276
282
|
*
|
|
277
283
|
* The first occurrence of each unique suggestion is kept. For file
|
|
278
284
|
* suggestions, `includeHidden` is merged across duplicates: if any
|
|
@@ -305,7 +311,8 @@ function deduplicateSuggestions(suggestions) {
|
|
|
305
311
|
if (existing == null) {
|
|
306
312
|
entries.set(key, suggestion);
|
|
307
313
|
order.push(key);
|
|
308
|
-
} else if (suggestion.kind === "
|
|
314
|
+
} else if (suggestion.kind === "literal" && existing.kind === "literal") require_joined_file_suggestion.mergeJoinedFileSuggestions(existing, suggestion);
|
|
315
|
+
else if (suggestion.kind === "file" && existing.kind === "file" && suggestion.includeHidden === true && existing.includeHidden !== true) entries.set(key, {
|
|
309
316
|
...existing,
|
|
310
317
|
includeHidden: true
|
|
311
318
|
});
|
package/dist/suggestion.d.cts
CHANGED
|
@@ -159,7 +159,7 @@ declare function createErrorWithSuggestions(baseError: Message, invalidInput: st
|
|
|
159
159
|
*
|
|
160
160
|
* Suggestions are considered duplicates if they have the same key:
|
|
161
161
|
* - Literal suggestions: same text
|
|
162
|
-
* - File suggestions: same type, extensions, and
|
|
162
|
+
* - File suggestions: same type, extensions, pattern, and insertion prefix
|
|
163
163
|
*
|
|
164
164
|
* The first occurrence of each unique suggestion is kept. For file
|
|
165
165
|
* suggestions, `includeHidden` is merged across duplicates: if any
|
package/dist/suggestion.d.ts
CHANGED
|
@@ -159,7 +159,7 @@ declare function createErrorWithSuggestions(baseError: Message, invalidInput: st
|
|
|
159
159
|
*
|
|
160
160
|
* Suggestions are considered duplicates if they have the same key:
|
|
161
161
|
* - Literal suggestions: same text
|
|
162
|
-
* - File suggestions: same type, extensions, and
|
|
162
|
+
* - File suggestions: same type, extensions, pattern, and insertion prefix
|
|
163
163
|
*
|
|
164
164
|
* The first occurrence of each unique suggestion is kept. For file
|
|
165
165
|
* suggestions, `includeHidden` is merged across duplicates: if any
|