@optique/core 1.4.0-dev.2683 → 1.4.0-dev.2709
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 +43 -16
- package/dist/constructs.js +43 -16
- package/dist/doc.cjs +11 -3
- package/dist/doc.js +12 -4
- package/dist/internal/empty-input.cjs +368 -0
- package/dist/internal/empty-input.js +355 -0
- package/dist/internal/usage.cjs +235 -0
- package/dist/internal/usage.d.cts +42 -0
- package/dist/internal/usage.d.ts +42 -0
- package/dist/internal/usage.js +233 -0
- package/dist/message.cjs +32 -5
- package/dist/message.js +33 -6
- package/dist/modifiers.cjs +43 -6
- package/dist/modifiers.js +43 -6
- package/dist/primitives.cjs +19 -4
- package/dist/primitives.js +19 -4
- package/dist/terminal-internal.cjs +4 -1
- package/dist/terminal-internal.d.cts +5 -0
- package/dist/terminal-internal.d.ts +5 -0
- package/dist/terminal-internal.js +4 -1
- package/dist/terminal-separators.cjs +13 -0
- package/dist/terminal-separators.js +12 -0
- package/dist/terminal.cjs +4 -1
- package/dist/terminal.js +5 -1
- package/dist/text-layout.cjs +14 -1
- package/dist/text-layout.d.cts +8 -1
- package/dist/text-layout.d.ts +8 -1
- package/dist/text-layout.js +13 -1
- package/dist/usage.cjs +41 -13
- package/dist/usage.d.cts +20 -0
- package/dist/usage.d.ts +20 -0
- package/dist/usage.js +41 -13
- package/package.json +10 -2
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_empty_input = require('./internal/empty-input.cjs');
|
|
9
10
|
const require_execution_context = require('./execution-context.cjs');
|
|
10
11
|
const require_usage_internals = require('./usage-internals.cjs');
|
|
11
12
|
const require_short_option = require('./short-option.cjs');
|
|
@@ -174,6 +175,12 @@ function constant(value) {
|
|
|
174
175
|
}
|
|
175
176
|
};
|
|
176
177
|
require_passthrough.defineKnownCompletion(result, (state) => require_annotation_state.normalizeInjectedAnnotationState(state));
|
|
178
|
+
require_empty_input.defineEmptyInputFacts(result, {
|
|
179
|
+
step: "success",
|
|
180
|
+
next: require_empty_input.UNCHANGED,
|
|
181
|
+
afterStep: true,
|
|
182
|
+
fromInitial: true
|
|
183
|
+
});
|
|
177
184
|
Object.defineProperty(result, "placeholder", {
|
|
178
185
|
value,
|
|
179
186
|
configurable: true,
|
|
@@ -202,7 +209,7 @@ function constant(value) {
|
|
|
202
209
|
* @since 1.0.0
|
|
203
210
|
*/
|
|
204
211
|
function fail() {
|
|
205
|
-
|
|
212
|
+
const result = {
|
|
206
213
|
$valueType: [],
|
|
207
214
|
$stateType: [],
|
|
208
215
|
mode: "sync",
|
|
@@ -236,7 +243,9 @@ function fail() {
|
|
|
236
243
|
sourceOnly: true
|
|
237
244
|
};
|
|
238
245
|
}
|
|
239
|
-
}
|
|
246
|
+
};
|
|
247
|
+
require_empty_input.defineEmptyInputFacts(result, require_empty_input.failingStepFacts(false));
|
|
248
|
+
return require_modifiers.fluent(result);
|
|
240
249
|
}
|
|
241
250
|
/**
|
|
242
251
|
* Internal helper to get suggestions from a value parser, using dependency values
|
|
@@ -794,6 +803,7 @@ function option(...args) {
|
|
|
794
803
|
configurable: true,
|
|
795
804
|
enumerable: false
|
|
796
805
|
});
|
|
806
|
+
else require_empty_input.defineEmptyInputFacts(result, require_empty_input.failingStepFacts(valueParser == null));
|
|
797
807
|
return require_modifiers.fluent(result);
|
|
798
808
|
}
|
|
799
809
|
/**
|
|
@@ -983,6 +993,7 @@ function flag(...args) {
|
|
|
983
993
|
enumerable: false,
|
|
984
994
|
writable: false
|
|
985
995
|
});
|
|
996
|
+
require_empty_input.defineEmptyInputFacts(result, require_empty_input.failingStepFacts(false));
|
|
986
997
|
return require_modifiers.fluent(result);
|
|
987
998
|
}
|
|
988
999
|
function normalizeNegatableFlagNameList(names) {
|
|
@@ -1395,6 +1406,7 @@ function argument(valueParser, options = {}) {
|
|
|
1395
1406
|
configurable: true,
|
|
1396
1407
|
enumerable: false
|
|
1397
1408
|
});
|
|
1409
|
+
else require_empty_input.defineEmptyInputFacts(result, require_empty_input.failingStepFacts(false));
|
|
1398
1410
|
return require_modifiers.fluent(result);
|
|
1399
1411
|
}
|
|
1400
1412
|
function normalizeCommandState(state) {
|
|
@@ -1770,9 +1782,11 @@ function command(name, parser, options = {}) {
|
|
|
1770
1782
|
const child = selectChild(state);
|
|
1771
1783
|
return child == null ? [] : [child];
|
|
1772
1784
|
});
|
|
1773
|
-
|
|
1785
|
+
const scoped = require_short_option.scopeParser(result, commandScope.source, (context) => {
|
|
1774
1786
|
if (normalizeCommandState(context.state) != null) commandScope.select(context, enteredScope);
|
|
1775
|
-
})
|
|
1787
|
+
});
|
|
1788
|
+
require_empty_input.defineEmptyInputFacts(scoped, require_empty_input.failingStepFacts(false));
|
|
1789
|
+
return require_modifiers.fluent(scoped);
|
|
1776
1790
|
}
|
|
1777
1791
|
/**
|
|
1778
1792
|
* Creates a parser that collects unrecognized options and passes them through.
|
|
@@ -1963,6 +1977,7 @@ function passThrough(options = {}) {
|
|
|
1963
1977
|
const accepts = format === "equalsOnly" ? equalsOptionPattern.test(token) : format === "nextToken" && optionPattern.test(token);
|
|
1964
1978
|
return accepts ? result.priority : void 0;
|
|
1965
1979
|
});
|
|
1980
|
+
require_empty_input.defineEmptyInputFacts(result, require_empty_input.failingStepFacts(true));
|
|
1966
1981
|
return require_modifiers.fluent(result);
|
|
1967
1982
|
}
|
|
1968
1983
|
|
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 { effectfulSchedulingNodesKey, exclusiveSourceScopeKey, includeSourceFailureChain, recordDerivedRawInput, replayDerivedParser, replayDerivedParserAsync, sourceCollectionExpansionKey } from "./dependency-runtime.js";
|
|
8
8
|
import { getWrappedChildParseState, getWrappedChildState, isAnnotationWrappedInitialState, normalizeInjectedAnnotationState } from "./annotation-state.js";
|
|
9
|
+
import { UNCHANGED, defineEmptyInputFacts, failingStepFacts } from "./internal/empty-input.js";
|
|
9
10
|
import { mergeChildExec, withChildContext, withChildExecPath } from "./execution-context.js";
|
|
10
11
|
import { extractLeadingCommandNames } from "./usage-internals.js";
|
|
11
12
|
import { attachedValuePrefix, combinedOptionScope, fullNameOwnsToken, prefixSuggestion, scopeParser, selectableOptionScope } from "./short-option.js";
|
|
@@ -174,6 +175,12 @@ function constant(value) {
|
|
|
174
175
|
}
|
|
175
176
|
};
|
|
176
177
|
defineKnownCompletion(result, (state) => normalizeInjectedAnnotationState(state));
|
|
178
|
+
defineEmptyInputFacts(result, {
|
|
179
|
+
step: "success",
|
|
180
|
+
next: UNCHANGED,
|
|
181
|
+
afterStep: true,
|
|
182
|
+
fromInitial: true
|
|
183
|
+
});
|
|
177
184
|
Object.defineProperty(result, "placeholder", {
|
|
178
185
|
value,
|
|
179
186
|
configurable: true,
|
|
@@ -202,7 +209,7 @@ function constant(value) {
|
|
|
202
209
|
* @since 1.0.0
|
|
203
210
|
*/
|
|
204
211
|
function fail() {
|
|
205
|
-
|
|
212
|
+
const result = {
|
|
206
213
|
$valueType: [],
|
|
207
214
|
$stateType: [],
|
|
208
215
|
mode: "sync",
|
|
@@ -236,7 +243,9 @@ function fail() {
|
|
|
236
243
|
sourceOnly: true
|
|
237
244
|
};
|
|
238
245
|
}
|
|
239
|
-
}
|
|
246
|
+
};
|
|
247
|
+
defineEmptyInputFacts(result, failingStepFacts(false));
|
|
248
|
+
return fluent(result);
|
|
240
249
|
}
|
|
241
250
|
/**
|
|
242
251
|
* Internal helper to get suggestions from a value parser, using dependency values
|
|
@@ -794,6 +803,7 @@ function option(...args) {
|
|
|
794
803
|
configurable: true,
|
|
795
804
|
enumerable: false
|
|
796
805
|
});
|
|
806
|
+
else defineEmptyInputFacts(result, failingStepFacts(valueParser == null));
|
|
797
807
|
return fluent(result);
|
|
798
808
|
}
|
|
799
809
|
/**
|
|
@@ -983,6 +993,7 @@ function flag(...args) {
|
|
|
983
993
|
enumerable: false,
|
|
984
994
|
writable: false
|
|
985
995
|
});
|
|
996
|
+
defineEmptyInputFacts(result, failingStepFacts(false));
|
|
986
997
|
return fluent(result);
|
|
987
998
|
}
|
|
988
999
|
function normalizeNegatableFlagNameList(names) {
|
|
@@ -1395,6 +1406,7 @@ function argument(valueParser, options = {}) {
|
|
|
1395
1406
|
configurable: true,
|
|
1396
1407
|
enumerable: false
|
|
1397
1408
|
});
|
|
1409
|
+
else defineEmptyInputFacts(result, failingStepFacts(false));
|
|
1398
1410
|
return fluent(result);
|
|
1399
1411
|
}
|
|
1400
1412
|
function normalizeCommandState(state) {
|
|
@@ -1770,9 +1782,11 @@ function command(name, parser, options = {}) {
|
|
|
1770
1782
|
const child = selectChild(state);
|
|
1771
1783
|
return child == null ? [] : [child];
|
|
1772
1784
|
});
|
|
1773
|
-
|
|
1785
|
+
const scoped = scopeParser(result, commandScope.source, (context) => {
|
|
1774
1786
|
if (normalizeCommandState(context.state) != null) commandScope.select(context, enteredScope);
|
|
1775
|
-
})
|
|
1787
|
+
});
|
|
1788
|
+
defineEmptyInputFacts(scoped, failingStepFacts(false));
|
|
1789
|
+
return fluent(scoped);
|
|
1776
1790
|
}
|
|
1777
1791
|
/**
|
|
1778
1792
|
* Creates a parser that collects unrecognized options and passes them through.
|
|
@@ -1963,6 +1977,7 @@ function passThrough(options = {}) {
|
|
|
1963
1977
|
const accepts = format === "equalsOnly" ? equalsOptionPattern.test(token) : format === "nextToken" && optionPattern.test(token);
|
|
1964
1978
|
return accepts ? result.priority : void 0;
|
|
1965
1979
|
});
|
|
1980
|
+
defineEmptyInputFacts(result, failingStepFacts(true));
|
|
1966
1981
|
return fluent(result);
|
|
1967
1982
|
}
|
|
1968
1983
|
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
const require_displaywidth = require('./displaywidth.cjs');
|
|
2
|
+
const require_terminal_separators = require('./terminal-separators.cjs');
|
|
2
3
|
const require_terminal = require('./terminal.cjs');
|
|
3
4
|
|
|
4
5
|
//#region src/terminal-internal.ts
|
|
@@ -151,6 +152,7 @@ function sameStyle(a, b) {
|
|
|
151
152
|
}
|
|
152
153
|
function* fragmentTokens(fragment, scopes = [], inherited = {}) {
|
|
153
154
|
if (fragment.type === "text") {
|
|
155
|
+
const marks = require_terminal_separators.wrapTrimmableFragments.has(fragment) ? { trimOnWrap: true } : {};
|
|
154
156
|
const lines = fragment.text.split("\n");
|
|
155
157
|
for (let i = 0; i < lines.length; i++) {
|
|
156
158
|
if (i > 0) yield {
|
|
@@ -161,7 +163,8 @@ function* fragmentTokens(fragment, scopes = [], inherited = {}) {
|
|
|
161
163
|
if (lines[i].length > 0 || lines.length === 1) yield {
|
|
162
164
|
text: lines[i],
|
|
163
165
|
width: require_displaywidth.getDisplayWidth(lines[i]),
|
|
164
|
-
scopes
|
|
166
|
+
scopes,
|
|
167
|
+
...marks
|
|
165
168
|
};
|
|
166
169
|
}
|
|
167
170
|
return;
|
|
@@ -16,6 +16,11 @@ interface TerminalToken {
|
|
|
16
16
|
readonly programBoundary?: boolean;
|
|
17
17
|
/** Enables only the legacy default formatter whitespace behavior. */
|
|
18
18
|
readonly legacyWhitespace?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Leading whitespace only separates the token from preceding content, so
|
|
21
|
+
* message wrapping may drop it when the token starts a wrapped line.
|
|
22
|
+
*/
|
|
23
|
+
readonly trimOnWrap?: boolean;
|
|
19
24
|
}
|
|
20
25
|
declare function terminalText(text: string): TerminalFragment;
|
|
21
26
|
declare function formatTerminalTerm(term: TerminalTerm, theme?: TerminalTheme, quotes?: boolean, usage?: TerminalFormatContext["usage"]): TerminalFragment;
|
|
@@ -16,6 +16,11 @@ interface TerminalToken {
|
|
|
16
16
|
readonly programBoundary?: boolean;
|
|
17
17
|
/** Enables only the legacy default formatter whitespace behavior. */
|
|
18
18
|
readonly legacyWhitespace?: boolean;
|
|
19
|
+
/**
|
|
20
|
+
* Leading whitespace only separates the token from preceding content, so
|
|
21
|
+
* message wrapping may drop it when the token starts a wrapped line.
|
|
22
|
+
*/
|
|
23
|
+
readonly trimOnWrap?: boolean;
|
|
19
24
|
}
|
|
20
25
|
declare function terminalText(text: string): TerminalFragment;
|
|
21
26
|
declare function formatTerminalTerm(term: TerminalTerm, theme?: TerminalTheme, quotes?: boolean, usage?: TerminalFormatContext["usage"]): TerminalFragment;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getDisplayWidth } from "./displaywidth.js";
|
|
2
|
+
import { wrapTrimmableFragments } from "./terminal-separators.js";
|
|
2
3
|
import { defaultTerminalTheme } from "./terminal.js";
|
|
3
4
|
|
|
4
5
|
//#region src/terminal-internal.ts
|
|
@@ -151,6 +152,7 @@ function sameStyle(a, b) {
|
|
|
151
152
|
}
|
|
152
153
|
function* fragmentTokens(fragment, scopes = [], inherited = {}) {
|
|
153
154
|
if (fragment.type === "text") {
|
|
155
|
+
const marks = wrapTrimmableFragments.has(fragment) ? { trimOnWrap: true } : {};
|
|
154
156
|
const lines = fragment.text.split("\n");
|
|
155
157
|
for (let i = 0; i < lines.length; i++) {
|
|
156
158
|
if (i > 0) yield {
|
|
@@ -161,7 +163,8 @@ function* fragmentTokens(fragment, scopes = [], inherited = {}) {
|
|
|
161
163
|
if (lines[i].length > 0 || lines.length === 1) yield {
|
|
162
164
|
text: lines[i],
|
|
163
165
|
width: getDisplayWidth(lines[i]),
|
|
164
|
-
scopes
|
|
166
|
+
scopes,
|
|
167
|
+
...marks
|
|
165
168
|
};
|
|
166
169
|
}
|
|
167
170
|
return;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
|
|
2
|
+
//#region src/terminal-separators.ts
|
|
3
|
+
/**
|
|
4
|
+
* Text fragments that only separate the items around them, such as the
|
|
5
|
+
* spaces the default `values` formatter puts between values. When message
|
|
6
|
+
* wrapping moves such a fragment to the start of a new line, its leading
|
|
7
|
+
* whitespace may be dropped.
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
const wrapTrimmableFragments = /* @__PURE__ */ new WeakSet();
|
|
11
|
+
|
|
12
|
+
//#endregion
|
|
13
|
+
exports.wrapTrimmableFragments = wrapTrimmableFragments;
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
//#region src/terminal-separators.ts
|
|
2
|
+
/**
|
|
3
|
+
* Text fragments that only separate the items around them, such as the
|
|
4
|
+
* spaces the default `values` formatter puts between values. When message
|
|
5
|
+
* wrapping moves such a fragment to the start of a new line, its leading
|
|
6
|
+
* whitespace may be dropped.
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
const wrapTrimmableFragments = /* @__PURE__ */ new WeakSet();
|
|
10
|
+
|
|
11
|
+
//#endregion
|
|
12
|
+
export { wrapTrimmableFragments };
|
package/dist/terminal.cjs
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
const require_terminal_separators = require('./terminal-separators.cjs');
|
|
1
2
|
|
|
2
3
|
//#region src/terminal.ts
|
|
3
4
|
const plain = (text) => ({
|
|
@@ -9,6 +10,8 @@ const styled = (text, style) => ({
|
|
|
9
10
|
style,
|
|
10
11
|
children: [plain(text)]
|
|
11
12
|
});
|
|
13
|
+
const valuesSeparator = plain(" ");
|
|
14
|
+
require_terminal_separators.wrapTrimmableFragments.add(valuesSeparator);
|
|
12
15
|
/**
|
|
13
16
|
* Optique's built-in terminal theme, with every formatter available for delegation.
|
|
14
17
|
* @since 1.3.0
|
|
@@ -34,7 +37,7 @@ const defaultTerminalTheme = {
|
|
|
34
37
|
value: (_term, ctx) => ctx.usage === "literal" ? plain(ctx.text) : styled(ctx.text, { foreground: "green" }),
|
|
35
38
|
values: (term, ctx) => ({
|
|
36
39
|
type: "concat",
|
|
37
|
-
children: term.values.flatMap((value, i) => [...i === 0 ? [] : [
|
|
40
|
+
children: term.values.flatMap((value, i) => [...i === 0 ? [] : [valuesSeparator], ctx.format({
|
|
38
41
|
type: "value",
|
|
39
42
|
value
|
|
40
43
|
})])
|
package/dist/terminal.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { wrapTrimmableFragments } from "./terminal-separators.js";
|
|
2
|
+
|
|
1
3
|
//#region src/terminal.ts
|
|
2
4
|
const plain = (text) => ({
|
|
3
5
|
type: "text",
|
|
@@ -8,6 +10,8 @@ const styled = (text, style) => ({
|
|
|
8
10
|
style,
|
|
9
11
|
children: [plain(text)]
|
|
10
12
|
});
|
|
13
|
+
const valuesSeparator = plain(" ");
|
|
14
|
+
wrapTrimmableFragments.add(valuesSeparator);
|
|
11
15
|
/**
|
|
12
16
|
* Optique's built-in terminal theme, with every formatter available for delegation.
|
|
13
17
|
* @since 1.3.0
|
|
@@ -33,7 +37,7 @@ const defaultTerminalTheme = {
|
|
|
33
37
|
value: (_term, ctx) => ctx.usage === "literal" ? plain(ctx.text) : styled(ctx.text, { foreground: "green" }),
|
|
34
38
|
values: (term, ctx) => ({
|
|
35
39
|
type: "concat",
|
|
36
|
-
children: term.values.flatMap((value, i) => [...i === 0 ? [] : [
|
|
40
|
+
children: term.values.flatMap((value, i) => [...i === 0 ? [] : [valuesSeparator], ctx.format({
|
|
37
41
|
type: "value",
|
|
38
42
|
value
|
|
39
43
|
})])
|
package/dist/text-layout.cjs
CHANGED
|
@@ -51,6 +51,18 @@ function placeText(text, cursor, maxWidth, trailingRoom = 0) {
|
|
|
51
51
|
}
|
|
52
52
|
};
|
|
53
53
|
}
|
|
54
|
+
const leadingSpacePattern = /^((?:\x1B(?:\[[0-9;:]*[@-~]|\][^\x1B\x07]*(?:\x1B\\|\x07)))*)[^\S\n]+/;
|
|
55
|
+
/**
|
|
56
|
+
* Removes the whitespace that begins a text's first line, keeping any
|
|
57
|
+
* escape sequences before it. Used when layout moves text that starts with
|
|
58
|
+
* a separating space to a fresh line.
|
|
59
|
+
* @param text Rendered text, possibly containing ANSI styles or OSC links.
|
|
60
|
+
* @returns The text without its leading whitespace.
|
|
61
|
+
* @internal
|
|
62
|
+
*/
|
|
63
|
+
function trimLeadingSpace(text) {
|
|
64
|
+
return text.replace(leadingSpacePattern, "$1");
|
|
65
|
+
}
|
|
54
66
|
/**
|
|
55
67
|
* Measures an annotation's fixed text without invoking theme callbacks.
|
|
56
68
|
* @param prefix The already joined prefix and optional label.
|
|
@@ -84,4 +96,5 @@ function spaceAfterLabel(label) {
|
|
|
84
96
|
exports.measureAnnotation = measureAnnotation;
|
|
85
97
|
exports.measureText = measureText;
|
|
86
98
|
exports.placeText = placeText;
|
|
87
|
-
exports.spaceAfterLabel = spaceAfterLabel;
|
|
99
|
+
exports.spaceAfterLabel = spaceAfterLabel;
|
|
100
|
+
exports.trimLeadingSpace = trimLeadingSpace;
|
package/dist/text-layout.d.cts
CHANGED
|
@@ -41,7 +41,14 @@ declare function placeText(text: string, cursor: TextCursor, maxWidth?: number,
|
|
|
41
41
|
readonly text: string;
|
|
42
42
|
readonly cursor: TextCursor;
|
|
43
43
|
};
|
|
44
|
-
/**
|
|
44
|
+
/**
|
|
45
|
+
* Removes the whitespace that begins a text's first line, keeping any
|
|
46
|
+
* escape sequences before it. Used when layout moves text that starts with
|
|
47
|
+
* a separating space to a fresh line.
|
|
48
|
+
* @param text Rendered text, possibly containing ANSI styles or OSC links.
|
|
49
|
+
* @returns The text without its leading whitespace.
|
|
50
|
+
* @internal
|
|
51
|
+
*/
|
|
45
52
|
|
|
46
53
|
/**
|
|
47
54
|
* Adds a separating space only when a label occupies its final physical line.
|
package/dist/text-layout.d.ts
CHANGED
|
@@ -41,7 +41,14 @@ declare function placeText(text: string, cursor: TextCursor, maxWidth?: number,
|
|
|
41
41
|
readonly text: string;
|
|
42
42
|
readonly cursor: TextCursor;
|
|
43
43
|
};
|
|
44
|
-
/**
|
|
44
|
+
/**
|
|
45
|
+
* Removes the whitespace that begins a text's first line, keeping any
|
|
46
|
+
* escape sequences before it. Used when layout moves text that starts with
|
|
47
|
+
* a separating space to a fresh line.
|
|
48
|
+
* @param text Rendered text, possibly containing ANSI styles or OSC links.
|
|
49
|
+
* @returns The text without its leading whitespace.
|
|
50
|
+
* @internal
|
|
51
|
+
*/
|
|
45
52
|
|
|
46
53
|
/**
|
|
47
54
|
* Adds a separating space only when a label occupies its final physical line.
|
package/dist/text-layout.js
CHANGED
|
@@ -51,6 +51,18 @@ function placeText(text, cursor, maxWidth, trailingRoom = 0) {
|
|
|
51
51
|
}
|
|
52
52
|
};
|
|
53
53
|
}
|
|
54
|
+
const leadingSpacePattern = /^((?:\x1B(?:\[[0-9;:]*[@-~]|\][^\x1B\x07]*(?:\x1B\\|\x07)))*)[^\S\n]+/;
|
|
55
|
+
/**
|
|
56
|
+
* Removes the whitespace that begins a text's first line, keeping any
|
|
57
|
+
* escape sequences before it. Used when layout moves text that starts with
|
|
58
|
+
* a separating space to a fresh line.
|
|
59
|
+
* @param text Rendered text, possibly containing ANSI styles or OSC links.
|
|
60
|
+
* @returns The text without its leading whitespace.
|
|
61
|
+
* @internal
|
|
62
|
+
*/
|
|
63
|
+
function trimLeadingSpace(text) {
|
|
64
|
+
return text.replace(leadingSpacePattern, "$1");
|
|
65
|
+
}
|
|
54
66
|
/**
|
|
55
67
|
* Measures an annotation's fixed text without invoking theme callbacks.
|
|
56
68
|
* @param prefix The already joined prefix and optional label.
|
|
@@ -81,4 +93,4 @@ function spaceAfterLabel(label) {
|
|
|
81
93
|
}
|
|
82
94
|
|
|
83
95
|
//#endregion
|
|
84
|
-
export { measureAnnotation, measureText, placeText, spaceAfterLabel };
|
|
96
|
+
export { measureAnnotation, measureText, placeText, spaceAfterLabel, trimLeadingSpace };
|
package/dist/usage.cjs
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
const require_displaywidth = require('./displaywidth.cjs');
|
|
2
2
|
const require_terminal_internal = require('./terminal-internal.cjs');
|
|
3
|
+
const require_internal_usage = require('./internal/usage.cjs');
|
|
3
4
|
const require_validate = require('./validate.cjs');
|
|
4
5
|
|
|
5
6
|
//#region src/usage.ts
|
|
@@ -190,16 +191,33 @@ function formatUsage(programName, usage, options = {}) {
|
|
|
190
191
|
theme: require_terminal_internal.cacheTerminalTheme(options.theme)
|
|
191
192
|
};
|
|
192
193
|
require_validate.validateProgramName(programName);
|
|
193
|
-
usage
|
|
194
|
+
return formatUsageLines(programName, normalizeUsage(filterUsageForDisplay(require_internal_usage.resolveUsageForDisplay(usage, isUsageHidden))), normalizeUsage(filterUsageForDisplay(require_internal_usage.forgetEmptyInput(usage))), options);
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Formats a display-ready usage, expanding a trailing command group into one
|
|
198
|
+
* line per command when requested.
|
|
199
|
+
* @param programName The program name.
|
|
200
|
+
* @param usage The usage to draw, already resolved, filtered and normalized.
|
|
201
|
+
* @param declared The same usage as declared, without empty-input records,
|
|
202
|
+
* filtered and normalized. It tells a group that was drawn
|
|
203
|
+
* as optional because it accepts empty input apart from a
|
|
204
|
+
* group declared optional.
|
|
205
|
+
* @param options The formatting options.
|
|
206
|
+
* @returns The formatted usage.
|
|
207
|
+
*/
|
|
208
|
+
function formatUsageLines(programName, usage, declared, options) {
|
|
194
209
|
if (options.expandCommands) {
|
|
195
|
-
|
|
210
|
+
let lastTerm = usage.at(-1);
|
|
211
|
+
const declaredLast = declared.at(-1);
|
|
212
|
+
if (lastTerm?.type === "optional" && lastTerm.terms.length === 1 && lastTerm.terms[0].type === "exclusive" && declaredLast?.type === "exclusive") lastTerm = lastTerm.terms[0];
|
|
196
213
|
if (usage.length > 0 && usage.slice(0, -1).every((t) => t.type === "command") && lastTerm.type === "exclusive" && lastTerm.terms.every((t) => t.length > 0 && (t[0].type === "command" || t[0].type === "option" || t[0].type === "argument" || t[0].type === "optional" && t[0].terms.length === 1 && (t[0].terms[0].type === "command" || t[0].terms[0].type === "option" || t[0].terms[0].type === "argument")))) {
|
|
214
|
+
const declaredBranches = declaredLast?.type === "exclusive" && declaredLast.terms.length === lastTerm.terms.length ? declaredLast.terms : void 0;
|
|
197
215
|
const lines = [];
|
|
198
|
-
for (
|
|
216
|
+
for (const [index, command] of lastTerm.terms.entries()) {
|
|
199
217
|
const firstTerm = command[0];
|
|
200
218
|
if (firstTerm?.type === "command" && isUsageHidden(firstTerm.hidden)) continue;
|
|
201
|
-
|
|
202
|
-
lines.push(
|
|
219
|
+
const line = normalizeUsage(filterUsageForDisplay([...usage.slice(0, -1), ...command]));
|
|
220
|
+
lines.push(formatUsageLines(programName, line, declaredBranches == null ? line : normalizeUsage(filterUsageForDisplay([...declared.slice(0, -1), ...declaredBranches[index]])), options));
|
|
203
221
|
}
|
|
204
222
|
if (lines.length > 0) return lines.join("\n");
|
|
205
223
|
}
|
|
@@ -277,7 +295,8 @@ function normalizeUsageTerm(term) {
|
|
|
277
295
|
} else if (term.type === "multiple") return {
|
|
278
296
|
type: "multiple",
|
|
279
297
|
terms: normalizeUsage(term.terms),
|
|
280
|
-
min: term.min
|
|
298
|
+
min: term.min,
|
|
299
|
+
...acceptsEmptyOf(term)
|
|
281
300
|
};
|
|
282
301
|
else if (term.type === "sequence") return {
|
|
283
302
|
type: "sequence",
|
|
@@ -287,14 +306,15 @@ function normalizeUsageTerm(term) {
|
|
|
287
306
|
const terms = [];
|
|
288
307
|
for (const usage of term.terms) {
|
|
289
308
|
const normalized = normalizeUsage(usage);
|
|
290
|
-
if (normalized.length >= 1 && normalized[0].type === "exclusive") {
|
|
309
|
+
if (normalized.length >= 1 && normalized[0].type === "exclusive" && normalized[0].acceptsEmpty == null) {
|
|
291
310
|
const rest = normalized.slice(1);
|
|
292
311
|
for (const subUsage of normalized[0].terms) terms.push([...subUsage, ...rest]);
|
|
293
312
|
} else if (normalized.length > 0 || !containsMalformedLeaf(usage)) terms.push(normalized);
|
|
294
313
|
}
|
|
295
314
|
return {
|
|
296
315
|
type: "exclusive",
|
|
297
|
-
terms
|
|
316
|
+
terms,
|
|
317
|
+
...acceptsEmptyOf(term)
|
|
298
318
|
};
|
|
299
319
|
} else {
|
|
300
320
|
if (term.type === "option") return {
|
|
@@ -311,10 +331,14 @@ function normalizeUsageTerm(term) {
|
|
|
311
331
|
return { ...term };
|
|
312
332
|
}
|
|
313
333
|
}
|
|
334
|
+
function acceptsEmptyOf(term) {
|
|
335
|
+
return term.acceptsEmpty == null ? {} : { acceptsEmpty: term.acceptsEmpty };
|
|
336
|
+
}
|
|
314
337
|
function isNonDegenerateTerm(term) {
|
|
315
338
|
if (term.type === "option") return term.names.length > 0;
|
|
316
339
|
if (term.type === "command") return term.name !== "";
|
|
317
340
|
if (term.type === "argument") return term.metavar.length > 0;
|
|
341
|
+
if ((term.type === "multiple" || term.type === "exclusive") && term.acceptsEmpty != null) return true;
|
|
318
342
|
if (term.type === "optional" || term.type === "multiple" || term.type === "exclusive" || term.type === "sequence") return term.terms.length > 0;
|
|
319
343
|
return true;
|
|
320
344
|
}
|
|
@@ -370,11 +394,13 @@ function cloneUsageTerm(term) {
|
|
|
370
394
|
case "multiple": return {
|
|
371
395
|
type: "multiple",
|
|
372
396
|
terms: term.terms.map(cloneUsageTerm),
|
|
373
|
-
min: term.min
|
|
397
|
+
min: term.min,
|
|
398
|
+
...acceptsEmptyOf(term)
|
|
374
399
|
};
|
|
375
400
|
case "exclusive": return {
|
|
376
401
|
type: "exclusive",
|
|
377
|
-
terms: term.terms.map((u) => u.map(cloneUsageTerm))
|
|
402
|
+
terms: term.terms.map((u) => u.map(cloneUsageTerm)),
|
|
403
|
+
...acceptsEmptyOf(term)
|
|
378
404
|
};
|
|
379
405
|
case "sequence": return {
|
|
380
406
|
type: "sequence",
|
|
@@ -416,7 +442,8 @@ function filterUsageForDisplay(usage, isHidden = isUsageHidden) {
|
|
|
416
442
|
if (filtered.length > 0) terms.push({
|
|
417
443
|
type: "multiple",
|
|
418
444
|
terms: filtered,
|
|
419
|
-
min: term.min
|
|
445
|
+
min: term.min,
|
|
446
|
+
...acceptsEmptyOf(term)
|
|
420
447
|
});
|
|
421
448
|
continue;
|
|
422
449
|
}
|
|
@@ -428,7 +455,8 @@ function filterUsageForDisplay(usage, isHidden = isUsageHidden) {
|
|
|
428
455
|
}).filter((branch) => branch.length > 0);
|
|
429
456
|
if (filteredBranches.length > 0) terms.push({
|
|
430
457
|
type: "exclusive",
|
|
431
|
-
terms: filteredBranches
|
|
458
|
+
terms: filteredBranches,
|
|
459
|
+
...acceptsEmptyOf(term)
|
|
432
460
|
});
|
|
433
461
|
continue;
|
|
434
462
|
}
|
|
@@ -482,7 +510,7 @@ function formatUsageTerm(term, options = {}) {
|
|
|
482
510
|
theme: require_terminal_internal.cacheTerminalTheme(options.theme)
|
|
483
511
|
};
|
|
484
512
|
const hiddenCheck = options.context === "doc" ? isDocHidden : isUsageHidden;
|
|
485
|
-
const visibleTerms = filterUsageForDisplay([term], hiddenCheck);
|
|
513
|
+
const visibleTerms = filterUsageForDisplay(require_internal_usage.resolveUsageForDisplay([term], hiddenCheck), hiddenCheck);
|
|
486
514
|
if (visibleTerms.length < 1) return "";
|
|
487
515
|
return wrapUsageTokens(formatUsageTermInternal(visibleTerms[0], options), options);
|
|
488
516
|
}
|
package/dist/usage.d.cts
CHANGED
|
@@ -164,6 +164,16 @@ type UsageTerm =
|
|
|
164
164
|
* The minimum number of times the term must occur.
|
|
165
165
|
*/
|
|
166
166
|
readonly min: number;
|
|
167
|
+
/**
|
|
168
|
+
* Whether the parser that produced this group succeeds when it parses an
|
|
169
|
+
* empty argument list on its own. Built-in combinators set this when
|
|
170
|
+
* the outcome is statically known and omit it otherwise. Usage
|
|
171
|
+
* formatters use it to decide whether to draw the group as omissible;
|
|
172
|
+
* it is a declaration for display, not evidence about how any other
|
|
173
|
+
* parser behaves.
|
|
174
|
+
* @since 1.4.0
|
|
175
|
+
*/
|
|
176
|
+
readonly acceptsEmpty?: boolean;
|
|
167
177
|
} |
|
|
168
178
|
/**
|
|
169
179
|
* An exclusive term, which represents a group of terms that are mutually
|
|
@@ -180,6 +190,16 @@ type UsageTerm =
|
|
|
180
190
|
* arguments, options, commands, or other usage terms.
|
|
181
191
|
*/
|
|
182
192
|
readonly terms: readonly Usage[];
|
|
193
|
+
/**
|
|
194
|
+
* Whether the parser that produced this group succeeds when it parses an
|
|
195
|
+
* empty argument list on its own. Built-in combinators set this when
|
|
196
|
+
* the outcome is statically known and omit it otherwise. Usage
|
|
197
|
+
* formatters use it to decide whether to draw the group as omissible;
|
|
198
|
+
* it is a declaration for display, not evidence about how any other
|
|
199
|
+
* parser behaves.
|
|
200
|
+
* @since 1.4.0
|
|
201
|
+
*/
|
|
202
|
+
readonly acceptsEmpty?: boolean;
|
|
183
203
|
}
|
|
184
204
|
/**
|
|
185
205
|
* A sequence term, which preserves the declaration order of its child
|
package/dist/usage.d.ts
CHANGED
|
@@ -164,6 +164,16 @@ type UsageTerm =
|
|
|
164
164
|
* The minimum number of times the term must occur.
|
|
165
165
|
*/
|
|
166
166
|
readonly min: number;
|
|
167
|
+
/**
|
|
168
|
+
* Whether the parser that produced this group succeeds when it parses an
|
|
169
|
+
* empty argument list on its own. Built-in combinators set this when
|
|
170
|
+
* the outcome is statically known and omit it otherwise. Usage
|
|
171
|
+
* formatters use it to decide whether to draw the group as omissible;
|
|
172
|
+
* it is a declaration for display, not evidence about how any other
|
|
173
|
+
* parser behaves.
|
|
174
|
+
* @since 1.4.0
|
|
175
|
+
*/
|
|
176
|
+
readonly acceptsEmpty?: boolean;
|
|
167
177
|
} |
|
|
168
178
|
/**
|
|
169
179
|
* An exclusive term, which represents a group of terms that are mutually
|
|
@@ -180,6 +190,16 @@ type UsageTerm =
|
|
|
180
190
|
* arguments, options, commands, or other usage terms.
|
|
181
191
|
*/
|
|
182
192
|
readonly terms: readonly Usage[];
|
|
193
|
+
/**
|
|
194
|
+
* Whether the parser that produced this group succeeds when it parses an
|
|
195
|
+
* empty argument list on its own. Built-in combinators set this when
|
|
196
|
+
* the outcome is statically known and omit it otherwise. Usage
|
|
197
|
+
* formatters use it to decide whether to draw the group as omissible;
|
|
198
|
+
* it is a declaration for display, not evidence about how any other
|
|
199
|
+
* parser behaves.
|
|
200
|
+
* @since 1.4.0
|
|
201
|
+
*/
|
|
202
|
+
readonly acceptsEmpty?: boolean;
|
|
183
203
|
}
|
|
184
204
|
/**
|
|
185
205
|
* A sequence term, which preserves the declaration order of its child
|