@optique/core 1.3.0-dev.2542 → 1.3.0-dev.2558

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/facade.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  const require_internal_annotations = require('./internal/annotations.cjs');
2
- const require_displaywidth = require('./displaywidth.cjs');
3
2
  const require_terminal_internal = require('./terminal-internal.cjs');
3
+ const require_text_layout = require('./text-layout.cjs');
4
4
  const require_message = require('./message.cjs');
5
5
  const require_message_renderer = require('./message-renderer.cjs');
6
6
  const require_validate = require('./validate.cjs');
@@ -517,27 +517,19 @@ function classifyParseFailure(failure, helpOptionNames, helpCommandNames, versio
517
517
  * @since 0.6.0
518
518
  */
519
519
  function handleCompletion(completionArgs, programName, parser, completionParser, stdout, stderr, onCompletion, onError, availableShells, colors, maxWidth, termWidth, completionCommandDisplayName, completionOptionDisplayName, isOptionMode, sectionOrder, showUsage, rootOptionSuggestions = [], messageFormatter, theme) {
520
- const formatMessage = require_message_renderer.resolveMessageFormatter({
521
- messageFormatter,
522
- theme
523
- });
524
- const errorLabel = () => require_terminal_internal.renderTerminalTerm({
525
- type: "errorLabel",
526
- label: "Error:"
527
- }, theme, colors) + " ";
528
520
  const shellName = completionArgs[0] || "";
529
521
  const args = completionArgs.slice(1);
530
522
  const callOnError = (code, error) => onError(code, error);
531
523
  const callOnCompletion = (code) => onCompletion(code);
532
524
  if (!shellName) {
533
525
  const error = require_message.message`Missing shell name for completion.`;
534
- const prefix = errorLabel();
535
- stderr(`${prefix}${formatMessage(error, {
526
+ stderr(require_message_renderer.renderErrorMessage(error, {
527
+ messageFormatter,
528
+ theme,
536
529
  colors,
537
530
  quotes: !colors,
538
- maxWidth,
539
- initialWidth: require_displaywidth.getDisplayWidth(prefix.split("\n").at(-1) ?? "")
540
- })}\n`);
531
+ maxWidth
532
+ }) + "\n");
541
533
  if (completionParser) {
542
534
  const displayName = isOptionMode ? completionOptionDisplayName ?? "--completion" : completionCommandDisplayName ?? "completion";
543
535
  const doc = require_internal_parser.getDocPage(completionParser, [displayName]);
@@ -565,13 +557,13 @@ function handleCompletion(completionArgs, programName, parser, completionParser,
565
557
  available.push(require_message.value(name));
566
558
  }
567
559
  const error = require_message.message`Unsupported shell ${shellName}. Available shells: ${available}.`;
568
- const prefix = errorLabel();
569
- stderr(`${prefix}${formatMessage(error, {
560
+ stderr(require_message_renderer.renderErrorMessage(error, {
561
+ messageFormatter,
562
+ theme,
570
563
  colors,
571
564
  quotes: !colors,
572
- maxWidth,
573
- initialWidth: require_displaywidth.getDisplayWidth(prefix.split("\n").at(-1) ?? "")
574
- })}`);
565
+ maxWidth
566
+ }));
575
567
  return require_mode_dispatch.dispatchByMode(parser.mode, () => {
576
568
  const result = callOnError(1, error);
577
569
  if (result instanceof Promise) throw new RunParserError("Synchronous parser returned async result.");
@@ -882,21 +874,13 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
882
874
  const { colors, messageFormatter, theme, maxWidth, termWidth, showDefault, showChoices, sectionOrder, showUsage, usageLine, commandList = "recursive", aboveError = "usage", onError = () => {
883
875
  throw new RunParserError("Failed to parse command line arguments.");
884
876
  }, stderr = console.error, stdout = console.log, brief, description, examples, author, bugs, footer } = options;
885
- const formatMessage = require_message_renderer.resolveMessageFormatter({
886
- messageFormatter,
887
- theme
888
- });
889
- const errorLabel = () => require_terminal_internal.renderTerminalTerm({
890
- type: "errorLabel",
891
- label: "Error:"
892
- }, theme, colors) + " ";
893
877
  let usagePrefix;
894
- const usageLabel = () => usagePrefix ??= require_terminal_internal.renderTerminalTerm({
878
+ const usageLabel = () => usagePrefix ??= require_text_layout.spaceAfterLabel(require_terminal_internal.renderTerminalTerm({
895
879
  type: "label",
896
880
  label: "Usage:",
897
881
  kind: "usageSummary"
898
- }, theme, colors) + " ";
899
- const usageLabelWidth = () => require_displaywidth.getDisplayWidth(usageLabel().split("\n").at(-1) ?? "");
882
+ }, theme, colors));
883
+ const usageLabelWidth = () => require_text_layout.measureText(usageLabel()).lastLineWidth;
900
884
  const norm = (c) => c === true ? {} : c;
901
885
  const helpCommandConfig = norm(options.help?.command);
902
886
  const helpOptionConfig = norm(options.help?.option);
@@ -1057,14 +1041,13 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
1057
1041
  maxWidth: maxWidth == null ? void 0 : maxWidth - usageLabelWidth(),
1058
1042
  expandCommands: true
1059
1043
  }), usageLabelWidth())}`);
1060
- const prefix = errorLabel();
1061
- const errorMessage = formatMessage(validationError, {
1062
- initialWidth: require_displaywidth.getDisplayWidth(prefix.split("\n").at(-1) ?? ""),
1063
- maxWidth,
1044
+ stderr(require_message_renderer.renderErrorMessage(validationError, {
1045
+ messageFormatter,
1046
+ theme,
1064
1047
  colors,
1065
- quotes: !colors
1066
- });
1067
- stderr(`${prefix}${errorMessage}`);
1048
+ quotes: !colors,
1049
+ maxWidth
1050
+ }));
1068
1051
  return onError(1, validationError);
1069
1052
  };
1070
1053
  const displayHelp = (doc) => {
@@ -1177,14 +1160,13 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
1177
1160
  maxWidth: maxWidth == null ? void 0 : maxWidth - usageLabelWidth(),
1178
1161
  expandCommands: true
1179
1162
  }), usageLabelWidth())}`);
1180
- const prefix = errorLabel();
1181
- const errorMessage = formatMessage(classified.error, {
1182
- initialWidth: require_displaywidth.getDisplayWidth(prefix.split("\n").at(-1) ?? ""),
1183
- maxWidth,
1163
+ stderr(require_message_renderer.renderErrorMessage(classified.error, {
1164
+ messageFormatter,
1165
+ theme,
1184
1166
  colors,
1185
- quotes: !colors
1186
- });
1187
- stderr(`${prefix}${errorMessage}`);
1167
+ quotes: !colors,
1168
+ maxWidth
1169
+ }));
1188
1170
  return onError(1, classified.error);
1189
1171
  };
1190
1172
  if (aboveError === "help") {
package/dist/facade.js CHANGED
@@ -1,8 +1,8 @@
1
1
  import { injectAnnotations } from "./internal/annotations.js";
2
- import { getDisplayWidth } from "./displaywidth.js";
3
2
  import { renderTerminalTerm } from "./terminal-internal.js";
3
+ import { measureText, spaceAfterLabel } from "./text-layout.js";
4
4
  import { commandLine, lineBreak, message, optionName, text, value } from "./message.js";
5
- import { resolveMessageFormatter } from "./message-renderer.js";
5
+ import { renderErrorMessage } from "./message-renderer.js";
6
6
  import { validateCommandNames, validateContextIds, validateMetaNameCollisions, validateOptionNames, validateProgramName } from "./validate.js";
7
7
  import { cloneUsage, formatUsage, isSuggestionHidden, normalizeUsage } from "./usage.js";
8
8
  import { formatDocPage } from "./doc.js";
@@ -517,27 +517,19 @@ function classifyParseFailure(failure, helpOptionNames, helpCommandNames, versio
517
517
  * @since 0.6.0
518
518
  */
519
519
  function handleCompletion(completionArgs, programName, parser, completionParser, stdout, stderr, onCompletion, onError, availableShells, colors, maxWidth, termWidth, completionCommandDisplayName, completionOptionDisplayName, isOptionMode, sectionOrder, showUsage, rootOptionSuggestions = [], messageFormatter, theme) {
520
- const formatMessage = resolveMessageFormatter({
521
- messageFormatter,
522
- theme
523
- });
524
- const errorLabel = () => renderTerminalTerm({
525
- type: "errorLabel",
526
- label: "Error:"
527
- }, theme, colors) + " ";
528
520
  const shellName = completionArgs[0] || "";
529
521
  const args = completionArgs.slice(1);
530
522
  const callOnError = (code, error) => onError(code, error);
531
523
  const callOnCompletion = (code) => onCompletion(code);
532
524
  if (!shellName) {
533
525
  const error = message`Missing shell name for completion.`;
534
- const prefix = errorLabel();
535
- stderr(`${prefix}${formatMessage(error, {
526
+ stderr(renderErrorMessage(error, {
527
+ messageFormatter,
528
+ theme,
536
529
  colors,
537
530
  quotes: !colors,
538
- maxWidth,
539
- initialWidth: getDisplayWidth(prefix.split("\n").at(-1) ?? "")
540
- })}\n`);
531
+ maxWidth
532
+ }) + "\n");
541
533
  if (completionParser) {
542
534
  const displayName = isOptionMode ? completionOptionDisplayName ?? "--completion" : completionCommandDisplayName ?? "completion";
543
535
  const doc = getDocPage(completionParser, [displayName]);
@@ -565,13 +557,13 @@ function handleCompletion(completionArgs, programName, parser, completionParser,
565
557
  available.push(value(name));
566
558
  }
567
559
  const error = message`Unsupported shell ${shellName}. Available shells: ${available}.`;
568
- const prefix = errorLabel();
569
- stderr(`${prefix}${formatMessage(error, {
560
+ stderr(renderErrorMessage(error, {
561
+ messageFormatter,
562
+ theme,
570
563
  colors,
571
564
  quotes: !colors,
572
- maxWidth,
573
- initialWidth: getDisplayWidth(prefix.split("\n").at(-1) ?? "")
574
- })}`);
565
+ maxWidth
566
+ }));
575
567
  return dispatchByMode(parser.mode, () => {
576
568
  const result = callOnError(1, error);
577
569
  if (result instanceof Promise) throw new RunParserError("Synchronous parser returned async result.");
@@ -882,21 +874,13 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
882
874
  const { colors, messageFormatter, theme, maxWidth, termWidth, showDefault, showChoices, sectionOrder, showUsage, usageLine, commandList = "recursive", aboveError = "usage", onError = () => {
883
875
  throw new RunParserError("Failed to parse command line arguments.");
884
876
  }, stderr = console.error, stdout = console.log, brief, description, examples, author, bugs, footer } = options;
885
- const formatMessage = resolveMessageFormatter({
886
- messageFormatter,
887
- theme
888
- });
889
- const errorLabel = () => renderTerminalTerm({
890
- type: "errorLabel",
891
- label: "Error:"
892
- }, theme, colors) + " ";
893
877
  let usagePrefix;
894
- const usageLabel = () => usagePrefix ??= renderTerminalTerm({
878
+ const usageLabel = () => usagePrefix ??= spaceAfterLabel(renderTerminalTerm({
895
879
  type: "label",
896
880
  label: "Usage:",
897
881
  kind: "usageSummary"
898
- }, theme, colors) + " ";
899
- const usageLabelWidth = () => getDisplayWidth(usageLabel().split("\n").at(-1) ?? "");
882
+ }, theme, colors));
883
+ const usageLabelWidth = () => measureText(usageLabel()).lastLineWidth;
900
884
  const norm = (c) => c === true ? {} : c;
901
885
  const helpCommandConfig = norm(options.help?.command);
902
886
  const helpOptionConfig = norm(options.help?.option);
@@ -1057,14 +1041,13 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
1057
1041
  maxWidth: maxWidth == null ? void 0 : maxWidth - usageLabelWidth(),
1058
1042
  expandCommands: true
1059
1043
  }), usageLabelWidth())}`);
1060
- const prefix = errorLabel();
1061
- const errorMessage = formatMessage(validationError, {
1062
- initialWidth: getDisplayWidth(prefix.split("\n").at(-1) ?? ""),
1063
- maxWidth,
1044
+ stderr(renderErrorMessage(validationError, {
1045
+ messageFormatter,
1046
+ theme,
1064
1047
  colors,
1065
- quotes: !colors
1066
- });
1067
- stderr(`${prefix}${errorMessage}`);
1048
+ quotes: !colors,
1049
+ maxWidth
1050
+ }));
1068
1051
  return onError(1, validationError);
1069
1052
  };
1070
1053
  const displayHelp = (doc) => {
@@ -1177,14 +1160,13 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
1177
1160
  maxWidth: maxWidth == null ? void 0 : maxWidth - usageLabelWidth(),
1178
1161
  expandCommands: true
1179
1162
  }), usageLabelWidth())}`);
1180
- const prefix = errorLabel();
1181
- const errorMessage = formatMessage(classified.error, {
1182
- initialWidth: getDisplayWidth(prefix.split("\n").at(-1) ?? ""),
1183
- maxWidth,
1163
+ stderr(renderErrorMessage(classified.error, {
1164
+ messageFormatter,
1165
+ theme,
1184
1166
  colors,
1185
- quotes: !colors
1186
- });
1187
- stderr(`${prefix}${errorMessage}`);
1167
+ quotes: !colors,
1168
+ maxWidth
1169
+ }));
1188
1170
  return onError(1, classified.error);
1189
1171
  };
1190
1172
  if (aboveError === "help") {
@@ -1,7 +1,12 @@
1
1
  const require_displaywidth = require('../displaywidth.cjs');
2
2
  const require_terminal_internal = require('../terminal-internal.cjs');
3
+ const require_text_layout = require('../text-layout.cjs');
3
4
  const require_message_renderer = require('../message-renderer.cjs');
4
5
 
5
6
  exports.getDisplayWidth = require_displaywidth.getDisplayWidth;
7
+ exports.measureText = require_text_layout.measureText;
8
+ exports.placeText = require_text_layout.placeText;
9
+ exports.renderErrorMessage = require_message_renderer.renderErrorMessage;
6
10
  exports.renderTerminalTerm = require_terminal_internal.renderTerminalTerm;
7
- exports.resolveMessageFormatter = require_message_renderer.resolveMessageFormatter;
11
+ exports.resolveMessageFormatter = require_message_renderer.resolveMessageFormatter;
12
+ exports.spaceAfterLabel = require_text_layout.spaceAfterLabel;
@@ -1,4 +1,5 @@
1
1
  import { getDisplayWidth } from "../displaywidth.cjs";
2
- import { resolveMessageFormatter } from "../message-renderer.cjs";
2
+ import { renderErrorMessage, resolveMessageFormatter } from "../message-renderer.cjs";
3
3
  import { renderTerminalTerm } from "../terminal-internal.cjs";
4
- export { getDisplayWidth, renderTerminalTerm, resolveMessageFormatter };
4
+ import { measureText, placeText, spaceAfterLabel } from "../text-layout.cjs";
5
+ export { getDisplayWidth, measureText, placeText, renderErrorMessage, renderTerminalTerm, resolveMessageFormatter, spaceAfterLabel };
@@ -1,4 +1,5 @@
1
1
  import { getDisplayWidth } from "../displaywidth.js";
2
- import { resolveMessageFormatter } from "../message-renderer.js";
2
+ import { renderErrorMessage, resolveMessageFormatter } from "../message-renderer.js";
3
3
  import { renderTerminalTerm } from "../terminal-internal.js";
4
- export { getDisplayWidth, renderTerminalTerm, resolveMessageFormatter };
4
+ import { measureText, placeText, spaceAfterLabel } from "../text-layout.js";
5
+ export { getDisplayWidth, measureText, placeText, renderErrorMessage, renderTerminalTerm, resolveMessageFormatter, spaceAfterLabel };
@@ -1,5 +1,6 @@
1
1
  import { getDisplayWidth } from "../displaywidth.js";
2
2
  import { renderTerminalTerm } from "../terminal-internal.js";
3
- import { resolveMessageFormatter } from "../message-renderer.js";
3
+ import { measureText, placeText, spaceAfterLabel } from "../text-layout.js";
4
+ import { renderErrorMessage, resolveMessageFormatter } from "../message-renderer.js";
4
5
 
5
- export { getDisplayWidth, renderTerminalTerm, resolveMessageFormatter };
6
+ export { getDisplayWidth, measureText, placeText, renderErrorMessage, renderTerminalTerm, resolveMessageFormatter, spaceAfterLabel };
@@ -1,4 +1,6 @@
1
1
  const require_message_registry = require('./message-registry.cjs');
2
+ const require_terminal_internal = require('./terminal-internal.cjs');
3
+ const require_text_layout = require('./text-layout.cjs');
2
4
  const require_message = require('./message.cjs');
3
5
 
4
6
  //#region src/message-renderer.ts
@@ -21,6 +23,38 @@ function resolveMessageFormatter(options) {
21
23
  });
22
24
  };
23
25
  }
26
+ /**
27
+ * Assembles a themed error prefix and a message at one layout boundary.
28
+ * Callers own streams, trailing newlines, and the default quoting policy.
29
+ * Opaque formatter output is appended unchanged, including empty output.
30
+ * @param message The original, unprefixed error message.
31
+ * @param options Theme, formatter, and physical-line layout options.
32
+ * @returns The prefixed message without an added trailing newline.
33
+ * @throws {TypeError} If initialWidth is not a finite integer.
34
+ * @throws {RangeError} If initialWidth is negative or a theme color is invalid.
35
+ * @internal
36
+ */
37
+ function renderErrorMessage(message, options = {}) {
38
+ const occupied = options.initialWidth ?? 0;
39
+ if (!Number.isFinite(occupied) || !Number.isInteger(occupied)) throw new TypeError("Initial width must be a finite integer.");
40
+ if (occupied < 0) throw new RangeError("Initial width must be nonnegative.");
41
+ const colors = options.colors;
42
+ const prefix = require_text_layout.placeText(require_text_layout.spaceAfterLabel(require_terminal_internal.renderTerminalTerm({
43
+ type: "errorLabel",
44
+ label: "Error:"
45
+ }, options.theme, typeof colors === "object" ? true : colors, void 0, void 0, typeof colors === "object" ? colors.resetSuffix : void 0)), {
46
+ line: "",
47
+ column: occupied
48
+ }, options.maxWidth);
49
+ const formatted = resolveMessageFormatter(options)(message, {
50
+ colors,
51
+ quotes: options.quotes,
52
+ maxWidth: options.maxWidth,
53
+ initialWidth: prefix.cursor.column
54
+ });
55
+ return prefix.text + formatted;
56
+ }
24
57
 
25
58
  //#endregion
59
+ exports.renderErrorMessage = renderErrorMessage;
26
60
  exports.resolveMessageFormatter = resolveMessageFormatter;
@@ -15,5 +15,20 @@ declare function resolveMessageFormatter(options: {
15
15
  readonly messageFormatter?: MessageFormatter;
16
16
  readonly theme?: TerminalTheme;
17
17
  }): (message: Message, options?: MessageFormatOptions, ambient?: TerminalStyle) => string;
18
+ /**
19
+ * Assembles a themed error prefix and a message at one layout boundary.
20
+ * Callers own streams, trailing newlines, and the default quoting policy.
21
+ * Opaque formatter output is appended unchanged, including empty output.
22
+ * @param message The original, unprefixed error message.
23
+ * @param options Theme, formatter, and physical-line layout options.
24
+ * @returns The prefixed message without an added trailing newline.
25
+ * @throws {TypeError} If initialWidth is not a finite integer.
26
+ * @throws {RangeError} If initialWidth is negative or a theme color is invalid.
27
+ * @internal
28
+ */
29
+ declare function renderErrorMessage(message: Message, options?: MessageFormatOptions & {
30
+ readonly theme?: TerminalTheme;
31
+ readonly messageFormatter?: MessageFormatter;
32
+ }): string;
18
33
  //#endregion
19
- export { resolveMessageFormatter };
34
+ export { renderErrorMessage, resolveMessageFormatter };
@@ -15,5 +15,20 @@ declare function resolveMessageFormatter(options: {
15
15
  readonly messageFormatter?: MessageFormatter;
16
16
  readonly theme?: TerminalTheme;
17
17
  }): (message: Message, options?: MessageFormatOptions, ambient?: TerminalStyle) => string;
18
+ /**
19
+ * Assembles a themed error prefix and a message at one layout boundary.
20
+ * Callers own streams, trailing newlines, and the default quoting policy.
21
+ * Opaque formatter output is appended unchanged, including empty output.
22
+ * @param message The original, unprefixed error message.
23
+ * @param options Theme, formatter, and physical-line layout options.
24
+ * @returns The prefixed message without an added trailing newline.
25
+ * @throws {TypeError} If initialWidth is not a finite integer.
26
+ * @throws {RangeError} If initialWidth is negative or a theme color is invalid.
27
+ * @internal
28
+ */
29
+ declare function renderErrorMessage(message: Message, options?: MessageFormatOptions & {
30
+ readonly theme?: TerminalTheme;
31
+ readonly messageFormatter?: MessageFormatter;
32
+ }): string;
18
33
  //#endregion
19
- export { resolveMessageFormatter };
34
+ export { renderErrorMessage, resolveMessageFormatter };
@@ -1,4 +1,6 @@
1
1
  import { messageRenderers } from "./message-registry.js";
2
+ import { renderTerminalTerm } from "./terminal-internal.js";
3
+ import { placeText, spaceAfterLabel } from "./text-layout.js";
2
4
  import { createMessageFormatter, formatMessage } from "./message.js";
3
5
 
4
6
  //#region src/message-renderer.ts
@@ -21,6 +23,37 @@ function resolveMessageFormatter(options) {
21
23
  });
22
24
  };
23
25
  }
26
+ /**
27
+ * Assembles a themed error prefix and a message at one layout boundary.
28
+ * Callers own streams, trailing newlines, and the default quoting policy.
29
+ * Opaque formatter output is appended unchanged, including empty output.
30
+ * @param message The original, unprefixed error message.
31
+ * @param options Theme, formatter, and physical-line layout options.
32
+ * @returns The prefixed message without an added trailing newline.
33
+ * @throws {TypeError} If initialWidth is not a finite integer.
34
+ * @throws {RangeError} If initialWidth is negative or a theme color is invalid.
35
+ * @internal
36
+ */
37
+ function renderErrorMessage(message, options = {}) {
38
+ const occupied = options.initialWidth ?? 0;
39
+ if (!Number.isFinite(occupied) || !Number.isInteger(occupied)) throw new TypeError("Initial width must be a finite integer.");
40
+ if (occupied < 0) throw new RangeError("Initial width must be nonnegative.");
41
+ const colors = options.colors;
42
+ const prefix = placeText(spaceAfterLabel(renderTerminalTerm({
43
+ type: "errorLabel",
44
+ label: "Error:"
45
+ }, options.theme, typeof colors === "object" ? true : colors, void 0, void 0, typeof colors === "object" ? colors.resetSuffix : void 0)), {
46
+ line: "",
47
+ column: occupied
48
+ }, options.maxWidth);
49
+ const formatted = resolveMessageFormatter(options)(message, {
50
+ colors,
51
+ quotes: options.quotes,
52
+ maxWidth: options.maxWidth,
53
+ initialWidth: prefix.cursor.column
54
+ });
55
+ return prefix.text + formatted;
56
+ }
24
57
 
25
58
  //#endregion
26
- export { resolveMessageFormatter };
59
+ export { renderErrorMessage, resolveMessageFormatter };
@@ -17,11 +17,29 @@ function valuesFormatter(theme) {
17
17
  children: [require_terminal.defaultTerminalTheme.values(term, context)]
18
18
  });
19
19
  }
20
+ /** Default callback identity survives any number of per-pass caches. */
21
+ const defaultFormatters = new WeakSet(Object.values(require_terminal.defaultTerminalTheme).filter((value) => typeof value === "function"));
20
22
  function formatTerminalTerm(term, theme = {}, quotes = false, usage) {
23
+ return formatTerminalLeaf(term, theme, quotes, usage).fragment;
24
+ }
25
+ /**
26
+ * Renders a semantic leaf and records the selected callback's provenance.
27
+ * @param term The semantic leaf to render.
28
+ * @param theme The optional role overrides.
29
+ * @param quotes Whether values should use their quoted representation.
30
+ * @param usage The leaf's position within a usage expression.
31
+ * @returns The fragment and whether its callback has default origin.
32
+ * @throws {TypeError} If the term is invalid or is a message-level hard break.
33
+ * @internal
34
+ */
35
+ function formatTerminalLeaf(term, theme = {}, quotes = false, usage) {
21
36
  const quote = (s) => quotes ? `\`${s}\`` : s;
22
37
  let text;
23
38
  switch (term.type) {
24
- case "text": return terminalText(term.text);
39
+ case "text": return {
40
+ fragment: terminalText(term.text),
41
+ defaultOrigin: true
42
+ };
25
43
  case "lineBreak": throw new TypeError("Hard breaks must be rendered at the message level.");
26
44
  case "optionName":
27
45
  text = quote(term.optionName);
@@ -65,19 +83,25 @@ function formatTerminalTerm(term, theme = {}, quotes = false, usage) {
65
83
  usage,
66
84
  format: (child) => formatTerminalTerm(child, theme, quotes, usage)
67
85
  };
86
+ function render(formatter, value) {
87
+ return {
88
+ fragment: formatter(value, ctx),
89
+ defaultOrigin: defaultFormatters.has(formatter)
90
+ };
91
+ }
68
92
  switch (term.type) {
69
- case "optionName": return (theme.optionName ?? require_terminal.defaultTerminalTheme.optionName)(term, ctx);
70
- case "optionNames": return (theme.optionNames ?? require_terminal.defaultTerminalTheme.optionNames)(term, ctx);
71
- case "metavar": return (theme.metavar ?? require_terminal.defaultTerminalTheme.metavar)(term, ctx);
72
- case "value": return (theme.value ?? require_terminal.defaultTerminalTheme.value)(term, ctx);
73
- case "values": return valuesFormatter(theme)(term, ctx);
74
- case "envVar": return (theme.envVar ?? require_terminal.defaultTerminalTheme.envVar)(term, ctx);
75
- case "commandLine": return (theme.commandLine ?? require_terminal.defaultTerminalTheme.commandLine)(term, ctx);
76
- case "url": return (theme.url ?? require_terminal.defaultTerminalTheme.url)(term, ctx);
77
- case "programName": return (theme.programName ?? require_terminal.defaultTerminalTheme.programName)(term, ctx);
78
- case "label": return (theme.label ?? require_terminal.defaultTerminalTheme.label)(term, ctx);
79
- case "syntaxPunctuation": return (theme.syntaxPunctuation ?? require_terminal.defaultTerminalTheme.syntaxPunctuation)(term, ctx);
80
- case "errorLabel": return (theme.errorLabel ?? require_terminal.defaultTerminalTheme.errorLabel)(term, ctx);
93
+ case "optionName": return render(theme.optionName ?? require_terminal.defaultTerminalTheme.optionName, term);
94
+ case "optionNames": return render(theme.optionNames ?? require_terminal.defaultTerminalTheme.optionNames, term);
95
+ case "metavar": return render(theme.metavar ?? require_terminal.defaultTerminalTheme.metavar, term);
96
+ case "value": return render(theme.value ?? require_terminal.defaultTerminalTheme.value, term);
97
+ case "values": return render(valuesFormatter(theme), term);
98
+ case "envVar": return render(theme.envVar ?? require_terminal.defaultTerminalTheme.envVar, term);
99
+ case "commandLine": return render(theme.commandLine ?? require_terminal.defaultTerminalTheme.commandLine, term);
100
+ case "url": return render(theme.url ?? require_terminal.defaultTerminalTheme.url, term);
101
+ case "programName": return render(theme.programName ?? require_terminal.defaultTerminalTheme.programName, term);
102
+ case "label": return render(theme.label ?? require_terminal.defaultTerminalTheme.label, term);
103
+ case "syntaxPunctuation": return render(theme.syntaxPunctuation ?? require_terminal.defaultTerminalTheme.syntaxPunctuation, term);
104
+ case "errorLabel": return render(theme.errorLabel ?? require_terminal.defaultTerminalTheme.errorLabel, term);
81
105
  }
82
106
  }
83
107
  const colorNames = [
@@ -225,14 +249,14 @@ function serializeTokens(tokens, colors = false, resetSuffix = "", ambient = {})
225
249
  * @since 1.3.0
226
250
  * @internal
227
251
  */
228
- function renderTerminalTerm(term, theme, colors, usage, ambient) {
229
- return serializeTokens(fragmentTokens(formatTerminalTerm(term, theme, false, usage)), colors, "", ambient);
252
+ function renderTerminalTerm(term, theme, colors, usage, ambient, resetSuffix = "") {
253
+ return serializeTokens(fragmentTokens(formatTerminalTerm(term, theme, false, usage)), colors, resetSuffix, ambient);
230
254
  }
231
255
  /** Memoizes semantic leaves for one layout pass, including measurement. */
232
256
  function cacheTerminalTheme(theme = {}) {
233
257
  function cache(formatter) {
234
258
  const fragments = /* @__PURE__ */ new Map();
235
- return (term, context) => {
259
+ const cached = (term, context) => {
236
260
  const key = JSON.stringify([
237
261
  term,
238
262
  context.quotes,
@@ -245,6 +269,8 @@ function cacheTerminalTheme(theme = {}) {
245
269
  }
246
270
  return fragment;
247
271
  };
272
+ if (defaultFormatters.has(formatter)) defaultFormatters.add(cached);
273
+ return cached;
248
274
  }
249
275
  return {
250
276
  optionName: cache(theme.optionName ?? require_terminal.defaultTerminalTheme.optionName),
@@ -268,6 +294,7 @@ function cacheTerminalTheme(theme = {}) {
268
294
 
269
295
  //#endregion
270
296
  exports.cacheTerminalTheme = cacheTerminalTheme;
297
+ exports.formatTerminalLeaf = formatTerminalLeaf;
271
298
  exports.formatTerminalTerm = formatTerminalTerm;
272
299
  exports.fragmentTokens = fragmentTokens;
273
300
  exports.renderTerminalTerm = renderTerminalTerm;
@@ -10,9 +10,29 @@ interface TerminalToken {
10
10
  readonly text: string;
11
11
  readonly width: number;
12
12
  readonly scopes: readonly Scope[];
13
+ /** Layout-owned space, removable beside an explicit hard break. */
14
+ readonly separator?: boolean;
15
+ /** The historical program separator wraps before its following atom. */
16
+ readonly programBoundary?: boolean;
17
+ /** Enables only the legacy default formatter whitespace behavior. */
18
+ readonly legacyWhitespace?: boolean;
13
19
  }
14
20
  declare function terminalText(text: string): TerminalFragment;
15
21
  declare function formatTerminalTerm(term: TerminalTerm, theme?: TerminalTheme, quotes?: boolean, usage?: TerminalFormatContext["usage"]): TerminalFragment;
22
+ /**
23
+ * Renders a semantic leaf and records the selected callback's provenance.
24
+ * @param term The semantic leaf to render.
25
+ * @param theme The optional role overrides.
26
+ * @param quotes Whether values should use their quoted representation.
27
+ * @param usage The leaf's position within a usage expression.
28
+ * @returns The fragment and whether its callback has default origin.
29
+ * @throws {TypeError} If the term is invalid or is a message-level hard break.
30
+ * @internal
31
+ */
32
+ declare function formatTerminalLeaf(term: TerminalTerm, theme?: TerminalTheme, quotes?: boolean, usage?: TerminalFormatContext["usage"]): {
33
+ readonly fragment: TerminalFragment;
34
+ readonly defaultOrigin: boolean;
35
+ };
16
36
  declare function styleCode(style: TerminalStyle): string;
17
37
  declare function fragmentTokens(fragment: TerminalFragment, scopes?: readonly Scope[], inherited?: TerminalStyle): Generator<TerminalToken>;
18
38
  declare function serializeTokens(tokens: Iterable<TerminalToken>, colors?: boolean, resetSuffix?: string, ambient?: TerminalStyle): string;
@@ -29,8 +49,8 @@ declare function serializeTokens(tokens: Iterable<TerminalToken>, colors?: boole
29
49
  * @since 1.3.0
30
50
  * @internal
31
51
  */
32
- declare function renderTerminalTerm(term: TerminalTerm, theme?: TerminalTheme, colors?: boolean, usage?: TerminalFormatContext["usage"], ambient?: TerminalStyle): string;
52
+ declare function renderTerminalTerm(term: TerminalTerm, theme?: TerminalTheme, colors?: boolean, usage?: TerminalFormatContext["usage"], ambient?: TerminalStyle, resetSuffix?: string): string;
33
53
  /** Memoizes semantic leaves for one layout pass, including measurement. */
34
54
  declare function cacheTerminalTheme(theme?: TerminalTheme): TerminalTheme;
35
55
  //#endregion
36
- export { Scope, TerminalToken, cacheTerminalTheme, formatTerminalTerm, fragmentTokens, renderTerminalTerm, serializeTokens, styleCode, terminalText };
56
+ export { Scope, TerminalToken, cacheTerminalTheme, formatTerminalLeaf, formatTerminalTerm, fragmentTokens, renderTerminalTerm, serializeTokens, styleCode, terminalText };
@@ -10,9 +10,29 @@ interface TerminalToken {
10
10
  readonly text: string;
11
11
  readonly width: number;
12
12
  readonly scopes: readonly Scope[];
13
+ /** Layout-owned space, removable beside an explicit hard break. */
14
+ readonly separator?: boolean;
15
+ /** The historical program separator wraps before its following atom. */
16
+ readonly programBoundary?: boolean;
17
+ /** Enables only the legacy default formatter whitespace behavior. */
18
+ readonly legacyWhitespace?: boolean;
13
19
  }
14
20
  declare function terminalText(text: string): TerminalFragment;
15
21
  declare function formatTerminalTerm(term: TerminalTerm, theme?: TerminalTheme, quotes?: boolean, usage?: TerminalFormatContext["usage"]): TerminalFragment;
22
+ /**
23
+ * Renders a semantic leaf and records the selected callback's provenance.
24
+ * @param term The semantic leaf to render.
25
+ * @param theme The optional role overrides.
26
+ * @param quotes Whether values should use their quoted representation.
27
+ * @param usage The leaf's position within a usage expression.
28
+ * @returns The fragment and whether its callback has default origin.
29
+ * @throws {TypeError} If the term is invalid or is a message-level hard break.
30
+ * @internal
31
+ */
32
+ declare function formatTerminalLeaf(term: TerminalTerm, theme?: TerminalTheme, quotes?: boolean, usage?: TerminalFormatContext["usage"]): {
33
+ readonly fragment: TerminalFragment;
34
+ readonly defaultOrigin: boolean;
35
+ };
16
36
  declare function styleCode(style: TerminalStyle): string;
17
37
  declare function fragmentTokens(fragment: TerminalFragment, scopes?: readonly Scope[], inherited?: TerminalStyle): Generator<TerminalToken>;
18
38
  declare function serializeTokens(tokens: Iterable<TerminalToken>, colors?: boolean, resetSuffix?: string, ambient?: TerminalStyle): string;
@@ -29,8 +49,8 @@ declare function serializeTokens(tokens: Iterable<TerminalToken>, colors?: boole
29
49
  * @since 1.3.0
30
50
  * @internal
31
51
  */
32
- declare function renderTerminalTerm(term: TerminalTerm, theme?: TerminalTheme, colors?: boolean, usage?: TerminalFormatContext["usage"], ambient?: TerminalStyle): string;
52
+ declare function renderTerminalTerm(term: TerminalTerm, theme?: TerminalTheme, colors?: boolean, usage?: TerminalFormatContext["usage"], ambient?: TerminalStyle, resetSuffix?: string): string;
33
53
  /** Memoizes semantic leaves for one layout pass, including measurement. */
34
54
  declare function cacheTerminalTheme(theme?: TerminalTheme): TerminalTheme;
35
55
  //#endregion
36
- export { Scope, TerminalToken, cacheTerminalTheme, formatTerminalTerm, fragmentTokens, renderTerminalTerm, serializeTokens, styleCode, terminalText };
56
+ export { Scope, TerminalToken, cacheTerminalTheme, formatTerminalLeaf, formatTerminalTerm, fragmentTokens, renderTerminalTerm, serializeTokens, styleCode, terminalText };