@optique/core 1.3.0-dev.2565 → 1.3.0-dev.2567

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.
@@ -1,7 +1,7 @@
1
1
  const require_internal_annotations = require('./internal/annotations.cjs');
2
- const require_message = require('./message.cjs');
3
2
  const require_validate = require('./validate.cjs');
4
3
  const require_usage = require('./usage.cjs');
4
+ const require_message = require('./message.cjs');
5
5
  const require_doc = require('./doc.cjs');
6
6
  const require_mode_dispatch = require('./internal/mode-dispatch.cjs');
7
7
  const require_internal_dependency = require('./internal/dependency.cjs');
@@ -1,7 +1,7 @@
1
1
  import { annotateFreshArray, getAnnotations, inheritAnnotations } from "./internal/annotations.js";
2
- import { message, optionName, text, values } from "./message.js";
3
2
  import { validateLabel } from "./validate.js";
4
3
  import { extractArgumentMetavars, extractCommandNames, extractOptionNames, isDocHidden, mergeHidden } from "./usage.js";
4
+ import { message, optionName, text, values } from "./message.js";
5
5
  import { deduplicateDocFragments } from "./doc.js";
6
6
  import { dispatchByMode, dispatchIterableByMode } from "./internal/mode-dispatch.js";
7
7
  import { createDependencySourceState, dependencyId, isDependencySourceState, isPendingDependencySourceState, isWrappedDependencySource, wrappedDependencySourceMarker } from "./internal/dependency.js";
package/dist/doc.cjs CHANGED
@@ -1,10 +1,11 @@
1
- const require_message_registry = require('./message-registry.cjs');
2
1
  const require_terminal_internal = require('./terminal-internal.cjs');
3
2
  const require_text_layout = require('./text-layout.cjs');
4
- const require_message = require('./message.cjs');
5
- const require_message_renderer = require('./message-renderer.cjs');
6
3
  const require_validate = require('./validate.cjs');
7
4
  const require_usage = require('./usage.cjs');
5
+ const require_terminal_width = require('./terminal-width.cjs');
6
+ const require_message_registry = require('./message-registry.cjs');
7
+ const require_message = require('./message.cjs');
8
+ const require_message_renderer = require('./message-renderer.cjs');
8
9
 
9
10
  //#region src/doc.ts
10
11
  /**
@@ -322,11 +323,7 @@ function formatDocPage(programName, page, options = {}) {
322
323
  const splitEntryMin = termIndent + 2 + Math.max(2, 2 * minDescWidth - 1);
323
324
  const fixedEntryMin = termIndent + 2 + termWidth + minDescWidth;
324
325
  const entryMin = needsDescColumn ? Math.min(splitEntryMin, fixedEntryMin) : hasEntries ? termIndent + 1 : 1;
325
- const programNameWidth = page.usage != null && showUsage ? Math.max(0, ...[...require_terminal_internal.fragmentTokens(require_terminal_internal.formatTerminalTerm({
326
- type: "programName",
327
- programName
328
- }, options.theme))].map((token) => token.width)) : 0;
329
- const usageMin = page.usage != null && showUsage ? Math.max(require_text_layout.measureText(usageLabel).maxLineWidth, usageLabelWidth + Math.max(programNameWidth, Math.min(maxVisibleAtomicWidth(page.usage, options.theme), programNameWidth + usageLabelWidth))) : 1;
326
+ const usageMin = page.usage != null && showUsage ? require_terminal_width.minimumUsageWidth(programName, page.usage, usageLabel, options.theme) : 1;
330
327
  let sectionMin = 1;
331
328
  for (const kind of [
332
329
  "examples",
@@ -338,16 +335,29 @@ function formatDocPage(programName, page, options = {}) {
338
335
  sectionMin = Math.max(sectionMin, 3, require_text_layout.measureText(label(title, kind)).maxLineWidth);
339
336
  }
340
337
  const minWidth = Math.max(entryMin, usageMin, sectionMin);
341
- if (options.maxWidth < minWidth) throw new RangeError(`maxWidth must be at least ${minWidth}, got ${options.maxWidth}.`);
342
- if (needsDescColumn && minDescWidth > 1) {
338
+ if (options.maxWidth < minWidth) {
339
+ if (!require_terminal_width.hasAutomaticWidth(options)) throw new RangeError(`maxWidth must be at least ${minWidth}, got ${options.maxWidth}.`);
340
+ options = {
341
+ ...options,
342
+ maxWidth: void 0
343
+ };
344
+ effectiveTermWidth = termWidth;
345
+ }
346
+ if (options.maxWidth != null && needsDescColumn && minDescWidth > 1) {
343
347
  const avail = options.maxWidth - termIndent - 2;
344
348
  const descW = avail - effectiveTermWidth;
345
349
  if (descW < minDescWidth) {
346
350
  const needed = termIndent + effectiveTermWidth + 2 + minDescWidth;
347
- throw new RangeError(`maxWidth must be at least ${needed}, got ${options.maxWidth}.`);
351
+ if (!require_terminal_width.hasAutomaticWidth(options)) throw new RangeError(`maxWidth must be at least ${needed}, got ${options.maxWidth}.`);
352
+ options = {
353
+ ...options,
354
+ maxWidth: void 0
355
+ };
356
+ effectiveTermWidth = termWidth;
348
357
  }
349
358
  }
350
359
  }
360
+ require_terminal_width.reportAutomaticWidth(options, options.maxWidth);
351
361
  let output = "";
352
362
  if (hasContent(page.brief)) {
353
363
  output += formatMessage(page.brief, {
@@ -508,18 +518,6 @@ function formatDocPage(programName, page, options = {}) {
508
518
  function indentLines(text$1, indent) {
509
519
  return text$1.split("\n").join("\n" + " ".repeat(indent));
510
520
  }
511
- /**
512
- * Returns the width of the widest non-breakable segment among visible
513
- * (non-usage-hidden) terms in a usage tree. Hidden terms are excluded
514
- * because they are filtered out before rendering, so they do not
515
- * contribute to the rendered width.
516
- */
517
- function maxVisibleAtomicWidth(usage, theme) {
518
- return usage.reduce((widest, term) => Math.max(widest, require_text_layout.measureText(require_usage.formatUsageTerm(term, {
519
- theme,
520
- maxWidth: 1
521
- })).maxLineWidth), 0);
522
- }
523
521
  function ansiAwareRightPad(text$1, length, char = " ") {
524
522
  const visibleWidth = require_text_layout.measureText(text$1).lastLineWidth;
525
523
  if (visibleWidth >= length) return text$1;
package/dist/doc.js CHANGED
@@ -1,10 +1,11 @@
1
- import { messageRenderers } from "./message-registry.js";
2
- import { cacheTerminalTheme, formatTerminalTerm, fragmentTokens, renderTerminalTerm, styleCode } from "./terminal-internal.js";
1
+ import { cacheTerminalTheme, renderTerminalTerm, styleCode } from "./terminal-internal.js";
3
2
  import { measureAnnotation, measureText, placeText, spaceAfterLabel } from "./text-layout.js";
4
- import { cloneMessage, text } from "./message.js";
5
- import { resolveMessageFormatter } from "./message-renderer.js";
6
3
  import { validateLabel, validateProgramName } from "./validate.js";
7
4
  import { cloneUsageTerm, formatUsage, formatUsageTerm, isDocHidden } from "./usage.js";
5
+ import { hasAutomaticWidth, minimumUsageWidth, reportAutomaticWidth } from "./terminal-width.js";
6
+ import { messageRenderers } from "./message-registry.js";
7
+ import { cloneMessage, text } from "./message.js";
8
+ import { resolveMessageFormatter } from "./message-renderer.js";
8
9
 
9
10
  //#region src/doc.ts
10
11
  /**
@@ -322,11 +323,7 @@ function formatDocPage(programName, page, options = {}) {
322
323
  const splitEntryMin = termIndent + 2 + Math.max(2, 2 * minDescWidth - 1);
323
324
  const fixedEntryMin = termIndent + 2 + termWidth + minDescWidth;
324
325
  const entryMin = needsDescColumn ? Math.min(splitEntryMin, fixedEntryMin) : hasEntries ? termIndent + 1 : 1;
325
- const programNameWidth = page.usage != null && showUsage ? Math.max(0, ...[...fragmentTokens(formatTerminalTerm({
326
- type: "programName",
327
- programName
328
- }, options.theme))].map((token) => token.width)) : 0;
329
- const usageMin = page.usage != null && showUsage ? Math.max(measureText(usageLabel).maxLineWidth, usageLabelWidth + Math.max(programNameWidth, Math.min(maxVisibleAtomicWidth(page.usage, options.theme), programNameWidth + usageLabelWidth))) : 1;
326
+ const usageMin = page.usage != null && showUsage ? minimumUsageWidth(programName, page.usage, usageLabel, options.theme) : 1;
330
327
  let sectionMin = 1;
331
328
  for (const kind of [
332
329
  "examples",
@@ -338,16 +335,29 @@ function formatDocPage(programName, page, options = {}) {
338
335
  sectionMin = Math.max(sectionMin, 3, measureText(label(title, kind)).maxLineWidth);
339
336
  }
340
337
  const minWidth = Math.max(entryMin, usageMin, sectionMin);
341
- if (options.maxWidth < minWidth) throw new RangeError(`maxWidth must be at least ${minWidth}, got ${options.maxWidth}.`);
342
- if (needsDescColumn && minDescWidth > 1) {
338
+ if (options.maxWidth < minWidth) {
339
+ if (!hasAutomaticWidth(options)) throw new RangeError(`maxWidth must be at least ${minWidth}, got ${options.maxWidth}.`);
340
+ options = {
341
+ ...options,
342
+ maxWidth: void 0
343
+ };
344
+ effectiveTermWidth = termWidth;
345
+ }
346
+ if (options.maxWidth != null && needsDescColumn && minDescWidth > 1) {
343
347
  const avail = options.maxWidth - termIndent - 2;
344
348
  const descW = avail - effectiveTermWidth;
345
349
  if (descW < minDescWidth) {
346
350
  const needed = termIndent + effectiveTermWidth + 2 + minDescWidth;
347
- throw new RangeError(`maxWidth must be at least ${needed}, got ${options.maxWidth}.`);
351
+ if (!hasAutomaticWidth(options)) throw new RangeError(`maxWidth must be at least ${needed}, got ${options.maxWidth}.`);
352
+ options = {
353
+ ...options,
354
+ maxWidth: void 0
355
+ };
356
+ effectiveTermWidth = termWidth;
348
357
  }
349
358
  }
350
359
  }
360
+ reportAutomaticWidth(options, options.maxWidth);
351
361
  let output = "";
352
362
  if (hasContent(page.brief)) {
353
363
  output += formatMessage(page.brief, {
@@ -508,18 +518,6 @@ function formatDocPage(programName, page, options = {}) {
508
518
  function indentLines(text$1, indent) {
509
519
  return text$1.split("\n").join("\n" + " ".repeat(indent));
510
520
  }
511
- /**
512
- * Returns the width of the widest non-breakable segment among visible
513
- * (non-usage-hidden) terms in a usage tree. Hidden terms are excluded
514
- * because they are filtered out before rendering, so they do not
515
- * contribute to the rendered width.
516
- */
517
- function maxVisibleAtomicWidth(usage, theme) {
518
- return usage.reduce((widest, term) => Math.max(widest, measureText(formatUsageTerm(term, {
519
- theme,
520
- maxWidth: 1
521
- })).maxLineWidth), 0);
522
- }
523
521
  function ansiAwareRightPad(text$1, length, char = " ") {
524
522
  const visibleWidth = measureText(text$1).lastLineWidth;
525
523
  if (visibleWidth >= length) return text$1;
package/dist/facade.cjs CHANGED
@@ -1,10 +1,11 @@
1
1
  const require_internal_annotations = require('./internal/annotations.cjs');
2
2
  const require_terminal_internal = require('./terminal-internal.cjs');
3
3
  const require_text_layout = require('./text-layout.cjs');
4
- const require_message = require('./message.cjs');
5
- const require_message_renderer = require('./message-renderer.cjs');
6
4
  const require_validate = require('./validate.cjs');
7
5
  const require_usage = require('./usage.cjs');
6
+ const require_terminal_width = require('./terminal-width.cjs');
7
+ const require_message = require('./message.cjs');
8
+ const require_message_renderer = require('./message-renderer.cjs');
8
9
  const require_doc = require('./doc.cjs');
9
10
  const require_mode_dispatch = require('./internal/mode-dispatch.cjs');
10
11
  const require_dependency_runtime = require('./dependency-runtime.cjs');
@@ -516,24 +517,34 @@ function classifyParseFailure(failure, helpOptionNames, helpCommandNames, versio
516
517
  * Handles shell completion requests.
517
518
  * @since 0.6.0
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
+ function handleCompletion(completionArgs, programName, parser, completionParser, stdout, stderr, onCompletion, onError, availableShells, colors, maxWidth, termWidth, completionCommandDisplayName, completionOptionDisplayName, isOptionMode, sectionOrder, showUsage, rootOptionSuggestions = [], messageFormatter, theme, automaticWidth = false) {
521
+ const errorOptions = (format) => automaticWidth ? require_terminal_width.withAutomaticWidth(format, (width) => {
522
+ maxWidth = width;
523
+ }) : format;
520
524
  const shellName = completionArgs[0] || "";
521
525
  const args = completionArgs.slice(1);
522
526
  const callOnError = (code, error) => onError(code, error);
523
527
  const callOnCompletion = (code) => onCompletion(code);
524
528
  if (!shellName) {
525
529
  const error = require_message.message`Missing shell name for completion.`;
526
- stderr(require_message_renderer.renderErrorMessage(error, {
530
+ const writeError = () => stderr(require_message_renderer.renderErrorMessage(error, errorOptions({
527
531
  messageFormatter,
528
532
  theme,
529
533
  colors,
530
534
  quotes: !colors,
531
535
  maxWidth
532
- }) + "\n");
536
+ })) + "\n");
537
+ if (!automaticWidth) writeError();
538
+ let wroteError = !automaticWidth;
539
+ const completionDocOptions = (format) => automaticWidth ? require_terminal_width.withAutomaticWidth(format, (width) => {
540
+ maxWidth = width;
541
+ writeError();
542
+ wroteError = true;
543
+ }) : format;
533
544
  if (completionParser) {
534
545
  const displayName = isOptionMode ? completionOptionDisplayName ?? "--completion" : completionCommandDisplayName ?? "completion";
535
546
  const doc = require_internal_parser.getDocPage(completionParser, [displayName]);
536
- if (doc) stderr(require_doc.formatDocPage(programName, doc, {
547
+ if (doc) stderr(require_doc.formatDocPage(programName, doc, completionDocOptions({
537
548
  messageFormatter,
538
549
  theme,
539
550
  colors,
@@ -541,8 +552,9 @@ function handleCompletion(completionArgs, programName, parser, completionParser,
541
552
  termWidth,
542
553
  sectionOrder,
543
554
  showUsage
544
- }));
555
+ })));
545
556
  }
557
+ if (!wroteError) writeError();
546
558
  return require_mode_dispatch.dispatchByMode(parser.mode, () => {
547
559
  const result = callOnError(1, error);
548
560
  if (result instanceof Promise) throw new RunParserError("Synchronous parser returned async result.");
@@ -557,13 +569,13 @@ function handleCompletion(completionArgs, programName, parser, completionParser,
557
569
  available.push(require_message.value(name));
558
570
  }
559
571
  const error = require_message.message`Unsupported shell ${shellName}. Available shells: ${available}.`;
560
- stderr(require_message_renderer.renderErrorMessage(error, {
572
+ stderr(require_message_renderer.renderErrorMessage(error, errorOptions({
561
573
  messageFormatter,
562
574
  theme,
563
575
  colors,
564
576
  quotes: !colors,
565
577
  maxWidth
566
- }));
578
+ })));
567
579
  return require_mode_dispatch.dispatchByMode(parser.mode, () => {
568
580
  const result = callOnError(1, error);
569
581
  if (result instanceof Promise) throw new RunParserError("Synchronous parser returned async result.");
@@ -871,9 +883,18 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
871
883
  options = optionsParam ?? {};
872
884
  }
873
885
  require_validate.validateProgramName(programName);
874
- const { colors, messageFormatter, theme, maxWidth, termWidth, showDefault, showChoices, sectionOrder, showUsage, usageLine, commandList = "recursive", aboveError = "usage", onError = () => {
886
+ const { colors, messageFormatter, theme: requestedTheme, maxWidth: requestedMaxWidth, termWidth, showDefault, showChoices, sectionOrder, showUsage, usageLine, commandList = "recursive", aboveError = "usage", onError = () => {
875
887
  throw new RunParserError("Failed to parse command line arguments.");
876
888
  }, stderr = console.error, stdout = console.log, brief, description, examples, author, bugs, footer } = options;
889
+ let maxWidth = requestedMaxWidth;
890
+ const automaticWidth = require_terminal_width.hasAutomaticWidth(options);
891
+ const theme = automaticWidth ? require_terminal_internal.cacheTerminalTheme(requestedTheme) : requestedTheme;
892
+ const formatOptions = (format) => automaticWidth ? require_terminal_width.withAutomaticWidth(format, (width) => {
893
+ maxWidth = width;
894
+ }) : format;
895
+ const resolveUsageWidth = (usage) => {
896
+ if (automaticWidth && maxWidth != null && maxWidth < require_terminal_width.minimumUsageWidth(programName, usage, usageLabel(), theme)) maxWidth = void 0;
897
+ };
877
898
  let usagePrefix;
878
899
  const usageLabel = () => usagePrefix ??= require_text_layout.spaceAfterLabel(require_terminal_internal.renderTerminalTerm({
879
900
  type: "label",
@@ -1016,7 +1037,7 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
1016
1037
  classified.shell,
1017
1038
  ...classified.commandPath ?? [],
1018
1039
  ...classified.args
1019
- ], programName, parser, classified.source === "command" ? completionParsers.completionCommand : completionParsers.completionOption, stdout, stderr, onCompletionResult, onErrorResult, availableShells, colors, maxWidth, termWidth, completionCommandNames[0], completionOptionNames[0], classified.source === "option", sectionOrder, showUsage, rootOptionSuggestions, messageFormatter, theme);
1040
+ ], programName, parser, classified.source === "command" ? completionParsers.completionCommand : completionParsers.completionOption, stdout, stderr, onCompletionResult, onErrorResult, availableShells, colors, maxWidth, termWidth, completionCommandNames[0], completionOptionNames[0], classified.source === "option", sectionOrder, showUsage, rootOptionSuggestions, messageFormatter, theme, automaticWidth);
1020
1041
  case "help": {
1021
1042
  let helpGeneratorParser;
1022
1043
  let docGeneratorParser;
@@ -1035,19 +1056,20 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
1035
1056
  docGeneratorParser = classified.commands.length > 0 ? parser : helpGeneratorParser;
1036
1057
  }
1037
1058
  const reportInvalidHelpCommand = (validationError) => {
1059
+ resolveUsageWidth(augmentedParser.usage);
1038
1060
  stderr(`${usageLabel()}${indentLines(require_usage.formatUsage(programName, augmentedParser.usage, {
1039
1061
  theme,
1040
1062
  colors,
1041
1063
  maxWidth: maxWidth == null ? void 0 : maxWidth - usageLabelWidth(),
1042
1064
  expandCommands: true
1043
1065
  }), usageLabelWidth())}`);
1044
- stderr(require_message_renderer.renderErrorMessage(validationError, {
1066
+ stderr(require_message_renderer.renderErrorMessage(validationError, formatOptions({
1045
1067
  messageFormatter,
1046
1068
  theme,
1047
1069
  colors,
1048
1070
  quotes: !colors,
1049
1071
  maxWidth
1050
- }));
1072
+ })));
1051
1073
  return onError(1, validationError);
1052
1074
  };
1053
1075
  const displayHelp = (doc) => {
@@ -1066,7 +1088,7 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
1066
1088
  footer: shouldOverride ? footer ?? doc.footer : doc.footer ?? footer
1067
1089
  }, commandList, isTopLevel);
1068
1090
  const renderedDoc = isTopLevel && usageLine != null ? applyUsageLine(augmentedDoc, usageLine) : augmentedDoc;
1069
- stdout(require_doc.formatDocPage(programName, renderedDoc, {
1091
+ stdout(require_doc.formatDocPage(programName, renderedDoc, formatOptions({
1070
1092
  messageFormatter,
1071
1093
  theme,
1072
1094
  colors,
@@ -1076,7 +1098,7 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
1076
1098
  showChoices,
1077
1099
  sectionOrder,
1078
1100
  showUsage
1079
- }));
1101
+ })));
1080
1102
  return onHelp(0, renderedDoc);
1081
1103
  }
1082
1104
  throw new RunParserError("Failed to generate help page.");
@@ -1142,7 +1164,7 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
1142
1164
  }, commandList, isTopLevel);
1143
1165
  const defaultRootUsage = typeof usageLine === "function" && (options.help || options.version || options.completion) ? require_usage.normalizeUsage(getRootHelpGeneratorParser().usage) : augmentedDoc.usage ?? [];
1144
1166
  const renderedDoc = isTopLevel && usageLine != null ? applyUsageLine(augmentedDoc, usageLine, defaultRootUsage) : augmentedDoc;
1145
- stderr(require_doc.formatDocPage(programName, renderedDoc, {
1167
+ stderr(require_doc.formatDocPage(programName, renderedDoc, formatOptions({
1146
1168
  messageFormatter,
1147
1169
  theme,
1148
1170
  colors,
@@ -1152,21 +1174,24 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
1152
1174
  showChoices,
1153
1175
  sectionOrder,
1154
1176
  showUsage
1155
- }));
1177
+ })));
1156
1178
  }
1157
- if (effectiveAboveError === "usage") stderr(`${usageLabel()}${indentLines(require_usage.formatUsage(programName, augmentedParser.usage, {
1158
- theme,
1159
- colors,
1160
- maxWidth: maxWidth == null ? void 0 : maxWidth - usageLabelWidth(),
1161
- expandCommands: true
1162
- }), usageLabelWidth())}`);
1163
- stderr(require_message_renderer.renderErrorMessage(classified.error, {
1179
+ if (effectiveAboveError === "usage") {
1180
+ resolveUsageWidth(augmentedParser.usage);
1181
+ stderr(`${usageLabel()}${indentLines(require_usage.formatUsage(programName, augmentedParser.usage, {
1182
+ theme,
1183
+ colors,
1184
+ maxWidth: maxWidth == null ? void 0 : maxWidth - usageLabelWidth(),
1185
+ expandCommands: true
1186
+ }), usageLabelWidth())}`);
1187
+ }
1188
+ stderr(require_message_renderer.renderErrorMessage(classified.error, formatOptions({
1164
1189
  messageFormatter,
1165
1190
  theme,
1166
1191
  colors,
1167
1192
  quotes: !colors,
1168
1193
  maxWidth
1169
- }));
1194
+ })));
1170
1195
  return onError(1, classified.error);
1171
1196
  };
1172
1197
  if (aboveError === "help") {
package/dist/facade.js CHANGED
@@ -1,10 +1,11 @@
1
1
  import { injectAnnotations } from "./internal/annotations.js";
2
- import { renderTerminalTerm } from "./terminal-internal.js";
2
+ import { cacheTerminalTheme, renderTerminalTerm } from "./terminal-internal.js";
3
3
  import { measureText, spaceAfterLabel } from "./text-layout.js";
4
- import { commandLine, lineBreak, message, optionName, text, value } from "./message.js";
5
- import { renderErrorMessage } from "./message-renderer.js";
6
4
  import { validateCommandNames, validateContextIds, validateMetaNameCollisions, validateOptionNames, validateProgramName } from "./validate.js";
7
5
  import { cloneUsage, formatUsage, isSuggestionHidden, normalizeUsage } from "./usage.js";
6
+ import { hasAutomaticWidth, minimumUsageWidth, withAutomaticWidth } from "./terminal-width.js";
7
+ import { commandLine, lineBreak, message, optionName, text, value } from "./message.js";
8
+ import { renderErrorMessage } from "./message-renderer.js";
8
9
  import { formatDocPage } from "./doc.js";
9
10
  import { dispatchByMode } from "./internal/mode-dispatch.js";
10
11
  import { collectExplicitSourceValues, collectExplicitSourceValuesAsync, createDependencyRuntimeContext } from "./dependency-runtime.js";
@@ -516,24 +517,34 @@ function classifyParseFailure(failure, helpOptionNames, helpCommandNames, versio
516
517
  * Handles shell completion requests.
517
518
  * @since 0.6.0
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
+ function handleCompletion(completionArgs, programName, parser, completionParser, stdout, stderr, onCompletion, onError, availableShells, colors, maxWidth, termWidth, completionCommandDisplayName, completionOptionDisplayName, isOptionMode, sectionOrder, showUsage, rootOptionSuggestions = [], messageFormatter, theme, automaticWidth = false) {
521
+ const errorOptions = (format) => automaticWidth ? withAutomaticWidth(format, (width) => {
522
+ maxWidth = width;
523
+ }) : format;
520
524
  const shellName = completionArgs[0] || "";
521
525
  const args = completionArgs.slice(1);
522
526
  const callOnError = (code, error) => onError(code, error);
523
527
  const callOnCompletion = (code) => onCompletion(code);
524
528
  if (!shellName) {
525
529
  const error = message`Missing shell name for completion.`;
526
- stderr(renderErrorMessage(error, {
530
+ const writeError = () => stderr(renderErrorMessage(error, errorOptions({
527
531
  messageFormatter,
528
532
  theme,
529
533
  colors,
530
534
  quotes: !colors,
531
535
  maxWidth
532
- }) + "\n");
536
+ })) + "\n");
537
+ if (!automaticWidth) writeError();
538
+ let wroteError = !automaticWidth;
539
+ const completionDocOptions = (format) => automaticWidth ? withAutomaticWidth(format, (width) => {
540
+ maxWidth = width;
541
+ writeError();
542
+ wroteError = true;
543
+ }) : format;
533
544
  if (completionParser) {
534
545
  const displayName = isOptionMode ? completionOptionDisplayName ?? "--completion" : completionCommandDisplayName ?? "completion";
535
546
  const doc = getDocPage(completionParser, [displayName]);
536
- if (doc) stderr(formatDocPage(programName, doc, {
547
+ if (doc) stderr(formatDocPage(programName, doc, completionDocOptions({
537
548
  messageFormatter,
538
549
  theme,
539
550
  colors,
@@ -541,8 +552,9 @@ function handleCompletion(completionArgs, programName, parser, completionParser,
541
552
  termWidth,
542
553
  sectionOrder,
543
554
  showUsage
544
- }));
555
+ })));
545
556
  }
557
+ if (!wroteError) writeError();
546
558
  return dispatchByMode(parser.mode, () => {
547
559
  const result = callOnError(1, error);
548
560
  if (result instanceof Promise) throw new RunParserError("Synchronous parser returned async result.");
@@ -557,13 +569,13 @@ function handleCompletion(completionArgs, programName, parser, completionParser,
557
569
  available.push(value(name));
558
570
  }
559
571
  const error = message`Unsupported shell ${shellName}. Available shells: ${available}.`;
560
- stderr(renderErrorMessage(error, {
572
+ stderr(renderErrorMessage(error, errorOptions({
561
573
  messageFormatter,
562
574
  theme,
563
575
  colors,
564
576
  quotes: !colors,
565
577
  maxWidth
566
- }));
578
+ })));
567
579
  return dispatchByMode(parser.mode, () => {
568
580
  const result = callOnError(1, error);
569
581
  if (result instanceof Promise) throw new RunParserError("Synchronous parser returned async result.");
@@ -871,9 +883,18 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
871
883
  options = optionsParam ?? {};
872
884
  }
873
885
  validateProgramName(programName);
874
- const { colors, messageFormatter, theme, maxWidth, termWidth, showDefault, showChoices, sectionOrder, showUsage, usageLine, commandList = "recursive", aboveError = "usage", onError = () => {
886
+ const { colors, messageFormatter, theme: requestedTheme, maxWidth: requestedMaxWidth, termWidth, showDefault, showChoices, sectionOrder, showUsage, usageLine, commandList = "recursive", aboveError = "usage", onError = () => {
875
887
  throw new RunParserError("Failed to parse command line arguments.");
876
888
  }, stderr = console.error, stdout = console.log, brief, description, examples, author, bugs, footer } = options;
889
+ let maxWidth = requestedMaxWidth;
890
+ const automaticWidth = hasAutomaticWidth(options);
891
+ const theme = automaticWidth ? cacheTerminalTheme(requestedTheme) : requestedTheme;
892
+ const formatOptions = (format) => automaticWidth ? withAutomaticWidth(format, (width) => {
893
+ maxWidth = width;
894
+ }) : format;
895
+ const resolveUsageWidth = (usage) => {
896
+ if (automaticWidth && maxWidth != null && maxWidth < minimumUsageWidth(programName, usage, usageLabel(), theme)) maxWidth = void 0;
897
+ };
877
898
  let usagePrefix;
878
899
  const usageLabel = () => usagePrefix ??= spaceAfterLabel(renderTerminalTerm({
879
900
  type: "label",
@@ -1016,7 +1037,7 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
1016
1037
  classified.shell,
1017
1038
  ...classified.commandPath ?? [],
1018
1039
  ...classified.args
1019
- ], programName, parser, classified.source === "command" ? completionParsers.completionCommand : completionParsers.completionOption, stdout, stderr, onCompletionResult, onErrorResult, availableShells, colors, maxWidth, termWidth, completionCommandNames[0], completionOptionNames[0], classified.source === "option", sectionOrder, showUsage, rootOptionSuggestions, messageFormatter, theme);
1040
+ ], programName, parser, classified.source === "command" ? completionParsers.completionCommand : completionParsers.completionOption, stdout, stderr, onCompletionResult, onErrorResult, availableShells, colors, maxWidth, termWidth, completionCommandNames[0], completionOptionNames[0], classified.source === "option", sectionOrder, showUsage, rootOptionSuggestions, messageFormatter, theme, automaticWidth);
1020
1041
  case "help": {
1021
1042
  let helpGeneratorParser;
1022
1043
  let docGeneratorParser;
@@ -1035,19 +1056,20 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
1035
1056
  docGeneratorParser = classified.commands.length > 0 ? parser : helpGeneratorParser;
1036
1057
  }
1037
1058
  const reportInvalidHelpCommand = (validationError) => {
1059
+ resolveUsageWidth(augmentedParser.usage);
1038
1060
  stderr(`${usageLabel()}${indentLines(formatUsage(programName, augmentedParser.usage, {
1039
1061
  theme,
1040
1062
  colors,
1041
1063
  maxWidth: maxWidth == null ? void 0 : maxWidth - usageLabelWidth(),
1042
1064
  expandCommands: true
1043
1065
  }), usageLabelWidth())}`);
1044
- stderr(renderErrorMessage(validationError, {
1066
+ stderr(renderErrorMessage(validationError, formatOptions({
1045
1067
  messageFormatter,
1046
1068
  theme,
1047
1069
  colors,
1048
1070
  quotes: !colors,
1049
1071
  maxWidth
1050
- }));
1072
+ })));
1051
1073
  return onError(1, validationError);
1052
1074
  };
1053
1075
  const displayHelp = (doc) => {
@@ -1066,7 +1088,7 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
1066
1088
  footer: shouldOverride ? footer ?? doc.footer : doc.footer ?? footer
1067
1089
  }, commandList, isTopLevel);
1068
1090
  const renderedDoc = isTopLevel && usageLine != null ? applyUsageLine(augmentedDoc, usageLine) : augmentedDoc;
1069
- stdout(formatDocPage(programName, renderedDoc, {
1091
+ stdout(formatDocPage(programName, renderedDoc, formatOptions({
1070
1092
  messageFormatter,
1071
1093
  theme,
1072
1094
  colors,
@@ -1076,7 +1098,7 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
1076
1098
  showChoices,
1077
1099
  sectionOrder,
1078
1100
  showUsage
1079
- }));
1101
+ })));
1080
1102
  return onHelp(0, renderedDoc);
1081
1103
  }
1082
1104
  throw new RunParserError("Failed to generate help page.");
@@ -1142,7 +1164,7 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
1142
1164
  }, commandList, isTopLevel);
1143
1165
  const defaultRootUsage = typeof usageLine === "function" && (options.help || options.version || options.completion) ? normalizeUsage(getRootHelpGeneratorParser().usage) : augmentedDoc.usage ?? [];
1144
1166
  const renderedDoc = isTopLevel && usageLine != null ? applyUsageLine(augmentedDoc, usageLine, defaultRootUsage) : augmentedDoc;
1145
- stderr(formatDocPage(programName, renderedDoc, {
1167
+ stderr(formatDocPage(programName, renderedDoc, formatOptions({
1146
1168
  messageFormatter,
1147
1169
  theme,
1148
1170
  colors,
@@ -1152,21 +1174,24 @@ function runParser(parserOrProgram, programNameOrArgs, argsOrOptions, optionsPar
1152
1174
  showChoices,
1153
1175
  sectionOrder,
1154
1176
  showUsage
1155
- }));
1177
+ })));
1156
1178
  }
1157
- if (effectiveAboveError === "usage") stderr(`${usageLabel()}${indentLines(formatUsage(programName, augmentedParser.usage, {
1158
- theme,
1159
- colors,
1160
- maxWidth: maxWidth == null ? void 0 : maxWidth - usageLabelWidth(),
1161
- expandCommands: true
1162
- }), usageLabelWidth())}`);
1163
- stderr(renderErrorMessage(classified.error, {
1179
+ if (effectiveAboveError === "usage") {
1180
+ resolveUsageWidth(augmentedParser.usage);
1181
+ stderr(`${usageLabel()}${indentLines(formatUsage(programName, augmentedParser.usage, {
1182
+ theme,
1183
+ colors,
1184
+ maxWidth: maxWidth == null ? void 0 : maxWidth - usageLabelWidth(),
1185
+ expandCommands: true
1186
+ }), usageLabelWidth())}`);
1187
+ }
1188
+ stderr(renderErrorMessage(classified.error, formatOptions({
1164
1189
  messageFormatter,
1165
1190
  theme,
1166
1191
  colors,
1167
1192
  quotes: !colors,
1168
1193
  maxWidth
1169
- }));
1194
+ })));
1170
1195
  return onError(1, classified.error);
1171
1196
  };
1172
1197
  if (aboveError === "help") {
package/dist/index.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  const require_internal_annotations = require('./internal/annotations.cjs');
2
2
  const require_terminal = require('./terminal.cjs');
3
- const require_message = require('./message.cjs');
4
3
  const require_usage = require('./usage.cjs');
4
+ const require_message = require('./message.cjs');
5
5
  const require_doc = require('./doc.cjs');
6
6
  const require_internal_dependency = require('./internal/dependency.cjs');
7
7
  const require_internal_parser = require('./internal/parser.cjs');
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import { getAnnotations } from "./internal/annotations.js";
2
2
  import { defaultTerminalTheme } from "./terminal.js";
3
- import { commandLine, createMessageFormatter, envVar, formatMessage, lineBreak, link, message, metavar, optionName, optionNames, text, value, valueSet, values } from "./message.js";
4
3
  import { cloneUsage, cloneUsageTerm, extractArgumentMetavars, extractCommandNames, extractLiteralValues, extractOptionNames, formatUsage, formatUsageTerm, isDocHidden, isSuggestionHidden, isUsageHidden, mergeHidden, normalizeUsage } from "./usage.js";
4
+ import { commandLine, createMessageFormatter, envVar, formatMessage, lineBreak, link, message, metavar, optionName, optionNames, text, value, valueSet, values } from "./message.js";
5
5
  import { cloneDocEntry, deduplicateDocEntries, deduplicateDocFragments, formatDocPage, isDocEntryHidden } from "./doc.js";
6
6
  import { dependency, deriveFrom, deriveFromAsync, deriveFromSync, getDependencySourceInfo, isDependencySource, isDerivedValueParser } from "./internal/dependency.js";
7
7
  import { createParserContext, getDocPage, getDocPageAsync, getDocPageSync, parse, parseAsync, parseDetailed, parseSync, suggest, suggestAsync, suggestSync } from "./internal/parser.js";
@@ -1,6 +1,6 @@
1
1
  const require_internal_annotations = require('./annotations.cjs');
2
- const require_message = require('../message.cjs');
3
2
  const require_usage = require('../usage.cjs');
3
+ const require_message = require('../message.cjs');
4
4
  const require_doc = require('../doc.cjs');
5
5
  const require_mode_dispatch = require('./mode-dispatch.cjs');
6
6
  const require_dependency_runtime = require('../dependency-runtime.cjs');
@@ -1,6 +1,6 @@
1
1
  import { hasMeaningfulAnnotations, inheritAnnotations, injectAnnotations, isInjectedAnnotationWrapper, unwrapInjectedAnnotationWrapper } from "./annotations.js";
2
- import { cloneMessage, message } from "../message.js";
3
2
  import { cloneUsage, normalizeUsage } from "../usage.js";
3
+ import { cloneMessage, message } from "../message.js";
4
4
  import { cloneDocEntry, isDocEntryHidden } from "../doc.js";
5
5
  import { dispatchByMode, dispatchParserByMode } from "./mode-dispatch.js";
6
6
  import { collectExplicitSourceValues, collectExplicitSourceValuesAsync, createDependencyRuntimeContext } from "../dependency-runtime.js";
@@ -1,6 +1,7 @@
1
1
  const require_displaywidth = require('../displaywidth.cjs');
2
2
  const require_terminal_internal = require('../terminal-internal.cjs');
3
3
  const require_text_layout = require('../text-layout.cjs');
4
+ const require_terminal_width = require('../terminal-width.cjs');
4
5
  const require_message_renderer = require('../message-renderer.cjs');
5
6
 
6
7
  exports.getDisplayWidth = require_displaywidth.getDisplayWidth;
@@ -9,4 +10,5 @@ exports.placeText = require_text_layout.placeText;
9
10
  exports.renderErrorMessage = require_message_renderer.renderErrorMessage;
10
11
  exports.renderTerminalTerm = require_terminal_internal.renderTerminalTerm;
11
12
  exports.resolveMessageFormatter = require_message_renderer.resolveMessageFormatter;
12
- exports.spaceAfterLabel = require_text_layout.spaceAfterLabel;
13
+ exports.spaceAfterLabel = require_text_layout.spaceAfterLabel;
14
+ exports.withAutomaticWidth = require_terminal_width.withAutomaticWidth;
@@ -2,4 +2,5 @@ import { getDisplayWidth } from "../displaywidth.cjs";
2
2
  import { renderErrorMessage, resolveMessageFormatter } from "../message-renderer.cjs";
3
3
  import { renderTerminalTerm } from "../terminal-internal.cjs";
4
4
  import { measureText, placeText, spaceAfterLabel } from "../text-layout.cjs";
5
- export { getDisplayWidth, measureText, placeText, renderErrorMessage, renderTerminalTerm, resolveMessageFormatter, spaceAfterLabel };
5
+ import { withAutomaticWidth } from "../terminal-width.cjs";
6
+ export { getDisplayWidth, measureText, placeText, renderErrorMessage, renderTerminalTerm, resolveMessageFormatter, spaceAfterLabel, withAutomaticWidth };
@@ -2,4 +2,5 @@ import { getDisplayWidth } from "../displaywidth.js";
2
2
  import { renderErrorMessage, resolveMessageFormatter } from "../message-renderer.js";
3
3
  import { renderTerminalTerm } from "../terminal-internal.js";
4
4
  import { measureText, placeText, spaceAfterLabel } from "../text-layout.js";
5
- export { getDisplayWidth, measureText, placeText, renderErrorMessage, renderTerminalTerm, resolveMessageFormatter, spaceAfterLabel };
5
+ import { withAutomaticWidth } from "../terminal-width.js";
6
+ export { getDisplayWidth, measureText, placeText, renderErrorMessage, renderTerminalTerm, resolveMessageFormatter, spaceAfterLabel, withAutomaticWidth };
@@ -1,6 +1,7 @@
1
1
  import { getDisplayWidth } from "../displaywidth.js";
2
2
  import { renderTerminalTerm } from "../terminal-internal.js";
3
3
  import { measureText, placeText, spaceAfterLabel } from "../text-layout.js";
4
+ import { withAutomaticWidth } from "../terminal-width.js";
4
5
  import { renderErrorMessage, resolveMessageFormatter } from "../message-renderer.js";
5
6
 
6
- export { getDisplayWidth, measureText, placeText, renderErrorMessage, renderTerminalTerm, resolveMessageFormatter, spaceAfterLabel };
7
+ export { getDisplayWidth, measureText, placeText, renderErrorMessage, renderTerminalTerm, resolveMessageFormatter, spaceAfterLabel, withAutomaticWidth };
@@ -1,6 +1,7 @@
1
- const require_message_registry = require('./message-registry.cjs');
2
1
  const require_terminal_internal = require('./terminal-internal.cjs');
3
2
  const require_text_layout = require('./text-layout.cjs');
3
+ const require_terminal_width = require('./terminal-width.cjs');
4
+ const require_message_registry = require('./message-registry.cjs');
4
5
  const require_message = require('./message.cjs');
5
6
 
6
7
  //#region src/message-renderer.ts
@@ -39,10 +40,19 @@ function renderErrorMessage(message, options = {}) {
39
40
  if (!Number.isFinite(occupied) || !Number.isInteger(occupied)) throw new TypeError("Initial width must be a finite integer.");
40
41
  if (occupied < 0) throw new RangeError("Initial width must be nonnegative.");
41
42
  const colors = options.colors;
42
- const prefix = require_text_layout.placeText(require_text_layout.spaceAfterLabel(require_terminal_internal.renderTerminalTerm({
43
+ const label = require_text_layout.spaceAfterLabel(require_terminal_internal.renderTerminalTerm({
43
44
  type: "errorLabel",
44
45
  label: "Error:"
45
- }, options.theme, typeof colors === "object" ? true : colors, void 0, void 0, typeof colors === "object" ? colors.resetSuffix : void 0)), {
46
+ }, options.theme, typeof colors === "object" ? true : colors, void 0, void 0, typeof colors === "object" ? colors.resetSuffix : void 0));
47
+ if (require_terminal_width.hasAutomaticWidth(options) && options.maxWidth != null) {
48
+ const size = require_text_layout.measureText(label);
49
+ if (options.maxWidth < Math.max(size.maxLineWidth, occupied + size.lastLineWidth + 1)) options = {
50
+ ...options,
51
+ maxWidth: void 0
52
+ };
53
+ }
54
+ require_terminal_width.reportAutomaticWidth(options, options.maxWidth);
55
+ const prefix = require_text_layout.placeText(label, {
46
56
  line: "",
47
57
  column: occupied
48
58
  }, options.maxWidth);
@@ -1,6 +1,7 @@
1
- import { messageRenderers } from "./message-registry.js";
2
1
  import { renderTerminalTerm } from "./terminal-internal.js";
3
- import { placeText, spaceAfterLabel } from "./text-layout.js";
2
+ import { measureText, placeText, spaceAfterLabel } from "./text-layout.js";
3
+ import { hasAutomaticWidth, reportAutomaticWidth } from "./terminal-width.js";
4
+ import { messageRenderers } from "./message-registry.js";
4
5
  import { createMessageFormatter, formatMessage } from "./message.js";
5
6
 
6
7
  //#region src/message-renderer.ts
@@ -39,10 +40,19 @@ function renderErrorMessage(message, options = {}) {
39
40
  if (!Number.isFinite(occupied) || !Number.isInteger(occupied)) throw new TypeError("Initial width must be a finite integer.");
40
41
  if (occupied < 0) throw new RangeError("Initial width must be nonnegative.");
41
42
  const colors = options.colors;
42
- const prefix = placeText(spaceAfterLabel(renderTerminalTerm({
43
+ const label = spaceAfterLabel(renderTerminalTerm({
43
44
  type: "errorLabel",
44
45
  label: "Error:"
45
- }, options.theme, typeof colors === "object" ? true : colors, void 0, void 0, typeof colors === "object" ? colors.resetSuffix : void 0)), {
46
+ }, options.theme, typeof colors === "object" ? true : colors, void 0, void 0, typeof colors === "object" ? colors.resetSuffix : void 0));
47
+ if (hasAutomaticWidth(options) && options.maxWidth != null) {
48
+ const size = measureText(label);
49
+ if (options.maxWidth < Math.max(size.maxLineWidth, occupied + size.lastLineWidth + 1)) options = {
50
+ ...options,
51
+ maxWidth: void 0
52
+ };
53
+ }
54
+ reportAutomaticWidth(options, options.maxWidth);
55
+ const prefix = placeText(label, {
46
56
  line: "",
47
57
  column: occupied
48
58
  }, options.maxWidth);
package/dist/message.cjs CHANGED
@@ -1,6 +1,6 @@
1
- const require_message_registry = require('./message-registry.cjs');
2
1
  const require_displaywidth = require('./displaywidth.cjs');
3
2
  const require_terminal_internal = require('./terminal-internal.cjs');
3
+ const require_message_registry = require('./message-registry.cjs');
4
4
 
5
5
  //#region src/message.ts
6
6
  /**
package/dist/message.js CHANGED
@@ -1,6 +1,6 @@
1
- import { messageRenderers } from "./message-registry.js";
2
1
  import { getDisplayWidth } from "./displaywidth.js";
3
2
  import { formatTerminalTerm, fragmentTokens, serializeTokens } from "./terminal-internal.js";
3
+ import { messageRenderers } from "./message-registry.js";
4
4
 
5
5
  //#region src/message.ts
6
6
  /**
@@ -1,7 +1,7 @@
1
1
  const require_internal_annotations = require('./internal/annotations.cjs');
2
- const require_message = require('./message.cjs');
3
2
  const require_validate = require('./validate.cjs');
4
3
  const require_usage = require('./usage.cjs');
4
+ const require_message = require('./message.cjs');
5
5
  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');
@@ -1,7 +1,7 @@
1
1
  import { annotateFreshArray, getAnnotations } from "./internal/annotations.js";
2
- import { message, metavar, optionName, optionNames, text, valueSet } from "./message.js";
3
2
  import { validateCommandNames, validateOptionNames } from "./validate.js";
4
3
  import { extractOptionNames, isDocHidden, isSuggestionHidden } from "./usage.js";
4
+ import { message, metavar, optionName, optionNames, text, valueSet } from "./message.js";
5
5
  import { dispatchByMode, dispatchIterableByMode, wrapForMode } from "./internal/mode-dispatch.js";
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";
@@ -1,5 +1,5 @@
1
- const require_message = require('./message.cjs');
2
1
  const require_usage = require('./usage.cjs');
2
+ const require_message = require('./message.cjs');
3
3
 
4
4
  //#region src/suggestion.ts
5
5
  /**
@@ -1,5 +1,5 @@
1
- import { lineBreak, message, optionName, text, value } from "./message.js";
2
1
  import { extractCommandNames, extractOptionNames, isSuggestionHidden } from "./usage.js";
2
+ import { lineBreak, message, optionName, text, value } from "./message.js";
3
3
 
4
4
  //#region src/suggestion.ts
5
5
  /**
@@ -0,0 +1,65 @@
1
+ const require_terminal_internal = require('./terminal-internal.cjs');
2
+ const require_text_layout = require('./text-layout.cjs');
3
+ const require_usage = require('./usage.cjs');
4
+
5
+ //#region src/terminal-width.ts
6
+ const automaticWidth = Symbol.for("@optique/core/automaticWidth");
7
+ /**
8
+ * Marks runner options without changing their public type. The enumerable
9
+ * symbol survives the option copies made by Program and context runners.
10
+ * @param options The options whose width was automatically detected.
11
+ * @param onResolved Receives the effective width before message rendering.
12
+ * @returns A marked copy of the options.
13
+ * @internal
14
+ */
15
+ function withAutomaticWidth(options, onResolved = () => {}) {
16
+ return {
17
+ ...options,
18
+ [automaticWidth]: onResolved
19
+ };
20
+ }
21
+ /**
22
+ * Checks whether a width is a runner hint rather than an explicit constraint.
23
+ * @param options The formatting or runner options.
24
+ * @returns Whether automatic width fallback is enabled.
25
+ * @internal
26
+ */
27
+ function hasAutomaticWidth(options) {
28
+ return automaticWidth in options && typeof options[automaticWidth] === "function";
29
+ }
30
+ /**
31
+ * Reports the resolved width before any opaque message formatter runs.
32
+ * @param options The marked formatting options.
33
+ * @param width The width selected by layout validation.
34
+ * @internal
35
+ */
36
+ function reportAutomaticWidth(options, width) {
37
+ if (automaticWidth in options && typeof options[automaticWidth] === "function") options[automaticWidth](width);
38
+ }
39
+ /**
40
+ * Measures the minimum usage layout, allowing long atomic terms to overflow.
41
+ * @param programName The program name shown in the usage line.
42
+ * @param usage The visible usage tree.
43
+ * @param label The rendered usage label, including its trailing space.
44
+ * @param theme The semantic terminal theme.
45
+ * @returns The minimum feasible width for the usage block.
46
+ * @internal
47
+ */
48
+ function minimumUsageWidth(programName, usage, label, theme) {
49
+ const labelWidth = require_text_layout.measureText(label).lastLineWidth;
50
+ const programWidth = Math.max(0, ...[...require_terminal_internal.fragmentTokens(require_terminal_internal.formatTerminalTerm({
51
+ type: "programName",
52
+ programName
53
+ }, theme))].map((token) => token.width));
54
+ const atomicWidth = usage.reduce((widest, term) => Math.max(widest, require_text_layout.measureText(require_usage.formatUsageTerm(term, {
55
+ theme,
56
+ maxWidth: 1
57
+ })).maxLineWidth), 0);
58
+ return Math.max(require_text_layout.measureText(label).maxLineWidth, labelWidth + Math.max(programWidth, Math.min(atomicWidth, programWidth + labelWidth)));
59
+ }
60
+
61
+ //#endregion
62
+ exports.hasAutomaticWidth = hasAutomaticWidth;
63
+ exports.minimumUsageWidth = minimumUsageWidth;
64
+ exports.reportAutomaticWidth = reportAutomaticWidth;
65
+ exports.withAutomaticWidth = withAutomaticWidth;
@@ -0,0 +1,19 @@
1
+ //#region src/terminal-width.d.ts
2
+
3
+ /**
4
+ * Marks runner options without changing their public type. The enumerable
5
+ * symbol survives the option copies made by Program and context runners.
6
+ * @param options The options whose width was automatically detected.
7
+ * @param onResolved Receives the effective width before message rendering.
8
+ * @returns A marked copy of the options.
9
+ * @internal
10
+ */
11
+ declare function withAutomaticWidth<T extends object>(options: T, onResolved?: (width: number | undefined) => void): T;
12
+ /**
13
+ * Checks whether a width is a runner hint rather than an explicit constraint.
14
+ * @param options The formatting or runner options.
15
+ * @returns Whether automatic width fallback is enabled.
16
+ * @internal
17
+ */
18
+ //#endregion
19
+ export { withAutomaticWidth };
@@ -0,0 +1,19 @@
1
+ //#region src/terminal-width.d.ts
2
+
3
+ /**
4
+ * Marks runner options without changing their public type. The enumerable
5
+ * symbol survives the option copies made by Program and context runners.
6
+ * @param options The options whose width was automatically detected.
7
+ * @param onResolved Receives the effective width before message rendering.
8
+ * @returns A marked copy of the options.
9
+ * @internal
10
+ */
11
+ declare function withAutomaticWidth<T extends object>(options: T, onResolved?: (width: number | undefined) => void): T;
12
+ /**
13
+ * Checks whether a width is a runner hint rather than an explicit constraint.
14
+ * @param options The formatting or runner options.
15
+ * @returns Whether automatic width fallback is enabled.
16
+ * @internal
17
+ */
18
+ //#endregion
19
+ export { withAutomaticWidth };
@@ -0,0 +1,62 @@
1
+ import { formatTerminalTerm, fragmentTokens } from "./terminal-internal.js";
2
+ import { measureText } from "./text-layout.js";
3
+ import { formatUsageTerm } from "./usage.js";
4
+
5
+ //#region src/terminal-width.ts
6
+ const automaticWidth = Symbol.for("@optique/core/automaticWidth");
7
+ /**
8
+ * Marks runner options without changing their public type. The enumerable
9
+ * symbol survives the option copies made by Program and context runners.
10
+ * @param options The options whose width was automatically detected.
11
+ * @param onResolved Receives the effective width before message rendering.
12
+ * @returns A marked copy of the options.
13
+ * @internal
14
+ */
15
+ function withAutomaticWidth(options, onResolved = () => {}) {
16
+ return {
17
+ ...options,
18
+ [automaticWidth]: onResolved
19
+ };
20
+ }
21
+ /**
22
+ * Checks whether a width is a runner hint rather than an explicit constraint.
23
+ * @param options The formatting or runner options.
24
+ * @returns Whether automatic width fallback is enabled.
25
+ * @internal
26
+ */
27
+ function hasAutomaticWidth(options) {
28
+ return automaticWidth in options && typeof options[automaticWidth] === "function";
29
+ }
30
+ /**
31
+ * Reports the resolved width before any opaque message formatter runs.
32
+ * @param options The marked formatting options.
33
+ * @param width The width selected by layout validation.
34
+ * @internal
35
+ */
36
+ function reportAutomaticWidth(options, width) {
37
+ if (automaticWidth in options && typeof options[automaticWidth] === "function") options[automaticWidth](width);
38
+ }
39
+ /**
40
+ * Measures the minimum usage layout, allowing long atomic terms to overflow.
41
+ * @param programName The program name shown in the usage line.
42
+ * @param usage The visible usage tree.
43
+ * @param label The rendered usage label, including its trailing space.
44
+ * @param theme The semantic terminal theme.
45
+ * @returns The minimum feasible width for the usage block.
46
+ * @internal
47
+ */
48
+ function minimumUsageWidth(programName, usage, label, theme) {
49
+ const labelWidth = measureText(label).lastLineWidth;
50
+ const programWidth = Math.max(0, ...[...fragmentTokens(formatTerminalTerm({
51
+ type: "programName",
52
+ programName
53
+ }, theme))].map((token) => token.width));
54
+ const atomicWidth = usage.reduce((widest, term) => Math.max(widest, measureText(formatUsageTerm(term, {
55
+ theme,
56
+ maxWidth: 1
57
+ })).maxLineWidth), 0);
58
+ return Math.max(measureText(label).maxLineWidth, labelWidth + Math.max(programWidth, Math.min(atomicWidth, programWidth + labelWidth)));
59
+ }
60
+
61
+ //#endregion
62
+ export { hasAutomaticWidth, minimumUsageWidth, reportAutomaticWidth, withAutomaticWidth };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/core",
3
- "version": "1.3.0-dev.2565",
3
+ "version": "1.3.0-dev.2567",
4
4
  "description": "Type-safe combinatorial command-line interface parser",
5
5
  "keywords": [
6
6
  "CLI",
@@ -237,7 +237,7 @@
237
237
  },
238
238
  "sideEffects": false,
239
239
  "devDependencies": {
240
- "@optique/env": "1.3.0-dev.2565+f4620bc0",
240
+ "@optique/env": "1.3.0-dev.2567+2ca0bc6e",
241
241
  "@types/node": "^24.0.0",
242
242
  "fast-check": "^4.7.0",
243
243
  "tsdown": "^0.13.0",