@optique/core 1.3.0-dev.2529 → 1.3.0-dev.2532

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.
Files changed (42) hide show
  1. package/dist/doc.cjs +93 -84
  2. package/dist/doc.d.cts +12 -1
  3. package/dist/doc.d.ts +12 -1
  4. package/dist/doc.js +88 -79
  5. package/dist/facade.cjs +49 -15
  6. package/dist/facade.d.cts +12 -1
  7. package/dist/facade.d.ts +12 -1
  8. package/dist/facade.js +48 -14
  9. package/dist/index.cjs +3 -0
  10. package/dist/index.d.cts +3 -2
  11. package/dist/index.d.ts +3 -2
  12. package/dist/index.js +3 -2
  13. package/dist/internal/terminal.cjs +7 -0
  14. package/dist/internal/terminal.d.cts +4 -0
  15. package/dist/internal/terminal.d.ts +4 -0
  16. package/dist/internal/terminal.js +5 -0
  17. package/dist/message-registry.cjs +6 -0
  18. package/dist/message-registry.d.cts +7 -0
  19. package/dist/message-registry.d.ts +7 -0
  20. package/dist/message-registry.js +5 -0
  21. package/dist/message-renderer.cjs +26 -0
  22. package/dist/message-renderer.d.cts +19 -0
  23. package/dist/message-renderer.d.ts +19 -0
  24. package/dist/message-renderer.js +26 -0
  25. package/dist/message.cjs +49 -85
  26. package/dist/message.d.cts +31 -1
  27. package/dist/message.d.ts +31 -1
  28. package/dist/message.js +49 -86
  29. package/dist/terminal-internal.cjs +268 -0
  30. package/dist/terminal-internal.d.cts +36 -0
  31. package/dist/terminal-internal.d.ts +36 -0
  32. package/dist/terminal-internal.js +262 -0
  33. package/dist/terminal.cjs +73 -0
  34. package/dist/terminal.d.cts +129 -0
  35. package/dist/terminal.d.ts +129 -0
  36. package/dist/terminal.js +72 -0
  37. package/dist/usage.cjs +110 -134
  38. package/dist/usage.d.cts +8 -0
  39. package/dist/usage.d.ts +8 -0
  40. package/dist/usage.js +110 -134
  41. package/package.json +18 -2
  42. package/skills/optique/SKILL.md +3 -3
package/dist/usage.cjs CHANGED
@@ -1,4 +1,5 @@
1
1
  const require_displaywidth = require('./displaywidth.cjs');
2
+ const require_terminal_internal = require('./terminal-internal.cjs');
2
3
  const require_validate = require('./validate.cjs');
3
4
 
4
5
  //#region src/usage.ts
@@ -182,6 +183,10 @@ function extractArgumentMetavars(usage) {
182
183
  * whitespace-only, or contains control characters.
183
184
  */
184
185
  function formatUsage(programName, usage, options = {}) {
186
+ options = {
187
+ ...options,
188
+ theme: require_terminal_internal.cacheTerminalTheme(options.theme)
189
+ };
185
190
  require_validate.validateProgramName(programName);
186
191
  usage = normalizeUsage(filterUsageForDisplay(usage));
187
192
  if (options.expandCommands) {
@@ -197,29 +202,23 @@ function formatUsage(programName, usage, options = {}) {
197
202
  if (lines.length > 0) return lines.join("\n");
198
203
  }
199
204
  }
200
- let output = options.colors ? `\x1b[1m${programName}\x1b[0m` : programName;
201
- let lineWidth = require_displaywidth.getDisplayWidth(programName);
202
- let first = true;
203
- for (const { text, width } of formatUsageTerms(usage, options)) {
204
- if (first) {
205
- first = false;
206
- if (options.maxWidth != null && lineWidth + 1 + width > options.maxWidth) {
207
- output += "\n";
208
- lineWidth = 0;
209
- } else {
210
- output += " ";
211
- lineWidth += 1;
212
- }
213
- } else if (options.maxWidth != null && lineWidth > 0 && lineWidth + width > options.maxWidth) {
214
- if (output.endsWith(" ")) output = output.slice(0, -1);
215
- output += "\n";
216
- lineWidth = 0;
217
- if (text === " ") continue;
218
- }
219
- output += text;
220
- lineWidth += width;
221
- }
222
- return output;
205
+ const programTokens = layoutUsageTokens(require_terminal_internal.fragmentTokens(require_terminal_internal.formatTerminalTerm({
206
+ type: "programName",
207
+ programName
208
+ }, options.theme)), options);
209
+ const terms = [...formatUsageTerms(usage, options)];
210
+ if (terms.length === 0) return require_terminal_internal.serializeTokens(programTokens, options.colors);
211
+ const width = programTokens.reduce((n, token) => token.width === -1 ? 0 : n + token.width, 0);
212
+ const separator = options.maxWidth != null && width + 1 + terms[0].width > options.maxWidth ? {
213
+ text: "\n",
214
+ width: -1,
215
+ scopes: []
216
+ } : {
217
+ text: " ",
218
+ width: 1,
219
+ scopes: []
220
+ };
221
+ return require_terminal_internal.serializeTokens([...programTokens, separator], options.colors) + wrapUsageTokens(terms, options, separator.width === -1 ? 0 : width + 1);
223
222
  }
224
223
  /**
225
224
  * Normalizes a usage description by flattening nested exclusive terms,
@@ -454,7 +453,8 @@ function* formatUsageTerms(terms, options) {
454
453
  for (const t of terms) {
455
454
  if (i > 0) yield {
456
455
  text: " ",
457
- width: 1
456
+ width: 1,
457
+ scopes: []
458
458
  };
459
459
  yield* formatUsageTermInternal(t, options);
460
460
  i++;
@@ -468,141 +468,117 @@ function* formatUsageTerms(terms, options) {
468
468
  * @param options Optional formatting options to customize the output.
469
469
  * See {@link UsageTermFormatOptions} for available options.
470
470
  * @returns A formatted string representation of the usage term.
471
+ * @throws {RangeError} If a theme supplies an invalid RGB channel or palette
472
+ * index, even when colors are disabled.
471
473
  */
472
474
  function formatUsageTerm(term, options = {}) {
475
+ options = {
476
+ ...options,
477
+ theme: require_terminal_internal.cacheTerminalTheme(options.theme)
478
+ };
473
479
  const hiddenCheck = options.context === "doc" ? isDocHidden : isUsageHidden;
474
480
  const visibleTerms = filterUsageForDisplay([term], hiddenCheck);
475
481
  if (visibleTerms.length < 1) return "";
476
- let lineWidth = 0;
477
- let output = "";
478
- for (const { text, width } of formatUsageTermInternal(visibleTerms[0], options)) {
479
- if (options.maxWidth != null && lineWidth > 0 && lineWidth + width > options.maxWidth) {
480
- if (output.endsWith(" ")) output = output.slice(0, -1);
481
- output += "\n";
482
+ return wrapUsageTokens(formatUsageTermInternal(visibleTerms[0], options), options);
483
+ }
484
+ function wrapUsageTokens(input, options, initialWidth = 0) {
485
+ return require_terminal_internal.serializeTokens(layoutUsageTokens(input, options, initialWidth), options.colors);
486
+ }
487
+ function layoutUsageTokens(input, options, initialWidth = 0) {
488
+ let lineWidth = initialWidth;
489
+ const output = [];
490
+ for (const token of input) {
491
+ if (options.maxWidth != null && lineWidth > 0 && lineWidth + token.width > options.maxWidth) {
492
+ const last = output.at(-1);
493
+ if (last?.text.endsWith(" ") && (!options.colors || last.scopes.length === 0)) output[output.length - 1] = {
494
+ ...last,
495
+ text: last.text.slice(0, -1),
496
+ width: last.width - 1
497
+ };
498
+ output.push({
499
+ text: "\n",
500
+ width: -1,
501
+ scopes: []
502
+ });
482
503
  lineWidth = 0;
483
- if (text === " ") continue;
504
+ if (token.text === " ") continue;
484
505
  }
485
- output += text;
486
- lineWidth += width;
506
+ output.push(token);
507
+ lineWidth += token.width;
487
508
  }
488
509
  return output;
489
510
  }
490
511
  function* formatUsageTermInternal(term, options) {
491
- const optionsSeparator = options.optionsSeparator ?? "/";
492
- if (term.type === "argument") yield {
493
- text: options?.colors ? `\x1b[4m${term.metavar}\x1b[0m` : term.metavar,
494
- width: require_displaywidth.getDisplayWidth(term.metavar)
512
+ function leaf(t, usage) {
513
+ return require_terminal_internal.fragmentTokens(require_terminal_internal.formatTerminalTerm(t, options.theme, false, usage));
514
+ }
515
+ function punctuation(punctuation$1, kind) {
516
+ return leaf({
517
+ type: "syntaxPunctuation",
518
+ punctuation: punctuation$1,
519
+ kind
520
+ });
521
+ }
522
+ const space = {
523
+ text: " ",
524
+ width: 1,
525
+ scopes: []
495
526
  };
496
- else if (term.type === "option") if (options?.onlyShortestOptions) {
497
- const shortestName = term.names.reduce((a, b) => require_displaywidth.getDisplayWidth(a) <= require_displaywidth.getDisplayWidth(b) ? a : b);
498
- yield {
499
- text: options?.colors ? `\x1b[3m${shortestName}\x1b[0m` : shortestName,
500
- width: require_displaywidth.getDisplayWidth(shortestName)
501
- };
502
- } else {
503
- let i = 0;
504
- for (const optionName of term.names) {
505
- if (i > 0) yield {
506
- text: options?.colors ? `\x1b[2m${optionsSeparator}\x1b[0m` : optionsSeparator,
507
- width: require_displaywidth.getDisplayWidth(optionsSeparator)
508
- };
509
- yield {
510
- text: options?.colors ? `\x1b[3m${optionName}\x1b[0m` : optionName,
511
- width: require_displaywidth.getDisplayWidth(optionName)
512
- };
513
- i++;
527
+ if (term.type === "argument") yield* leaf({
528
+ type: "metavar",
529
+ metavar: term.metavar
530
+ }, "argument");
531
+ else if (term.type === "option") {
532
+ const names = options.onlyShortestOptions ? [term.names.reduce((a, b) => require_displaywidth.getDisplayWidth(a) <= require_displaywidth.getDisplayWidth(b) ? a : b)] : term.names;
533
+ for (let i = 0; i < names.length; i++) {
534
+ if (i > 0) yield* punctuation(options.optionsSeparator ?? "/", "optionSeparator");
535
+ yield* leaf({
536
+ type: "optionName",
537
+ optionName: names[i]
538
+ });
514
539
  }
515
- if (term.metavar != null) {
516
- yield {
517
- text: " ",
518
- width: 1
519
- };
520
- yield {
521
- text: options?.colors ? `\x1b[4m\x1b[2m${term.metavar}\x1b[0m` : term.metavar,
522
- width: require_displaywidth.getDisplayWidth(term.metavar)
523
- };
540
+ if (!options.onlyShortestOptions && term.metavar != null) {
541
+ yield space;
542
+ yield* leaf({
543
+ type: "metavar",
544
+ metavar: term.metavar
545
+ }, "optionValue");
524
546
  }
525
- }
526
- else if (term.type === "command") yield {
527
- text: options?.colors ? `\x1b[1m${term.name}\x1b[0m` : term.name,
528
- width: require_displaywidth.getDisplayWidth(term.name)
529
- };
547
+ } else if (term.type === "command") yield* leaf({
548
+ type: "optionName",
549
+ optionName: term.name
550
+ }, "command");
530
551
  else if (term.type === "optional") {
531
- yield {
532
- text: options?.colors ? `\x1b[2m[\x1b[0m` : "[",
533
- width: 1
534
- };
552
+ yield* punctuation("[", "optionalOpen");
535
553
  yield* formatUsageTerms(term.terms, options);
536
- yield {
537
- text: options?.colors ? `\x1b[2m]\x1b[0m` : "]",
538
- width: 1
539
- };
554
+ yield* punctuation("]", "optionalClose");
540
555
  } else if (term.type === "exclusive") {
541
- yield {
542
- text: options?.colors ? `\x1b[2m(\x1b[0m` : "(",
543
- width: 1
544
- };
545
- let i = 0;
546
- for (const termGroup of term.terms) {
556
+ yield* punctuation("(", "groupOpen");
557
+ for (let i = 0; i < term.terms.length; i++) {
547
558
  if (i > 0) {
548
- yield {
549
- text: " ",
550
- width: 1
551
- };
552
- yield {
553
- text: "|",
554
- width: 1
555
- };
556
- yield {
557
- text: " ",
558
- width: 1
559
- };
559
+ yield space;
560
+ yield* punctuation("|", "alternative");
561
+ yield space;
560
562
  }
561
- yield* formatUsageTerms(termGroup, options);
562
- i++;
563
+ yield* formatUsageTerms(term.terms[i], options);
563
564
  }
564
- yield {
565
- text: options?.colors ? `\x1b[2m)\x1b[0m` : ")",
566
- width: 1
567
- };
565
+ yield* punctuation(")", "groupClose");
568
566
  } else if (term.type === "sequence") yield* formatUsageTerms(term.terms, options);
569
567
  else if (term.type === "multiple") {
570
- if (term.min < 1) yield {
571
- text: options?.colors ? `\x1b[2m[\x1b[0m` : "[",
572
- width: 1
573
- };
568
+ if (term.min < 1) yield* punctuation("[", "optionalOpen");
574
569
  for (let i = 0; i < Math.max(1, term.min); i++) {
575
- if (i > 0) yield {
576
- text: " ",
577
- width: 1
578
- };
570
+ if (i > 0) yield space;
579
571
  yield* formatUsageTerms(term.terms, options);
580
572
  }
581
- yield {
582
- text: options?.colors ? `\x1b[2m...\x1b[0m` : "...",
583
- width: 3
584
- };
585
- if (term.min < 1) yield {
586
- text: options?.colors ? `\x1b[2m]\x1b[0m` : "]",
587
- width: 1
588
- };
589
- } else if (term.type === "literal") yield {
590
- text: term.value,
591
- width: require_displaywidth.getDisplayWidth(term.value)
592
- };
593
- else if (term.type === "passthrough") {
594
- const text = "[...]";
595
- yield {
596
- text: options?.colors ? `\x1b[2m${text}\x1b[0m` : text,
597
- width: 5
598
- };
599
- } else if (term.type === "ellipsis") {
600
- const text = "...";
601
- yield {
602
- text: options?.colors ? `\x1b[2m${text}\x1b[0m` : text,
603
- width: 3
604
- };
605
- } else throw new TypeError(`Unknown usage term type: ${term["type"]}.`);
573
+ yield* punctuation("...", "ellipsis");
574
+ if (term.min < 1) yield* punctuation("]", "optionalClose");
575
+ } else if (term.type === "literal") yield* leaf({
576
+ type: "value",
577
+ value: term.value
578
+ }, "literal");
579
+ else if (term.type === "passthrough") yield* punctuation("[...]", "passthrough");
580
+ else if (term.type === "ellipsis") yield* punctuation("...", "ellipsis");
581
+ else throw new TypeError(`Unknown usage term type: ${term["type"]}.`);
606
582
  }
607
583
 
608
584
  //#endregion
package/dist/usage.d.cts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { NonEmptyString } from "./nonempty.cjs";
2
+ import { TerminalTheme } from "./terminal.cjs";
2
3
 
3
4
  //#region src/usage.d.ts
4
5
 
@@ -340,6 +341,11 @@ declare function extractArgumentMetavars(usage: Usage): Set<string>;
340
341
  * Options for formatting usage descriptions.
341
342
  */
342
343
  interface UsageFormatOptions {
344
+ /**
345
+ * Semantic terminal theme.
346
+ * @since 1.3.0
347
+ */
348
+ readonly theme?: TerminalTheme;
343
349
  /**
344
350
  * When `true`, expands commands in the usage description
345
351
  * to multiple lines, showing each command on a new line.
@@ -477,6 +483,8 @@ interface UsageTermFormatOptions extends UsageFormatOptions {
477
483
  * @param options Optional formatting options to customize the output.
478
484
  * See {@link UsageTermFormatOptions} for available options.
479
485
  * @returns A formatted string representation of the usage term.
486
+ * @throws {RangeError} If a theme supplies an invalid RGB channel or palette
487
+ * index, even when colors are disabled.
480
488
  */
481
489
  declare function formatUsageTerm(term: UsageTerm, options?: UsageTermFormatOptions): string;
482
490
  //#endregion
package/dist/usage.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { NonEmptyString } from "./nonempty.js";
2
+ import { TerminalTheme } from "./terminal.js";
2
3
 
3
4
  //#region src/usage.d.ts
4
5
 
@@ -340,6 +341,11 @@ declare function extractArgumentMetavars(usage: Usage): Set<string>;
340
341
  * Options for formatting usage descriptions.
341
342
  */
342
343
  interface UsageFormatOptions {
344
+ /**
345
+ * Semantic terminal theme.
346
+ * @since 1.3.0
347
+ */
348
+ readonly theme?: TerminalTheme;
343
349
  /**
344
350
  * When `true`, expands commands in the usage description
345
351
  * to multiple lines, showing each command on a new line.
@@ -477,6 +483,8 @@ interface UsageTermFormatOptions extends UsageFormatOptions {
477
483
  * @param options Optional formatting options to customize the output.
478
484
  * See {@link UsageTermFormatOptions} for available options.
479
485
  * @returns A formatted string representation of the usage term.
486
+ * @throws {RangeError} If a theme supplies an invalid RGB channel or palette
487
+ * index, even when colors are disabled.
480
488
  */
481
489
  declare function formatUsageTerm(term: UsageTerm, options?: UsageTermFormatOptions): string;
482
490
  //#endregion
package/dist/usage.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { getDisplayWidth } from "./displaywidth.js";
2
+ import { cacheTerminalTheme, formatTerminalTerm, fragmentTokens, serializeTokens } from "./terminal-internal.js";
2
3
  import { validateProgramName } from "./validate.js";
3
4
 
4
5
  //#region src/usage.ts
@@ -182,6 +183,10 @@ function extractArgumentMetavars(usage) {
182
183
  * whitespace-only, or contains control characters.
183
184
  */
184
185
  function formatUsage(programName, usage, options = {}) {
186
+ options = {
187
+ ...options,
188
+ theme: cacheTerminalTheme(options.theme)
189
+ };
185
190
  validateProgramName(programName);
186
191
  usage = normalizeUsage(filterUsageForDisplay(usage));
187
192
  if (options.expandCommands) {
@@ -197,29 +202,23 @@ function formatUsage(programName, usage, options = {}) {
197
202
  if (lines.length > 0) return lines.join("\n");
198
203
  }
199
204
  }
200
- let output = options.colors ? `\x1b[1m${programName}\x1b[0m` : programName;
201
- let lineWidth = getDisplayWidth(programName);
202
- let first = true;
203
- for (const { text, width } of formatUsageTerms(usage, options)) {
204
- if (first) {
205
- first = false;
206
- if (options.maxWidth != null && lineWidth + 1 + width > options.maxWidth) {
207
- output += "\n";
208
- lineWidth = 0;
209
- } else {
210
- output += " ";
211
- lineWidth += 1;
212
- }
213
- } else if (options.maxWidth != null && lineWidth > 0 && lineWidth + width > options.maxWidth) {
214
- if (output.endsWith(" ")) output = output.slice(0, -1);
215
- output += "\n";
216
- lineWidth = 0;
217
- if (text === " ") continue;
218
- }
219
- output += text;
220
- lineWidth += width;
221
- }
222
- return output;
205
+ const programTokens = layoutUsageTokens(fragmentTokens(formatTerminalTerm({
206
+ type: "programName",
207
+ programName
208
+ }, options.theme)), options);
209
+ const terms = [...formatUsageTerms(usage, options)];
210
+ if (terms.length === 0) return serializeTokens(programTokens, options.colors);
211
+ const width = programTokens.reduce((n, token) => token.width === -1 ? 0 : n + token.width, 0);
212
+ const separator = options.maxWidth != null && width + 1 + terms[0].width > options.maxWidth ? {
213
+ text: "\n",
214
+ width: -1,
215
+ scopes: []
216
+ } : {
217
+ text: " ",
218
+ width: 1,
219
+ scopes: []
220
+ };
221
+ return serializeTokens([...programTokens, separator], options.colors) + wrapUsageTokens(terms, options, separator.width === -1 ? 0 : width + 1);
223
222
  }
224
223
  /**
225
224
  * Normalizes a usage description by flattening nested exclusive terms,
@@ -454,7 +453,8 @@ function* formatUsageTerms(terms, options) {
454
453
  for (const t of terms) {
455
454
  if (i > 0) yield {
456
455
  text: " ",
457
- width: 1
456
+ width: 1,
457
+ scopes: []
458
458
  };
459
459
  yield* formatUsageTermInternal(t, options);
460
460
  i++;
@@ -468,141 +468,117 @@ function* formatUsageTerms(terms, options) {
468
468
  * @param options Optional formatting options to customize the output.
469
469
  * See {@link UsageTermFormatOptions} for available options.
470
470
  * @returns A formatted string representation of the usage term.
471
+ * @throws {RangeError} If a theme supplies an invalid RGB channel or palette
472
+ * index, even when colors are disabled.
471
473
  */
472
474
  function formatUsageTerm(term, options = {}) {
475
+ options = {
476
+ ...options,
477
+ theme: cacheTerminalTheme(options.theme)
478
+ };
473
479
  const hiddenCheck = options.context === "doc" ? isDocHidden : isUsageHidden;
474
480
  const visibleTerms = filterUsageForDisplay([term], hiddenCheck);
475
481
  if (visibleTerms.length < 1) return "";
476
- let lineWidth = 0;
477
- let output = "";
478
- for (const { text, width } of formatUsageTermInternal(visibleTerms[0], options)) {
479
- if (options.maxWidth != null && lineWidth > 0 && lineWidth + width > options.maxWidth) {
480
- if (output.endsWith(" ")) output = output.slice(0, -1);
481
- output += "\n";
482
+ return wrapUsageTokens(formatUsageTermInternal(visibleTerms[0], options), options);
483
+ }
484
+ function wrapUsageTokens(input, options, initialWidth = 0) {
485
+ return serializeTokens(layoutUsageTokens(input, options, initialWidth), options.colors);
486
+ }
487
+ function layoutUsageTokens(input, options, initialWidth = 0) {
488
+ let lineWidth = initialWidth;
489
+ const output = [];
490
+ for (const token of input) {
491
+ if (options.maxWidth != null && lineWidth > 0 && lineWidth + token.width > options.maxWidth) {
492
+ const last = output.at(-1);
493
+ if (last?.text.endsWith(" ") && (!options.colors || last.scopes.length === 0)) output[output.length - 1] = {
494
+ ...last,
495
+ text: last.text.slice(0, -1),
496
+ width: last.width - 1
497
+ };
498
+ output.push({
499
+ text: "\n",
500
+ width: -1,
501
+ scopes: []
502
+ });
482
503
  lineWidth = 0;
483
- if (text === " ") continue;
504
+ if (token.text === " ") continue;
484
505
  }
485
- output += text;
486
- lineWidth += width;
506
+ output.push(token);
507
+ lineWidth += token.width;
487
508
  }
488
509
  return output;
489
510
  }
490
511
  function* formatUsageTermInternal(term, options) {
491
- const optionsSeparator = options.optionsSeparator ?? "/";
492
- if (term.type === "argument") yield {
493
- text: options?.colors ? `\x1b[4m${term.metavar}\x1b[0m` : term.metavar,
494
- width: getDisplayWidth(term.metavar)
512
+ function leaf(t, usage) {
513
+ return fragmentTokens(formatTerminalTerm(t, options.theme, false, usage));
514
+ }
515
+ function punctuation(punctuation$1, kind) {
516
+ return leaf({
517
+ type: "syntaxPunctuation",
518
+ punctuation: punctuation$1,
519
+ kind
520
+ });
521
+ }
522
+ const space = {
523
+ text: " ",
524
+ width: 1,
525
+ scopes: []
495
526
  };
496
- else if (term.type === "option") if (options?.onlyShortestOptions) {
497
- const shortestName = term.names.reduce((a, b) => getDisplayWidth(a) <= getDisplayWidth(b) ? a : b);
498
- yield {
499
- text: options?.colors ? `\x1b[3m${shortestName}\x1b[0m` : shortestName,
500
- width: getDisplayWidth(shortestName)
501
- };
502
- } else {
503
- let i = 0;
504
- for (const optionName of term.names) {
505
- if (i > 0) yield {
506
- text: options?.colors ? `\x1b[2m${optionsSeparator}\x1b[0m` : optionsSeparator,
507
- width: getDisplayWidth(optionsSeparator)
508
- };
509
- yield {
510
- text: options?.colors ? `\x1b[3m${optionName}\x1b[0m` : optionName,
511
- width: getDisplayWidth(optionName)
512
- };
513
- i++;
527
+ if (term.type === "argument") yield* leaf({
528
+ type: "metavar",
529
+ metavar: term.metavar
530
+ }, "argument");
531
+ else if (term.type === "option") {
532
+ const names = options.onlyShortestOptions ? [term.names.reduce((a, b) => getDisplayWidth(a) <= getDisplayWidth(b) ? a : b)] : term.names;
533
+ for (let i = 0; i < names.length; i++) {
534
+ if (i > 0) yield* punctuation(options.optionsSeparator ?? "/", "optionSeparator");
535
+ yield* leaf({
536
+ type: "optionName",
537
+ optionName: names[i]
538
+ });
514
539
  }
515
- if (term.metavar != null) {
516
- yield {
517
- text: " ",
518
- width: 1
519
- };
520
- yield {
521
- text: options?.colors ? `\x1b[4m\x1b[2m${term.metavar}\x1b[0m` : term.metavar,
522
- width: getDisplayWidth(term.metavar)
523
- };
540
+ if (!options.onlyShortestOptions && term.metavar != null) {
541
+ yield space;
542
+ yield* leaf({
543
+ type: "metavar",
544
+ metavar: term.metavar
545
+ }, "optionValue");
524
546
  }
525
- }
526
- else if (term.type === "command") yield {
527
- text: options?.colors ? `\x1b[1m${term.name}\x1b[0m` : term.name,
528
- width: getDisplayWidth(term.name)
529
- };
547
+ } else if (term.type === "command") yield* leaf({
548
+ type: "optionName",
549
+ optionName: term.name
550
+ }, "command");
530
551
  else if (term.type === "optional") {
531
- yield {
532
- text: options?.colors ? `\x1b[2m[\x1b[0m` : "[",
533
- width: 1
534
- };
552
+ yield* punctuation("[", "optionalOpen");
535
553
  yield* formatUsageTerms(term.terms, options);
536
- yield {
537
- text: options?.colors ? `\x1b[2m]\x1b[0m` : "]",
538
- width: 1
539
- };
554
+ yield* punctuation("]", "optionalClose");
540
555
  } else if (term.type === "exclusive") {
541
- yield {
542
- text: options?.colors ? `\x1b[2m(\x1b[0m` : "(",
543
- width: 1
544
- };
545
- let i = 0;
546
- for (const termGroup of term.terms) {
556
+ yield* punctuation("(", "groupOpen");
557
+ for (let i = 0; i < term.terms.length; i++) {
547
558
  if (i > 0) {
548
- yield {
549
- text: " ",
550
- width: 1
551
- };
552
- yield {
553
- text: "|",
554
- width: 1
555
- };
556
- yield {
557
- text: " ",
558
- width: 1
559
- };
559
+ yield space;
560
+ yield* punctuation("|", "alternative");
561
+ yield space;
560
562
  }
561
- yield* formatUsageTerms(termGroup, options);
562
- i++;
563
+ yield* formatUsageTerms(term.terms[i], options);
563
564
  }
564
- yield {
565
- text: options?.colors ? `\x1b[2m)\x1b[0m` : ")",
566
- width: 1
567
- };
565
+ yield* punctuation(")", "groupClose");
568
566
  } else if (term.type === "sequence") yield* formatUsageTerms(term.terms, options);
569
567
  else if (term.type === "multiple") {
570
- if (term.min < 1) yield {
571
- text: options?.colors ? `\x1b[2m[\x1b[0m` : "[",
572
- width: 1
573
- };
568
+ if (term.min < 1) yield* punctuation("[", "optionalOpen");
574
569
  for (let i = 0; i < Math.max(1, term.min); i++) {
575
- if (i > 0) yield {
576
- text: " ",
577
- width: 1
578
- };
570
+ if (i > 0) yield space;
579
571
  yield* formatUsageTerms(term.terms, options);
580
572
  }
581
- yield {
582
- text: options?.colors ? `\x1b[2m...\x1b[0m` : "...",
583
- width: 3
584
- };
585
- if (term.min < 1) yield {
586
- text: options?.colors ? `\x1b[2m]\x1b[0m` : "]",
587
- width: 1
588
- };
589
- } else if (term.type === "literal") yield {
590
- text: term.value,
591
- width: getDisplayWidth(term.value)
592
- };
593
- else if (term.type === "passthrough") {
594
- const text = "[...]";
595
- yield {
596
- text: options?.colors ? `\x1b[2m${text}\x1b[0m` : text,
597
- width: 5
598
- };
599
- } else if (term.type === "ellipsis") {
600
- const text = "...";
601
- yield {
602
- text: options?.colors ? `\x1b[2m${text}\x1b[0m` : text,
603
- width: 3
604
- };
605
- } else throw new TypeError(`Unknown usage term type: ${term["type"]}.`);
573
+ yield* punctuation("...", "ellipsis");
574
+ if (term.min < 1) yield* punctuation("]", "optionalClose");
575
+ } else if (term.type === "literal") yield* leaf({
576
+ type: "value",
577
+ value: term.value
578
+ }, "literal");
579
+ else if (term.type === "passthrough") yield* punctuation("[...]", "passthrough");
580
+ else if (term.type === "ellipsis") yield* punctuation("...", "ellipsis");
581
+ else throw new TypeError(`Unknown usage term type: ${term["type"]}.`);
606
582
  }
607
583
 
608
584
  //#endregion