@optique/core 1.4.0-dev.2708 → 1.4.0-dev.2711

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.
@@ -0,0 +1,355 @@
1
+ //#region src/internal/empty-input.ts
2
+ /**
3
+ * The state of an empty step that leaves its input state unchanged.
4
+ * @internal
5
+ */
6
+ const UNCHANGED = Object.freeze({ unchanged: true });
7
+ /**
8
+ * Returns the state that an empty step produces from the given initial
9
+ * state.
10
+ * @param next The step's resulting state.
11
+ * @param initialState The state the step started from.
12
+ * @returns The resulting state.
13
+ * @internal
14
+ */
15
+ function resolveEmptyStepState(next, initialState) {
16
+ return "unchanged" in next ? initialState : next.state(initialState);
17
+ }
18
+ const emptyInputFactsKey = Symbol("emptyInputFacts");
19
+ const UNKNOWN = Object.freeze({});
20
+ /**
21
+ * Attaches empty-input facts to a parser. The facts are stored as a
22
+ * non-enumerable property, so object spread does not copy them, and they
23
+ * are bound to the parser's current `parse`, `complete`, and
24
+ * `initialState`, so replacing any of those invalidates them.
25
+ * @param parser The parser to annotate.
26
+ * @param facts The facts to attach.
27
+ * @internal
28
+ */
29
+ function defineEmptyInputFacts(parser, facts) {
30
+ if (Object.keys(facts).length < 1) return;
31
+ Object.defineProperty(parser, emptyInputFactsKey, {
32
+ value: {
33
+ facts,
34
+ parse: parser.parse,
35
+ complete: parser.complete,
36
+ ...Object.getOwnPropertyDescriptor(parser, "initialState")?.get == null ? { initialState: { value: parser.initialState } } : {}
37
+ },
38
+ configurable: true,
39
+ enumerable: false,
40
+ writable: false
41
+ });
42
+ }
43
+ /**
44
+ * Returns the empty-input facts of a parser, or an empty object when the
45
+ * facts are unknown.
46
+ * @param parser The parser to inspect.
47
+ * @returns The facts known for the parser.
48
+ * @internal
49
+ */
50
+ function getEmptyInputFacts(parser) {
51
+ const stored = parser[emptyInputFactsKey];
52
+ if (stored == null || stored.parse !== parser.parse || stored.complete !== parser.complete || stored.initialState != null && stored.initialState.value !== parser.initialState) return UNKNOWN;
53
+ return stored.facts;
54
+ }
55
+ /**
56
+ * Returns whether `parse(parser, [])` succeeds, when that is known from the
57
+ * facts.
58
+ * @param facts The parser's facts.
59
+ * @returns `true` or `false` when known, `undefined` otherwise.
60
+ * @internal
61
+ */
62
+ function acceptsEmptyInput(facts) {
63
+ if (facts.step === "failure") return false;
64
+ if (facts.step == null) return void 0;
65
+ return facts.afterStep;
66
+ }
67
+ /**
68
+ * Returns the facts of a parser that fails its empty step and whose
69
+ * completion from the initial state has the given outcome.
70
+ * @param fromInitial Whether completion from the initial state succeeds.
71
+ * @returns The facts.
72
+ * @internal
73
+ */
74
+ function failingStepFacts(fromInitial) {
75
+ return {
76
+ step: "failure",
77
+ fromInitial
78
+ };
79
+ }
80
+ /**
81
+ * Returns whether `complete()` succeeds after the parser was offered an
82
+ * empty step whose state change was kept (on success) or discarded (on
83
+ * failure).
84
+ * @param facts The parser's facts.
85
+ * @returns The outcome, or `undefined` when unknown.
86
+ * @internal
87
+ */
88
+ function completionAfterOptionalStep(facts) {
89
+ if (facts.step === "failure") return facts.fromInitial;
90
+ if (facts.step == null) return void 0;
91
+ return facts.afterStep;
92
+ }
93
+ /**
94
+ * The state of a wrapper that stores its child's resulting state as the
95
+ * only element of a fresh array.
96
+ */
97
+ function wrapInFreshArray(child, childInitialState) {
98
+ return { state: () => [resolveEmptyStepState(child, childInitialState)] };
99
+ }
100
+ /**
101
+ * Computes the facts of `optional(p)` and `withDefault(p, value)` when
102
+ * completing the wrapper from its initial state is known to succeed.
103
+ * @param child The facts of the wrapped parser.
104
+ * @param childInitialState The wrapped parser's initial state.
105
+ * @param fromInitial Whether completing the wrapper from its initial state
106
+ * succeeds, or `undefined` when unknown.
107
+ * @returns The facts of the wrapper.
108
+ * @internal
109
+ */
110
+ function optionalLikeFacts(child, childInitialState, fromInitial) {
111
+ if (child.step === "failure") return {
112
+ step: "success",
113
+ next: UNCHANGED,
114
+ afterStep: fromInitial,
115
+ fromInitial
116
+ };
117
+ if (child.step == null) return { fromInitial };
118
+ return {
119
+ step: child.step,
120
+ ...child.next == null ? {} : { next: wrapInFreshArray(child.next, childInitialState) },
121
+ afterStep: child.afterStep,
122
+ fromInitial
123
+ };
124
+ }
125
+ /**
126
+ * Computes the facts of `multiple(p, { min })` without a `max` bound.
127
+ * @param child The facts of the repeated parser.
128
+ * @param retainsItem Whether a successful empty step of the child is kept
129
+ * as a repetition item, or `undefined` when unknown.
130
+ * @param childInitialState The repeated parser's initial state.
131
+ * @param min The minimum number of items.
132
+ * @returns The facts of the repetition.
133
+ * @internal
134
+ */
135
+ function multipleFacts(child, retainsItem, childInitialState, min) {
136
+ const fromInitial = min < 1;
137
+ if (child.step === "failure") return min < 1 ? {
138
+ step: "success",
139
+ next: UNCHANGED,
140
+ afterStep: true,
141
+ fromInitial
142
+ } : {
143
+ step: "failure",
144
+ fromInitial
145
+ };
146
+ if (child.step == null || retainsItem == null) return { fromInitial };
147
+ if (!retainsItem) return {
148
+ step: "success",
149
+ next: UNCHANGED,
150
+ afterStep: min < 1,
151
+ fromInitial
152
+ };
153
+ return {
154
+ step: child.step,
155
+ ...child.next == null ? {} : { next: wrapInFreshArray(child.next, childInitialState) },
156
+ afterStep: child.afterStep == null ? void 0 : min <= 1 && child.afterStep,
157
+ fromInitial
158
+ };
159
+ }
160
+ function selectZeroInputCandidate(branches) {
161
+ let index = -1;
162
+ let count = 0;
163
+ for (let i = 0; i < branches.length; i++) {
164
+ const branch = branches[i];
165
+ if (branch.matchesTokens) continue;
166
+ if (branch.facts.step == null) return void 0;
167
+ if (branch.facts.step !== "success") continue;
168
+ if (index < 0) index = i;
169
+ count++;
170
+ }
171
+ return count === 1 ? { index } : null;
172
+ }
173
+ /**
174
+ * Computes the completion of `or()` and `longestMatch()` from their initial
175
+ * state, which selects the unique zero-input candidate among the branches
176
+ * that cannot match tokens.
177
+ */
178
+ function exclusiveFromInitial(branches) {
179
+ const candidate = selectZeroInputCandidate(branches);
180
+ if (candidate === void 0) return void 0;
181
+ if (candidate === null) return false;
182
+ return branches[candidate.index].facts.afterStep;
183
+ }
184
+ /**
185
+ * An exemplar of an exclusive combinator's state after it selected a branch.
186
+ * It is a fresh, non-nullish value, which is all that repetition item checks
187
+ * look at.
188
+ */
189
+ function exclusiveNextState(index) {
190
+ return { state: () => [index, {
191
+ success: true,
192
+ consumed: []
193
+ }] };
194
+ }
195
+ /**
196
+ * Computes the facts of `or()`.
197
+ * @param branches The branches of the exclusive combinator.
198
+ * @returns The facts of the combinator.
199
+ * @internal
200
+ */
201
+ function orFacts(branches) {
202
+ const candidate = selectZeroInputCandidate(branches);
203
+ if (candidate === void 0) return {};
204
+ const fromInitial = exclusiveFromInitial(branches);
205
+ if (candidate === null) return {
206
+ step: "failure",
207
+ fromInitial
208
+ };
209
+ return {
210
+ step: "success",
211
+ next: exclusiveNextState(candidate.index),
212
+ afterStep: branches[candidate.index].facts.afterStep,
213
+ fromInitial
214
+ };
215
+ }
216
+ /**
217
+ * Computes the facts of `longestMatch()`, which selects the first branch
218
+ * whose empty step succeeds, preferring a definitive success over a
219
+ * provisional one.
220
+ * @param branches The branches of the exclusive combinator.
221
+ * @returns The facts of the combinator.
222
+ * @internal
223
+ */
224
+ function longestMatchFacts(branches) {
225
+ const fromInitial = exclusiveFromInitial(branches);
226
+ let best = null;
227
+ for (let i = 0; i < branches.length; i++) {
228
+ const facts = branches[i].facts;
229
+ if (facts.step == null) return { fromInitial };
230
+ if (facts.step === "failure") continue;
231
+ if (best == null || best.facts.step === "provisional" && facts.step === "success") best = {
232
+ index: i,
233
+ facts
234
+ };
235
+ if (best.facts.step === "success") break;
236
+ }
237
+ if (best == null) return {
238
+ step: "failure",
239
+ fromInitial
240
+ };
241
+ return {
242
+ step: best.facts.step,
243
+ next: exclusiveNextState(best.index),
244
+ afterStep: best.facts.afterStep,
245
+ fromInitial
246
+ };
247
+ }
248
+ function all(values) {
249
+ let unknown = false;
250
+ for (const value of values) {
251
+ if (value === false) return false;
252
+ if (value == null) unknown = true;
253
+ }
254
+ return unknown ? void 0 : true;
255
+ }
256
+ /**
257
+ * Computes the facts of `tuple()`. On an empty buffer `tuple()` offers
258
+ * each child one zero-token step, stores the state of every child whose
259
+ * step succeeded in a fresh state array, and skips the rest.
260
+ * @param children The children in declaration order.
261
+ * @returns The facts of the tuple.
262
+ * @internal
263
+ */
264
+ function tupleFacts(children) {
265
+ const fromInitial = all(children.map((c) => c.facts.fromInitial));
266
+ if (children.some((c) => c.facts.step == null)) return {
267
+ step: "success",
268
+ fromInitial
269
+ };
270
+ const afterStep = all(children.map((c) => completionAfterOptionalStep(c.facts)));
271
+ let next;
272
+ if (children.every((c) => c.facts.step === "failure")) next = UNCHANGED;
273
+ else if (children.every((c) => c.facts.step === "failure" || c.facts.next != null)) {
274
+ const steps = children.map((c) => c.facts.step === "failure" ? void 0 : c.facts.next);
275
+ next = { state: (initialState) => {
276
+ const slots = Array.isArray(initialState) ? initialState : [];
277
+ return steps.map((step, i) => step == null ? slots[i] : resolveEmptyStepState(step, slots[i]));
278
+ } };
279
+ }
280
+ return {
281
+ step: "success",
282
+ ...next == null ? {} : { next },
283
+ afterStep,
284
+ fromInitial
285
+ };
286
+ }
287
+ /**
288
+ * The state of `object()` after an empty step: a committed field state makes
289
+ * it store a fresh record with the other fields unchanged.
290
+ */
291
+ function objectNextState(changed) {
292
+ if (changed.length < 1) return UNCHANGED;
293
+ return { state: (initialState) => {
294
+ const record = initialState != null && typeof initialState === "object" ? { ...initialState } : {};
295
+ for (const [key, next] of changed) record[key] = resolveEmptyStepState(next, record[key]);
296
+ return record;
297
+ } };
298
+ }
299
+ /**
300
+ * Computes the facts of a synchronous `object()`. On an empty buffer
301
+ * `object()` offers each field that cannot match tokens one zero-token
302
+ * step, keeps the resulting state when it changed, and then succeeds only
303
+ * if every field can complete.
304
+ * @param children The fields.
305
+ * @param keys The field keys, parallel to `children`.
306
+ * @returns The facts of the object.
307
+ * @internal
308
+ */
309
+ function objectFacts(children, keys) {
310
+ const fromInitial = all(children.map((c) => c.facts.fromInitial));
311
+ const outcomes = [];
312
+ const changed = [];
313
+ let nextKnown = true;
314
+ for (const [i, child] of children.entries()) {
315
+ const { step, next } = child.facts;
316
+ if (child.matchesTokens || step === "failure") outcomes.push(child.facts.fromInitial);
317
+ else if (step == null) {
318
+ outcomes.push(void 0);
319
+ nextKnown = false;
320
+ } else if (next == null) {
321
+ nextKnown = false;
322
+ outcomes.push(child.facts.afterStep === child.facts.fromInitial ? child.facts.afterStep : void 0);
323
+ } else if ("unchanged" in next) outcomes.push(child.facts.fromInitial);
324
+ else {
325
+ changed.push([keys[i], next]);
326
+ outcomes.push(child.facts.afterStep);
327
+ }
328
+ }
329
+ const accepted = all(outcomes);
330
+ if (accepted == null) return { fromInitial };
331
+ if (!accepted) return {
332
+ step: "failure",
333
+ fromInitial
334
+ };
335
+ return {
336
+ step: "success",
337
+ ...nextKnown ? { next: objectNextState(changed) } : {},
338
+ afterStep: true,
339
+ fromInitial
340
+ };
341
+ }
342
+ /**
343
+ * Attaches empty-input facts to a parser and returns the parser.
344
+ * @param parser The parser to annotate.
345
+ * @param facts The facts to attach.
346
+ * @returns The same parser.
347
+ * @internal
348
+ */
349
+ function withEmptyInputFacts(parser, facts) {
350
+ defineEmptyInputFacts(parser, facts);
351
+ return parser;
352
+ }
353
+
354
+ //#endregion
355
+ export { UNCHANGED, acceptsEmptyInput, defineEmptyInputFacts, failingStepFacts, getEmptyInputFacts, longestMatchFacts, multipleFacts, objectFacts, optionalLikeFacts, orFacts, resolveEmptyStepState, tupleFacts, withEmptyInputFacts };
@@ -0,0 +1,248 @@
1
+
2
+ //#region src/internal/usage.ts
3
+ const EMPTY = {
4
+ visible: false,
5
+ omissible: true,
6
+ hidesRequired: false
7
+ };
8
+ function isHiddenLeaf(term, isHidden) {
9
+ return (term.type === "argument" || term.type === "option" || term.type === "command" || term.type === "passthrough") && isHidden(term.hidden);
10
+ }
11
+ function isDegenerateLeaf(term) {
12
+ return term.type === "option" && term.names.length === 0 || term.type === "command" && term.name === "" || term.type === "argument" && term.metavar.length === 0 || term.type === "literal" && term.value === "";
13
+ }
14
+ function projectTerms(terms, isHidden) {
15
+ let visible = false;
16
+ let omissible = true;
17
+ let hidesRequired = false;
18
+ for (const term of terms) {
19
+ const projection = projectTerm(term, isHidden);
20
+ hidesRequired ||= projection.hidesRequired;
21
+ if (!projection.visible) continue;
22
+ visible = true;
23
+ omissible &&= projection.omissible;
24
+ }
25
+ return {
26
+ visible,
27
+ omissible,
28
+ hidesRequired
29
+ };
30
+ }
31
+ /**
32
+ * Projects an alternative of an exclusive term. Display filtering drops an
33
+ * alternative whose leading command is hidden as a whole, trailing terms
34
+ * included.
35
+ */
36
+ function projectBranch(branch, isHidden) {
37
+ const first = branch[0];
38
+ if (first?.type === "command" && isHidden(first.hidden)) return {
39
+ ...EMPTY,
40
+ hidesRequired: true
41
+ };
42
+ return projectTerms(branch, isHidden);
43
+ }
44
+ function projectTerm(term, isHidden) {
45
+ if (isHiddenLeaf(term, isHidden)) return {
46
+ ...EMPTY,
47
+ hidesRequired: term.type !== "passthrough"
48
+ };
49
+ if (isDegenerateLeaf(term)) return EMPTY;
50
+ switch (term.type) {
51
+ case "argument":
52
+ case "option":
53
+ case "command":
54
+ case "literal": return {
55
+ visible: true,
56
+ omissible: false,
57
+ hidesRequired: false
58
+ };
59
+ case "passthrough":
60
+ case "ellipsis": return {
61
+ visible: true,
62
+ omissible: true,
63
+ hidesRequired: false
64
+ };
65
+ case "optional": {
66
+ const inner = projectTerms(term.terms, isHidden);
67
+ return {
68
+ ...inner,
69
+ omissible: true,
70
+ hidesRequired: false
71
+ };
72
+ }
73
+ case "sequence": return projectTerms(term.terms, isHidden);
74
+ case "multiple": {
75
+ const inner = projectTerms(term.terms, isHidden);
76
+ return {
77
+ ...inner,
78
+ omissible: term.min < 1 || inner.omissible,
79
+ hidesRequired: term.min > 0 && inner.hidesRequired
80
+ };
81
+ }
82
+ case "exclusive": {
83
+ let visible = false;
84
+ let omissible = false;
85
+ let hidesRequired = false;
86
+ for (const branch of term.terms) {
87
+ const projection = projectBranch(branch, isHidden);
88
+ hidesRequired ||= projection.hidesRequired;
89
+ if (!projection.visible) continue;
90
+ visible = true;
91
+ omissible ||= projection.omissible;
92
+ }
93
+ return {
94
+ visible,
95
+ omissible,
96
+ hidesRequired
97
+ };
98
+ }
99
+ }
100
+ }
101
+ /**
102
+ * Returns the terms `X` when the visible part of `terms` is exactly
103
+ * `optional(X)`, possibly nested, with a required `X`, and no required term
104
+ * was hidden.
105
+ */
106
+ function unwrapSingleOptional(terms, isHidden) {
107
+ if (projectTerms(terms, isHidden).hidesRequired) return void 0;
108
+ const visible = terms.filter((t) => projectTerm(t, isHidden).visible);
109
+ if (visible.length !== 1) return void 0;
110
+ const [only] = visible;
111
+ if (only.type !== "optional") return void 0;
112
+ const inner = projectTerms(only.terms, isHidden);
113
+ if (!inner.visible || inner.hidesRequired) return void 0;
114
+ if (inner.omissible) return unwrapSingleOptional(only.terms, isHidden);
115
+ return only.terms;
116
+ }
117
+ function resolveTerms(terms, isHidden) {
118
+ return terms.map((term) => resolveTerm(term, isHidden));
119
+ }
120
+ function resolveTerm(term, isHidden) {
121
+ switch (term.type) {
122
+ case "optional": return {
123
+ type: "optional",
124
+ terms: resolveTerms(term.terms, isHidden)
125
+ };
126
+ case "sequence": return {
127
+ type: "sequence",
128
+ terms: resolveTerms(term.terms, isHidden)
129
+ };
130
+ case "multiple": {
131
+ const terms = resolveTerms(term.terms, isHidden);
132
+ const resolved = {
133
+ type: "multiple",
134
+ terms,
135
+ min: term.min
136
+ };
137
+ if (term.acceptsEmpty == null) return resolved;
138
+ const projection = projectTerm(resolved, isHidden);
139
+ if (!projection.visible || projection.omissible === term.acceptsEmpty) return resolved;
140
+ if (term.acceptsEmpty) return {
141
+ type: "optional",
142
+ terms: [resolved]
143
+ };
144
+ if (term.min < 1) return resolved;
145
+ const required = unwrapSingleOptional(terms, isHidden);
146
+ return required == null ? resolved : {
147
+ ...resolved,
148
+ terms: required
149
+ };
150
+ }
151
+ case "exclusive": {
152
+ const branches = term.terms.map((branch) => resolveTerms(branch, isHidden));
153
+ const resolved = {
154
+ type: "exclusive",
155
+ terms: branches
156
+ };
157
+ if (term.acceptsEmpty == null) return resolved;
158
+ const projection = projectTerm(resolved, isHidden);
159
+ if (!projection.visible || projection.omissible === term.acceptsEmpty) return resolved;
160
+ if (term.acceptsEmpty) {
161
+ const visible = branches.filter((branch) => projectBranch(branch, isHidden).visible);
162
+ return {
163
+ type: "optional",
164
+ terms: visible.length === 1 ? visible[0] : [{
165
+ type: "exclusive",
166
+ terms: visible
167
+ }]
168
+ };
169
+ }
170
+ const required = [];
171
+ for (const branch of branches) {
172
+ const branchProjection = projectBranch(branch, isHidden);
173
+ if (!branchProjection.visible || !branchProjection.omissible) {
174
+ required.push(branch);
175
+ continue;
176
+ }
177
+ const unwrapped = unwrapSingleOptional(branch, isHidden);
178
+ if (unwrapped == null) return resolved;
179
+ required.push(unwrapped);
180
+ }
181
+ return {
182
+ type: "exclusive",
183
+ terms: required
184
+ };
185
+ }
186
+ default: return term;
187
+ }
188
+ }
189
+ /**
190
+ * Rewrites a usage so that plain synopsis notation reflects the recorded
191
+ * empty-input behavior of its groups.
192
+ *
193
+ * Each `exclusive` or `multiple` term that records `acceptsEmpty` is drawn
194
+ * as omissible exactly when its parser accepts an empty argument list,
195
+ * using only exact local rewrites: a group that should be omissible is
196
+ * wrapped in an optional term, and a group that should be required has
197
+ * `optional(X)` alternatives or repeated items replaced by `X`. A group
198
+ * whose visible terms cannot be drawn that way keeps its declared notation.
199
+ * Enclosing groups are resolved after their children, so their constraints
200
+ * take precedence.
201
+ *
202
+ * The result records no `acceptsEmpty` fields, so resolving it again
203
+ * returns an equal usage. Hidden terms are kept; callers filter them for
204
+ * display afterwards.
205
+ * @param usage The usage to resolve.
206
+ * @param isHidden Reports whether a term is hidden in the rendering context.
207
+ * @returns The resolved usage.
208
+ * @internal
209
+ */
210
+ function resolveUsageForDisplay(usage, isHidden) {
211
+ return resolveTerms(usage, isHidden);
212
+ }
213
+ /**
214
+ * Removes every `acceptsEmpty` record from a usage without changing how it
215
+ * is drawn otherwise.
216
+ * @param usage The usage to copy.
217
+ * @returns The usage without records.
218
+ * @internal
219
+ */
220
+ function forgetEmptyInput(usage) {
221
+ return usage.map(forgetEmptyInputTerm);
222
+ }
223
+ function forgetEmptyInputTerm(term) {
224
+ switch (term.type) {
225
+ case "optional": return {
226
+ type: "optional",
227
+ terms: forgetEmptyInput(term.terms)
228
+ };
229
+ case "sequence": return {
230
+ type: "sequence",
231
+ terms: forgetEmptyInput(term.terms)
232
+ };
233
+ case "multiple": return {
234
+ type: "multiple",
235
+ terms: forgetEmptyInput(term.terms),
236
+ min: term.min
237
+ };
238
+ case "exclusive": return {
239
+ type: "exclusive",
240
+ terms: term.terms.map(forgetEmptyInput)
241
+ };
242
+ default: return term;
243
+ }
244
+ }
245
+
246
+ //#endregion
247
+ exports.forgetEmptyInput = forgetEmptyInput;
248
+ exports.resolveUsageForDisplay = resolveUsageForDisplay;
@@ -0,0 +1,42 @@
1
+ import { HiddenVisibility, Usage } from "../usage.cjs";
2
+
3
+ //#region src/internal/usage.d.ts
4
+
5
+ /**
6
+ * Reports whether a term with the given visibility is hidden in the
7
+ * rendering context.
8
+ * @internal
9
+ */
10
+ type HiddenPredicate = (hidden?: HiddenVisibility) => boolean;
11
+ /**
12
+ * Rewrites a usage so that plain synopsis notation reflects the recorded
13
+ * empty-input behavior of its groups.
14
+ *
15
+ * Each `exclusive` or `multiple` term that records `acceptsEmpty` is drawn
16
+ * as omissible exactly when its parser accepts an empty argument list,
17
+ * using only exact local rewrites: a group that should be omissible is
18
+ * wrapped in an optional term, and a group that should be required has
19
+ * `optional(X)` alternatives or repeated items replaced by `X`. A group
20
+ * whose visible terms cannot be drawn that way keeps its declared notation.
21
+ * Enclosing groups are resolved after their children, so their constraints
22
+ * take precedence.
23
+ *
24
+ * The result records no `acceptsEmpty` fields, so resolving it again
25
+ * returns an equal usage. Hidden terms are kept; callers filter them for
26
+ * display afterwards.
27
+ * @param usage The usage to resolve.
28
+ * @param isHidden Reports whether a term is hidden in the rendering context.
29
+ * @returns The resolved usage.
30
+ * @internal
31
+ */
32
+ declare function resolveUsageForDisplay(usage: Usage, isHidden: HiddenPredicate): Usage;
33
+ /**
34
+ * Removes every `acceptsEmpty` record from a usage without changing how it
35
+ * is drawn otherwise.
36
+ * @param usage The usage to copy.
37
+ * @returns The usage without records.
38
+ * @internal
39
+ */
40
+ declare function forgetEmptyInput(usage: Usage): Usage;
41
+ //#endregion
42
+ export { HiddenPredicate, forgetEmptyInput, resolveUsageForDisplay };
@@ -0,0 +1,42 @@
1
+ import { HiddenVisibility, Usage } from "../usage.js";
2
+
3
+ //#region src/internal/usage.d.ts
4
+
5
+ /**
6
+ * Reports whether a term with the given visibility is hidden in the
7
+ * rendering context.
8
+ * @internal
9
+ */
10
+ type HiddenPredicate = (hidden?: HiddenVisibility) => boolean;
11
+ /**
12
+ * Rewrites a usage so that plain synopsis notation reflects the recorded
13
+ * empty-input behavior of its groups.
14
+ *
15
+ * Each `exclusive` or `multiple` term that records `acceptsEmpty` is drawn
16
+ * as omissible exactly when its parser accepts an empty argument list,
17
+ * using only exact local rewrites: a group that should be omissible is
18
+ * wrapped in an optional term, and a group that should be required has
19
+ * `optional(X)` alternatives or repeated items replaced by `X`. A group
20
+ * whose visible terms cannot be drawn that way keeps its declared notation.
21
+ * Enclosing groups are resolved after their children, so their constraints
22
+ * take precedence.
23
+ *
24
+ * The result records no `acceptsEmpty` fields, so resolving it again
25
+ * returns an equal usage. Hidden terms are kept; callers filter them for
26
+ * display afterwards.
27
+ * @param usage The usage to resolve.
28
+ * @param isHidden Reports whether a term is hidden in the rendering context.
29
+ * @returns The resolved usage.
30
+ * @internal
31
+ */
32
+ declare function resolveUsageForDisplay(usage: Usage, isHidden: HiddenPredicate): Usage;
33
+ /**
34
+ * Removes every `acceptsEmpty` record from a usage without changing how it
35
+ * is drawn otherwise.
36
+ * @param usage The usage to copy.
37
+ * @returns The usage without records.
38
+ * @internal
39
+ */
40
+ declare function forgetEmptyInput(usage: Usage): Usage;
41
+ //#endregion
42
+ export { HiddenPredicate, forgetEmptyInput, resolveUsageForDisplay };