@optique/core 1.0.16-dev.2947 → 1.0.16-dev.2983
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/constructs.cjs +52 -18
- package/dist/constructs.js +53 -19
- package/dist/input-trace.cjs +89 -10
- package/dist/input-trace.d.cts +6 -3
- package/dist/input-trace.d.ts +6 -3
- package/dist/input-trace.js +89 -10
- package/dist/internal/command-doc-metadata.cjs +32 -4
- package/dist/internal/command-doc-metadata.js +30 -4
- package/dist/internal/command-usage.cjs +52 -0
- package/dist/internal/command-usage.js +47 -0
- package/dist/internal/parser.cjs +111 -7
- package/dist/internal/parser.js +111 -7
- package/dist/message.cjs +4 -3
- package/dist/message.js +4 -3
- package/dist/modifiers.cjs +15 -1
- package/dist/modifiers.js +16 -2
- package/dist/primitives.cjs +7 -4
- package/dist/primitives.js +8 -5
- package/dist/usage.cjs +7 -6
- package/dist/usage.js +7 -6
- package/package.json +1 -1
package/dist/input-trace.js
CHANGED
|
@@ -21,23 +21,102 @@ function serializePath(path) {
|
|
|
21
21
|
if (path.length === 0) return "";
|
|
22
22
|
return path.map(serializeKey).join("");
|
|
23
23
|
}
|
|
24
|
+
function height(node$1) {
|
|
25
|
+
return node$1?.height ?? 0;
|
|
26
|
+
}
|
|
27
|
+
function node(key, entry, left, right) {
|
|
28
|
+
return {
|
|
29
|
+
key,
|
|
30
|
+
entry,
|
|
31
|
+
left,
|
|
32
|
+
right,
|
|
33
|
+
height: 1 + Math.max(height(left), height(right))
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
function balance(key, entry, left, right) {
|
|
37
|
+
if (height(left) - height(right) > 1 && left != null) {
|
|
38
|
+
const pivot = left.right;
|
|
39
|
+
if (height(pivot) > height(left.left) && pivot != null) return node(pivot.key, pivot.entry, node(left.key, left.entry, left.left, pivot.left), node(key, entry, pivot.right, right));
|
|
40
|
+
return node(left.key, left.entry, left.left, node(key, entry, left.right, right));
|
|
41
|
+
}
|
|
42
|
+
if (height(right) - height(left) > 1 && right != null) {
|
|
43
|
+
const pivot = right.left;
|
|
44
|
+
if (height(pivot) > height(right.right) && pivot != null) return node(pivot.key, pivot.entry, node(key, entry, left, pivot.left), node(right.key, right.entry, pivot.right, right.right));
|
|
45
|
+
return node(right.key, right.entry, node(key, entry, left, right.left), right.right);
|
|
46
|
+
}
|
|
47
|
+
return node(key, entry, left, right);
|
|
48
|
+
}
|
|
49
|
+
function setEntry(root, key, entry) {
|
|
50
|
+
if (root == null) return node(key, entry, void 0, void 0);
|
|
51
|
+
if (key === root.key) return entry === root.entry ? root : {
|
|
52
|
+
...root,
|
|
53
|
+
entry
|
|
54
|
+
};
|
|
55
|
+
if (key < root.key) {
|
|
56
|
+
const left = setEntry(root.left, key, entry);
|
|
57
|
+
return left === root.left ? root : balance(root.key, root.entry, left, root.right);
|
|
58
|
+
}
|
|
59
|
+
const right = setEntry(root.right, key, entry);
|
|
60
|
+
return right === root.right ? root : balance(root.key, root.entry, root.left, right);
|
|
61
|
+
}
|
|
62
|
+
function popMin(root) {
|
|
63
|
+
if (root.left == null) return [root, root.right];
|
|
64
|
+
const [minimum, left] = popMin(root.left);
|
|
65
|
+
return [minimum, balance(root.key, root.entry, left, root.right)];
|
|
66
|
+
}
|
|
67
|
+
function deleteEntry(root, key) {
|
|
68
|
+
if (root == null) return void 0;
|
|
69
|
+
if (key === root.key) {
|
|
70
|
+
if (root.left == null) return root.right;
|
|
71
|
+
if (root.right == null) return root.left;
|
|
72
|
+
const [successor, right$1] = popMin(root.right);
|
|
73
|
+
return balance(successor.key, successor.entry, root.left, right$1);
|
|
74
|
+
}
|
|
75
|
+
if (key < root.key) {
|
|
76
|
+
const left = deleteEntry(root.left, key);
|
|
77
|
+
return left === root.left ? root : balance(root.key, root.entry, left, root.right);
|
|
78
|
+
}
|
|
79
|
+
const right = deleteEntry(root.right, key);
|
|
80
|
+
return right === root.right ? root : balance(root.key, root.entry, root.left, right);
|
|
81
|
+
}
|
|
82
|
+
function inspectNode(root, lower, upper) {
|
|
83
|
+
if (root == null) return {
|
|
84
|
+
size: 0,
|
|
85
|
+
height: 0
|
|
86
|
+
};
|
|
87
|
+
if (lower !== void 0 && root.key <= lower || upper !== void 0 && root.key >= upper) throw new TypeError("Input trace keys are not strictly ordered.");
|
|
88
|
+
const left = inspectNode(root.left, lower, root.key);
|
|
89
|
+
const right = inspectNode(root.right, root.key, upper);
|
|
90
|
+
const actualHeight = 1 + Math.max(left.height, right.height);
|
|
91
|
+
if (root.height !== actualHeight || Math.abs(left.height - right.height) > 1) throw new TypeError("Input trace heights or balance are invalid.");
|
|
92
|
+
return {
|
|
93
|
+
size: 1 + left.size + right.size,
|
|
94
|
+
height: actualHeight
|
|
95
|
+
};
|
|
96
|
+
}
|
|
24
97
|
var InputTraceImpl = class InputTraceImpl {
|
|
25
|
-
#
|
|
26
|
-
constructor(
|
|
27
|
-
this.#
|
|
98
|
+
#root;
|
|
99
|
+
constructor(root) {
|
|
100
|
+
this.#root = root;
|
|
28
101
|
}
|
|
29
102
|
get(path) {
|
|
30
|
-
|
|
103
|
+
const key = serializePath(path);
|
|
104
|
+
let current = this.#root;
|
|
105
|
+
while (current != null) {
|
|
106
|
+
if (key === current.key) return current.entry;
|
|
107
|
+
current = key < current.key ? current.left : current.right;
|
|
108
|
+
}
|
|
109
|
+
return void 0;
|
|
31
110
|
}
|
|
32
111
|
set(path, entry) {
|
|
33
|
-
|
|
34
|
-
copy.set(serializePath(path), entry);
|
|
35
|
-
return new InputTraceImpl(copy);
|
|
112
|
+
return new InputTraceImpl(setEntry(this.#root, serializePath(path), entry));
|
|
36
113
|
}
|
|
37
114
|
delete(path) {
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
115
|
+
return new InputTraceImpl(deleteEntry(this.#root, serializePath(path)));
|
|
116
|
+
}
|
|
117
|
+
static inspect(trace) {
|
|
118
|
+
if (!(trace instanceof InputTraceImpl)) throw new TypeError("Expected an Optique input trace.");
|
|
119
|
+
return inspectNode(trace.#root);
|
|
41
120
|
}
|
|
42
121
|
};
|
|
43
122
|
/**
|
|
@@ -1,7 +1,24 @@
|
|
|
1
|
+
const require_command_usage = require('./command-usage.cjs');
|
|
1
2
|
|
|
2
3
|
//#region src/internal/command-doc-metadata.ts
|
|
4
|
+
const selectedCommandUsage = /* @__PURE__ */ new WeakMap();
|
|
5
|
+
/** Reads command occurrences selected by final parser state. @internal */
|
|
6
|
+
function getSelectedCommandUsage(metadata) {
|
|
7
|
+
return metadata == null ? [] : selectedCommandUsage.get(metadata) ?? [];
|
|
8
|
+
}
|
|
9
|
+
/** Attaches selection without adding public metadata properties. @internal */
|
|
10
|
+
function withSelectedCommandUsage(metadata, selected) {
|
|
11
|
+
const result = withOrigin(metadata ?? {}, metadata?.origin ?? "entry");
|
|
12
|
+
selectedCommandUsage.set(result, selected);
|
|
13
|
+
return result;
|
|
14
|
+
}
|
|
15
|
+
/** Maps selected commands to the same child scope as its usage. @internal */
|
|
16
|
+
function scopeCommandDocMetadata(metadata, scope) {
|
|
17
|
+
return metadata == null ? void 0 : withSelectedCommandUsage(metadata, getSelectedCommandUsage(metadata).map((id) => require_command_usage.scopeCommandUsageId(scope, id)));
|
|
18
|
+
}
|
|
3
19
|
function withOrigin(metadata, origin) {
|
|
4
20
|
const result = { ...metadata };
|
|
21
|
+
selectedCommandUsage.set(result, getSelectedCommandUsage(metadata));
|
|
5
22
|
Object.defineProperty(result, "origin", {
|
|
6
23
|
value: origin,
|
|
7
24
|
enumerable: false
|
|
@@ -23,11 +40,11 @@ function getCommandDocMetadata(docs) {
|
|
|
23
40
|
const customPage = origin === "entry" && (docs.brief !== carrier.brief || docs.footer !== carrier.footer);
|
|
24
41
|
const description = origin === "entry" && !customPage || docs.description === carrier.description ? carrier.metadata.description : docs.description;
|
|
25
42
|
const footer = docs.footer === carrier.footer ? carrier.metadata.footer : docs.footer;
|
|
26
|
-
return withOrigin({
|
|
43
|
+
return withSelectedCommandUsage(withOrigin({
|
|
27
44
|
...brief == null ? {} : { brief },
|
|
28
45
|
...description == null ? {} : { description },
|
|
29
46
|
...footer == null ? {} : { footer }
|
|
30
|
-
}, customPage ? "custom" : origin);
|
|
47
|
+
}, customPage ? "custom" : origin), getSelectedCommandUsage(carrier.metadata));
|
|
31
48
|
}
|
|
32
49
|
const metadata = docs[commandDocMetadata];
|
|
33
50
|
if (metadata != null) return metadata;
|
|
@@ -67,7 +84,7 @@ function withCommandDocMetadata(docs, metadata) {
|
|
|
67
84
|
* @param includeCustom Whether to retain custom page fields inside merge().
|
|
68
85
|
* @returns The first defined value from children with the same origin.
|
|
69
86
|
*/
|
|
70
|
-
function
|
|
87
|
+
function collectCommandPageMetadata(current, docs, includeCustom = false) {
|
|
71
88
|
const next = getCommandDocMetadata(docs);
|
|
72
89
|
if (next == null) return current;
|
|
73
90
|
const origin = next.origin ?? "entry";
|
|
@@ -83,8 +100,19 @@ function collectCommandDocMetadata(current, docs, includeCustom = false) {
|
|
|
83
100
|
...footer == null ? {} : { footer }
|
|
84
101
|
}, origin);
|
|
85
102
|
}
|
|
103
|
+
/** Collects page fields and all selected command occurrences. @internal */
|
|
104
|
+
function collectCommandDocMetadata(current, docs, includeCustom = false, scope) {
|
|
105
|
+
const next = getCommandDocMetadata(docs);
|
|
106
|
+
const selected = getSelectedCommandUsage(next);
|
|
107
|
+
const result = collectCommandPageMetadata(current, docs, includeCustom);
|
|
108
|
+
const combined = [...getSelectedCommandUsage(current), ...scope == null ? selected : selected.map((id) => require_command_usage.scopeCommandUsageId(scope, id))];
|
|
109
|
+
return combined.length === 0 ? result : withSelectedCommandUsage(result, combined);
|
|
110
|
+
}
|
|
86
111
|
|
|
87
112
|
//#endregion
|
|
88
113
|
exports.collectCommandDocMetadata = collectCommandDocMetadata;
|
|
89
114
|
exports.getCommandDocMetadata = getCommandDocMetadata;
|
|
90
|
-
exports.
|
|
115
|
+
exports.getSelectedCommandUsage = getSelectedCommandUsage;
|
|
116
|
+
exports.scopeCommandDocMetadata = scopeCommandDocMetadata;
|
|
117
|
+
exports.withCommandDocMetadata = withCommandDocMetadata;
|
|
118
|
+
exports.withSelectedCommandUsage = withSelectedCommandUsage;
|
|
@@ -1,6 +1,24 @@
|
|
|
1
|
+
import { scopeCommandUsageId } from "./command-usage.js";
|
|
2
|
+
|
|
1
3
|
//#region src/internal/command-doc-metadata.ts
|
|
4
|
+
const selectedCommandUsage = /* @__PURE__ */ new WeakMap();
|
|
5
|
+
/** Reads command occurrences selected by final parser state. @internal */
|
|
6
|
+
function getSelectedCommandUsage(metadata) {
|
|
7
|
+
return metadata == null ? [] : selectedCommandUsage.get(metadata) ?? [];
|
|
8
|
+
}
|
|
9
|
+
/** Attaches selection without adding public metadata properties. @internal */
|
|
10
|
+
function withSelectedCommandUsage(metadata, selected) {
|
|
11
|
+
const result = withOrigin(metadata ?? {}, metadata?.origin ?? "entry");
|
|
12
|
+
selectedCommandUsage.set(result, selected);
|
|
13
|
+
return result;
|
|
14
|
+
}
|
|
15
|
+
/** Maps selected commands to the same child scope as its usage. @internal */
|
|
16
|
+
function scopeCommandDocMetadata(metadata, scope) {
|
|
17
|
+
return metadata == null ? void 0 : withSelectedCommandUsage(metadata, getSelectedCommandUsage(metadata).map((id) => scopeCommandUsageId(scope, id)));
|
|
18
|
+
}
|
|
2
19
|
function withOrigin(metadata, origin) {
|
|
3
20
|
const result = { ...metadata };
|
|
21
|
+
selectedCommandUsage.set(result, getSelectedCommandUsage(metadata));
|
|
4
22
|
Object.defineProperty(result, "origin", {
|
|
5
23
|
value: origin,
|
|
6
24
|
enumerable: false
|
|
@@ -22,11 +40,11 @@ function getCommandDocMetadata(docs) {
|
|
|
22
40
|
const customPage = origin === "entry" && (docs.brief !== carrier.brief || docs.footer !== carrier.footer);
|
|
23
41
|
const description = origin === "entry" && !customPage || docs.description === carrier.description ? carrier.metadata.description : docs.description;
|
|
24
42
|
const footer = docs.footer === carrier.footer ? carrier.metadata.footer : docs.footer;
|
|
25
|
-
return withOrigin({
|
|
43
|
+
return withSelectedCommandUsage(withOrigin({
|
|
26
44
|
...brief == null ? {} : { brief },
|
|
27
45
|
...description == null ? {} : { description },
|
|
28
46
|
...footer == null ? {} : { footer }
|
|
29
|
-
}, customPage ? "custom" : origin);
|
|
47
|
+
}, customPage ? "custom" : origin), getSelectedCommandUsage(carrier.metadata));
|
|
30
48
|
}
|
|
31
49
|
const metadata = docs[commandDocMetadata];
|
|
32
50
|
if (metadata != null) return metadata;
|
|
@@ -66,7 +84,7 @@ function withCommandDocMetadata(docs, metadata) {
|
|
|
66
84
|
* @param includeCustom Whether to retain custom page fields inside merge().
|
|
67
85
|
* @returns The first defined value from children with the same origin.
|
|
68
86
|
*/
|
|
69
|
-
function
|
|
87
|
+
function collectCommandPageMetadata(current, docs, includeCustom = false) {
|
|
70
88
|
const next = getCommandDocMetadata(docs);
|
|
71
89
|
if (next == null) return current;
|
|
72
90
|
const origin = next.origin ?? "entry";
|
|
@@ -82,6 +100,14 @@ function collectCommandDocMetadata(current, docs, includeCustom = false) {
|
|
|
82
100
|
...footer == null ? {} : { footer }
|
|
83
101
|
}, origin);
|
|
84
102
|
}
|
|
103
|
+
/** Collects page fields and all selected command occurrences. @internal */
|
|
104
|
+
function collectCommandDocMetadata(current, docs, includeCustom = false, scope) {
|
|
105
|
+
const next = getCommandDocMetadata(docs);
|
|
106
|
+
const selected = getSelectedCommandUsage(next);
|
|
107
|
+
const result = collectCommandPageMetadata(current, docs, includeCustom);
|
|
108
|
+
const combined = [...getSelectedCommandUsage(current), ...scope == null ? selected : selected.map((id) => scopeCommandUsageId(scope, id))];
|
|
109
|
+
return combined.length === 0 ? result : withSelectedCommandUsage(result, combined);
|
|
110
|
+
}
|
|
85
111
|
|
|
86
112
|
//#endregion
|
|
87
|
-
export { collectCommandDocMetadata, getCommandDocMetadata, withCommandDocMetadata };
|
|
113
|
+
export { collectCommandDocMetadata, getCommandDocMetadata, getSelectedCommandUsage, scopeCommandDocMetadata, withCommandDocMetadata, withSelectedCommandUsage };
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/internal/command-usage.ts
|
|
3
|
+
const commandUsageIds = /* @__PURE__ */ new WeakMap();
|
|
4
|
+
/** Associates a command usage leaf with its parser occurrence. @internal */
|
|
5
|
+
function identifyCommandUsage(term, id) {
|
|
6
|
+
commandUsageIds.set(term, id);
|
|
7
|
+
return term;
|
|
8
|
+
}
|
|
9
|
+
/** Reads a command usage leaf's private identity. @internal */
|
|
10
|
+
function getCommandUsageId(term) {
|
|
11
|
+
return commandUsageIds.get(term);
|
|
12
|
+
}
|
|
13
|
+
/** Preserves private identity when cloning a usage leaf. @internal */
|
|
14
|
+
function copyCommandUsageId(source, target) {
|
|
15
|
+
const id = commandUsageIds.get(source);
|
|
16
|
+
if (id != null) commandUsageIds.set(target, id);
|
|
17
|
+
return target;
|
|
18
|
+
}
|
|
19
|
+
/** Distinguishes repeated uses of one parser in different children. @internal */
|
|
20
|
+
function scopeCommandUsageId(scope, id) {
|
|
21
|
+
let scoped = scope.get(id);
|
|
22
|
+
if (scoped == null) {
|
|
23
|
+
scoped = {};
|
|
24
|
+
scope.set(id, scoped);
|
|
25
|
+
}
|
|
26
|
+
return scoped;
|
|
27
|
+
}
|
|
28
|
+
/** Copies usage with identities scoped to this child occurrence. @internal */
|
|
29
|
+
function scopeCommandUsage(usage, scope) {
|
|
30
|
+
return usage.map((term) => {
|
|
31
|
+
if (term.type === "command") {
|
|
32
|
+
const id = getCommandUsageId(term);
|
|
33
|
+
return id == null ? term : identifyCommandUsage({ ...term }, scopeCommandUsageId(scope, id));
|
|
34
|
+
}
|
|
35
|
+
if (term.type === "optional" || term.type === "multiple") return {
|
|
36
|
+
...term,
|
|
37
|
+
terms: scopeCommandUsage(term.terms, scope)
|
|
38
|
+
};
|
|
39
|
+
if (term.type === "exclusive") return {
|
|
40
|
+
...term,
|
|
41
|
+
terms: term.terms.map((branch) => scopeCommandUsage(branch, scope))
|
|
42
|
+
};
|
|
43
|
+
return term;
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
//#endregion
|
|
48
|
+
exports.copyCommandUsageId = copyCommandUsageId;
|
|
49
|
+
exports.getCommandUsageId = getCommandUsageId;
|
|
50
|
+
exports.identifyCommandUsage = identifyCommandUsage;
|
|
51
|
+
exports.scopeCommandUsage = scopeCommandUsage;
|
|
52
|
+
exports.scopeCommandUsageId = scopeCommandUsageId;
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
//#region src/internal/command-usage.ts
|
|
2
|
+
const commandUsageIds = /* @__PURE__ */ new WeakMap();
|
|
3
|
+
/** Associates a command usage leaf with its parser occurrence. @internal */
|
|
4
|
+
function identifyCommandUsage(term, id) {
|
|
5
|
+
commandUsageIds.set(term, id);
|
|
6
|
+
return term;
|
|
7
|
+
}
|
|
8
|
+
/** Reads a command usage leaf's private identity. @internal */
|
|
9
|
+
function getCommandUsageId(term) {
|
|
10
|
+
return commandUsageIds.get(term);
|
|
11
|
+
}
|
|
12
|
+
/** Preserves private identity when cloning a usage leaf. @internal */
|
|
13
|
+
function copyCommandUsageId(source, target) {
|
|
14
|
+
const id = commandUsageIds.get(source);
|
|
15
|
+
if (id != null) commandUsageIds.set(target, id);
|
|
16
|
+
return target;
|
|
17
|
+
}
|
|
18
|
+
/** Distinguishes repeated uses of one parser in different children. @internal */
|
|
19
|
+
function scopeCommandUsageId(scope, id) {
|
|
20
|
+
let scoped = scope.get(id);
|
|
21
|
+
if (scoped == null) {
|
|
22
|
+
scoped = {};
|
|
23
|
+
scope.set(id, scoped);
|
|
24
|
+
}
|
|
25
|
+
return scoped;
|
|
26
|
+
}
|
|
27
|
+
/** Copies usage with identities scoped to this child occurrence. @internal */
|
|
28
|
+
function scopeCommandUsage(usage, scope) {
|
|
29
|
+
return usage.map((term) => {
|
|
30
|
+
if (term.type === "command") {
|
|
31
|
+
const id = getCommandUsageId(term);
|
|
32
|
+
return id == null ? term : identifyCommandUsage({ ...term }, scopeCommandUsageId(scope, id));
|
|
33
|
+
}
|
|
34
|
+
if (term.type === "optional" || term.type === "multiple") return {
|
|
35
|
+
...term,
|
|
36
|
+
terms: scopeCommandUsage(term.terms, scope)
|
|
37
|
+
};
|
|
38
|
+
if (term.type === "exclusive") return {
|
|
39
|
+
...term,
|
|
40
|
+
terms: term.terms.map((branch) => scopeCommandUsage(branch, scope))
|
|
41
|
+
};
|
|
42
|
+
return term;
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
//#endregion
|
|
47
|
+
export { copyCommandUsageId, getCommandUsageId, identifyCommandUsage, scopeCommandUsage, scopeCommandUsageId };
|
package/dist/internal/parser.cjs
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
const require_annotations = require('./annotations.cjs');
|
|
2
2
|
const require_message = require('../message.cjs');
|
|
3
|
+
const require_command_usage = require('./command-usage.cjs');
|
|
4
|
+
const require_command_doc_metadata = require('./command-doc-metadata.cjs');
|
|
3
5
|
const require_usage = require('../usage.cjs');
|
|
4
6
|
const require_doc = require('../doc.cjs');
|
|
5
7
|
const require_mode_dispatch = require('./mode-dispatch.cjs');
|
|
@@ -527,20 +529,66 @@ function suggest(parser, args, options) {
|
|
|
527
529
|
*
|
|
528
530
|
* @param term The usage term to search in
|
|
529
531
|
* @param commandName The command name to find
|
|
532
|
+
* @param selectedCommandUsage The command occurrences selected by parser state
|
|
530
533
|
* @returns The expanded usage terms if found, null otherwise
|
|
531
534
|
*/
|
|
532
|
-
function findCommandInExclusive(term, commandName) {
|
|
535
|
+
function findCommandInExclusive(term, commandName, selectedCommandUsage) {
|
|
536
|
+
return findCommandInExclusiveMatching(term, commandName, selectedCommandUsage) ?? findCommandInExclusiveMatching(term, commandName, selectedCommandUsage, false);
|
|
537
|
+
}
|
|
538
|
+
function findCommandInExclusiveMatching(term, commandName, selectedCommandUsage, requireSelectedIdentity = true) {
|
|
533
539
|
if (term.type !== "exclusive") return null;
|
|
534
540
|
for (const termGroup of term.terms) {
|
|
535
541
|
const firstTerm = termGroup[0];
|
|
536
|
-
if (firstTerm?.type === "command" && firstTerm.name === commandName)
|
|
542
|
+
if (firstTerm?.type === "command" && firstTerm.name === commandName) {
|
|
543
|
+
const id = require_command_usage.getCommandUsageId(firstTerm);
|
|
544
|
+
if (id == null ? !requireSelectedIdentity : selectedCommandUsage.has(id)) return termGroup;
|
|
545
|
+
}
|
|
546
|
+
if (requireSelectedIdentity && (firstTerm?.type === "optional" || firstTerm?.type === "multiple") && hasSelectedLeadingCommand(firstTerm.terms, commandName, selectedCommandUsage)) return termGroup;
|
|
537
547
|
if (firstTerm?.type === "exclusive") {
|
|
538
|
-
const found =
|
|
548
|
+
const found = findCommandInExclusiveMatching(firstTerm, commandName, selectedCommandUsage, requireSelectedIdentity);
|
|
539
549
|
if (found) return [...found, ...termGroup.slice(1)];
|
|
540
550
|
}
|
|
541
551
|
}
|
|
542
552
|
return null;
|
|
543
553
|
}
|
|
554
|
+
function hasSelectedLeadingCommand(usage, commandName, selectedCommandUsage) {
|
|
555
|
+
const first = usage[0];
|
|
556
|
+
if (first?.type === "command") {
|
|
557
|
+
const id = require_command_usage.getCommandUsageId(first);
|
|
558
|
+
return first.name === commandName && id != null && selectedCommandUsage.has(id);
|
|
559
|
+
}
|
|
560
|
+
if (first?.type === "optional" || first?.type === "multiple") return hasSelectedLeadingCommand(first.terms, commandName, selectedCommandUsage);
|
|
561
|
+
return first?.type === "exclusive" && first.terms.some((branch) => hasSelectedLeadingCommand(branch, commandName, selectedCommandUsage));
|
|
562
|
+
}
|
|
563
|
+
function applyWrappedCommandUsageLine(term, commandName, selectedCommandUsage) {
|
|
564
|
+
const candidates = [];
|
|
565
|
+
const visit = (terms, rebuild) => {
|
|
566
|
+
const first = terms[0];
|
|
567
|
+
if (first?.type === "command") {
|
|
568
|
+
const id = require_command_usage.getCommandUsageId(first);
|
|
569
|
+
if (first.name === commandName && id != null && selectedCommandUsage.has(id)) candidates.push({
|
|
570
|
+
command: first,
|
|
571
|
+
tail: terms.slice(1),
|
|
572
|
+
replace: (tail$1) => rebuild([first, ...tail$1])
|
|
573
|
+
});
|
|
574
|
+
} else if (first?.type === "optional" || first?.type === "multiple") visit(first.terms, (children) => rebuild([{
|
|
575
|
+
...first,
|
|
576
|
+
terms: children
|
|
577
|
+
}, ...terms.slice(1)]));
|
|
578
|
+
else if (first?.type === "exclusive") first.terms.forEach((branch, index) => {
|
|
579
|
+
visit(branch, (children) => rebuild([{
|
|
580
|
+
...first,
|
|
581
|
+
terms: first.terms.map((other, otherIndex) => otherIndex === index ? children : other)
|
|
582
|
+
}, ...terms.slice(1)]));
|
|
583
|
+
});
|
|
584
|
+
};
|
|
585
|
+
visit([term], (terms) => terms[0]);
|
|
586
|
+
if (candidates.length !== 1) return term;
|
|
587
|
+
const { command, tail, replace } = candidates[0];
|
|
588
|
+
if (command.usageLine == null) return term;
|
|
589
|
+
const customUsageLine = typeof command.usageLine === "function" ? command.usageLine(require_usage.cloneUsage(tail)) : command.usageLine;
|
|
590
|
+
return replace(require_usage.normalizeUsage(customUsageLine));
|
|
591
|
+
}
|
|
544
592
|
/**
|
|
545
593
|
* Generates a documentation page for a synchronous parser.
|
|
546
594
|
*
|
|
@@ -651,10 +699,13 @@ async function getDocPageAsyncImpl(parser, args, options) {
|
|
|
651
699
|
*/
|
|
652
700
|
function buildDocPage(parser, context, args) {
|
|
653
701
|
let effectiveArgs = args;
|
|
654
|
-
let
|
|
702
|
+
let matchedCommandPath = context.exec?.commandPath ?? [];
|
|
703
|
+
const docs = parser.getDocFragments({
|
|
655
704
|
kind: "available",
|
|
656
705
|
state: context.state
|
|
657
706
|
}, void 0);
|
|
707
|
+
let selectedCommandUsage = require_command_doc_metadata.getSelectedCommandUsage(require_command_doc_metadata.getCommandDocMetadata(docs));
|
|
708
|
+
let { brief, description, fragments, footer } = docs;
|
|
658
709
|
if (args.length === 0 && Reflect.get(parser, Symbol.for("@optique/core/commandParser")) === true && fragments.length === 1 && fragments[0].type === "entry" && fragments[0].term.type === "command") {
|
|
659
710
|
const cmdName = fragments[0].term.name;
|
|
660
711
|
const matchedState = require_annotations.inheritAnnotations(context.state, ["matched", cmdName]);
|
|
@@ -663,7 +714,9 @@ function buildDocPage(parser, context, args) {
|
|
|
663
714
|
state: matchedState
|
|
664
715
|
}, void 0);
|
|
665
716
|
({brief, description, fragments, footer} = matched);
|
|
717
|
+
selectedCommandUsage = require_command_doc_metadata.getSelectedCommandUsage(require_command_doc_metadata.getCommandDocMetadata(matched));
|
|
666
718
|
effectiveArgs = [cmdName];
|
|
719
|
+
matchedCommandPath = [cmdName];
|
|
667
720
|
}
|
|
668
721
|
const buildingSections = [];
|
|
669
722
|
let untitledSection = null;
|
|
@@ -698,9 +751,15 @@ function buildDocPage(parser, context, args) {
|
|
|
698
751
|
}
|
|
699
752
|
}
|
|
700
753
|
const sections = buildingSections;
|
|
754
|
+
const selectedCommandUsageIds = new Set(selectedCommandUsage);
|
|
701
755
|
const usage = [...require_usage.normalizeUsage(parser.usage)];
|
|
702
756
|
const maybeApplyCommandUsageLine = (term, arg, isLastArg, usageIndex) => {
|
|
703
|
-
if (
|
|
757
|
+
if (!isLastArg) return;
|
|
758
|
+
if (term?.type === "optional" || term?.type === "multiple" || term?.type === "exclusive") {
|
|
759
|
+
usage[usageIndex] = applyWrappedCommandUsageLine(term, arg, selectedCommandUsageIds);
|
|
760
|
+
return;
|
|
761
|
+
}
|
|
762
|
+
if (term?.type !== "command" || term.name !== arg || term.usageLine == null) return;
|
|
704
763
|
const defaultUsageLine = require_usage.cloneUsage(usage.slice(usageIndex + 1));
|
|
705
764
|
const customUsageLine = typeof term.usageLine === "function" ? term.usageLine(defaultUsageLine) : term.usageLine;
|
|
706
765
|
const normalizedCustomUsageLine = require_usage.normalizeUsage(customUsageLine);
|
|
@@ -712,7 +771,7 @@ function buildDocPage(parser, context, args) {
|
|
|
712
771
|
if (i >= usage.length) break;
|
|
713
772
|
let term = usage[i];
|
|
714
773
|
if (term.type === "exclusive") {
|
|
715
|
-
const found = findCommandInExclusive(term, arg);
|
|
774
|
+
const found = findCommandInExclusive(term, arg, selectedCommandUsageIds);
|
|
716
775
|
if (found) {
|
|
717
776
|
usage.splice(i, 1, ...found);
|
|
718
777
|
term = usage[i];
|
|
@@ -731,13 +790,58 @@ function buildDocPage(parser, context, args) {
|
|
|
731
790
|
}
|
|
732
791
|
}
|
|
733
792
|
return {
|
|
734
|
-
usage,
|
|
793
|
+
usage: revealMatchedCommandUsage(usage, matchedCommandPath, selectedCommandUsageIds),
|
|
735
794
|
sections,
|
|
736
795
|
...brief != null && { brief: require_message.cloneMessage(brief) },
|
|
737
796
|
...description != null && { description: require_message.cloneMessage(description) },
|
|
738
797
|
...footer != null && { footer: require_message.cloneMessage(footer) }
|
|
739
798
|
};
|
|
740
799
|
}
|
|
800
|
+
function revealMatchedCommandUsage(usage, matchedCommandPath, selectedCommandUsage) {
|
|
801
|
+
return revealMatchedCommandUsageTerms(usage, matchedCommandPath, 0, selectedCommandUsage)[0];
|
|
802
|
+
}
|
|
803
|
+
function revealMatchedCommandUsageTerms(usage, matchedCommandPath, pathIndex, selectedCommandUsage, allowNameFallback = true) {
|
|
804
|
+
const terms = [];
|
|
805
|
+
let nextPathIndex = pathIndex;
|
|
806
|
+
let nameFallbackEnabled = allowNameFallback;
|
|
807
|
+
for (let index = 0; index < usage.length; index++) {
|
|
808
|
+
const term = usage[index];
|
|
809
|
+
if (term.type === "command") {
|
|
810
|
+
const id = require_command_usage.getCommandUsageId(term);
|
|
811
|
+
if (id != null) {
|
|
812
|
+
if (selectedCommandUsage.has(id)) {
|
|
813
|
+
const { hidden: _hidden$1,...revealed$1 } = term;
|
|
814
|
+
terms.push(revealed$1);
|
|
815
|
+
if (term.name === matchedCommandPath[nextPathIndex]) nextPathIndex++;
|
|
816
|
+
} else terms.push(term);
|
|
817
|
+
continue;
|
|
818
|
+
}
|
|
819
|
+
if (!nameFallbackEnabled || term.name !== matchedCommandPath[nextPathIndex]) {
|
|
820
|
+
nameFallbackEnabled = false;
|
|
821
|
+
terms.push(term);
|
|
822
|
+
continue;
|
|
823
|
+
}
|
|
824
|
+
const { hidden: _hidden,...revealed } = term;
|
|
825
|
+
terms.push(revealed);
|
|
826
|
+
nextPathIndex++;
|
|
827
|
+
} else if (term.type === "optional" || term.type === "multiple") {
|
|
828
|
+
const [nested, afterTerm] = revealMatchedCommandUsageTerms(term.terms, matchedCommandPath, nextPathIndex, selectedCommandUsage, nameFallbackEnabled);
|
|
829
|
+
terms.push({
|
|
830
|
+
...term,
|
|
831
|
+
terms: nested
|
|
832
|
+
});
|
|
833
|
+
nextPathIndex = afterTerm;
|
|
834
|
+
} else if (term.type === "exclusive") {
|
|
835
|
+
const branches = term.terms.map((branch) => revealMatchedCommandUsageTerms(branch, matchedCommandPath, nextPathIndex, selectedCommandUsage, nameFallbackEnabled));
|
|
836
|
+
terms.push({
|
|
837
|
+
...term,
|
|
838
|
+
terms: branches.map(([branch]) => branch)
|
|
839
|
+
});
|
|
840
|
+
nextPathIndex = Math.max(nextPathIndex, ...branches.map(([, afterBranch]) => afterBranch));
|
|
841
|
+
} else terms.push(term);
|
|
842
|
+
}
|
|
843
|
+
return [terms, nextPathIndex];
|
|
844
|
+
}
|
|
741
845
|
|
|
742
846
|
//#endregion
|
|
743
847
|
exports.annotationWrapperRequiresSourceBindingKey = annotationWrapperRequiresSourceBindingKey;
|