@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/usage.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { getDisplayWidth } from "./displaywidth.js";
2
- import { cacheTerminalTheme, formatTerminalTerm, fragmentTokens, serializeTokens } from "./terminal-internal.js";
2
+ import { cacheTerminalTheme, formatTerminalLeaf, fragmentTokens, serializeTokens } from "./terminal-internal.js";
3
3
  import { validateProgramName } from "./validate.js";
4
4
 
5
5
  //#region src/usage.ts
@@ -204,23 +204,17 @@ function formatUsage(programName, usage, options = {}) {
204
204
  if (lines.length > 0) return lines.join("\n");
205
205
  }
206
206
  }
207
- const programTokens = layoutUsageTokens(fragmentTokens(formatTerminalTerm({
208
- type: "programName",
209
- programName
210
- }, options.theme)), options);
211
- const terms = [...formatUsageTerms(usage, options)];
212
- if (terms.length === 0) return serializeTokens(programTokens, options.colors);
213
- const width = programTokens.reduce((n, token) => token.width === -1 ? 0 : n + token.width, 0);
214
- const separator = options.maxWidth != null && width + 1 + terms[0].width > options.maxWidth ? {
215
- text: "\n",
216
- width: -1,
217
- scopes: []
218
- } : {
219
- text: " ",
220
- width: 1,
221
- scopes: []
222
- };
223
- return serializeTokens([...programTokens, separator], options.colors) + wrapUsageTokens(terms, options, separator.width === -1 ? 0 : width + 1);
207
+ return wrapUsageTokens(function* () {
208
+ yield* usageLeafTokens({
209
+ type: "programName",
210
+ programName
211
+ }, options);
212
+ yield {
213
+ ...usageSpace,
214
+ programBoundary: true
215
+ };
216
+ yield* formatUsageTerms(usage, options);
217
+ }(), options);
224
218
  }
225
219
  /**
226
220
  * Normalizes a usage description by flattening nested exclusive terms,
@@ -450,14 +444,23 @@ function filterUsageForDisplay(usage, isHidden = isUsageHidden) {
450
444
  }
451
445
  return terms;
452
446
  }
447
+ const usageSpace = {
448
+ text: " ",
449
+ width: 1,
450
+ scopes: [],
451
+ separator: true
452
+ };
453
+ function* usageLeafTokens(term, options, usage) {
454
+ const { fragment, defaultOrigin } = formatTerminalLeaf(term, options.theme, false, usage);
455
+ for (const token of fragmentTokens(fragment)) yield {
456
+ ...token,
457
+ legacyWhitespace: defaultOrigin
458
+ };
459
+ }
453
460
  function* formatUsageTerms(terms, options) {
454
461
  let i = 0;
455
462
  for (const t of terms) {
456
- if (i > 0) yield {
457
- text: " ",
458
- width: 1,
459
- scopes: []
460
- };
463
+ if (i > 0) yield usageSpace;
461
464
  yield* formatUsageTermInternal(t, options);
462
465
  i++;
463
466
  }
@@ -483,21 +486,57 @@ function formatUsageTerm(term, options = {}) {
483
486
  if (visibleTerms.length < 1) return "";
484
487
  return wrapUsageTokens(formatUsageTermInternal(visibleTerms[0], options), options);
485
488
  }
486
- function wrapUsageTokens(input, options, initialWidth = 0) {
487
- return serializeTokens(layoutUsageTokens(input, options, initialWidth), options.colors);
489
+ function wrapUsageTokens(input, options) {
490
+ return serializeTokens(layoutUsageTokens(input, options), options.colors);
488
491
  }
489
- function layoutUsageTokens(input, options, initialWidth = 0) {
490
- let lineWidth = initialWidth;
491
- const output = [];
492
+ /** Keeps one layout-owned space between nonempty outputs on the same line. */
493
+ function* normalizeUsageSeparators(input) {
494
+ let previous;
495
+ let pending = [];
492
496
  for (const token of input) {
497
+ if (token.separator || token.text === "") {
498
+ pending.push(token);
499
+ continue;
500
+ }
501
+ let separated = false;
502
+ for (const item of pending) if (!item.separator) yield item;
503
+ else if (!separated && previous != null && previous.width !== -1 && token.width !== -1) {
504
+ yield item;
505
+ separated = true;
506
+ }
507
+ pending = [];
508
+ yield token;
509
+ previous = token;
510
+ }
511
+ for (const item of pending) if (!item.separator) yield item;
512
+ }
513
+ function layoutUsageTokens(input, options) {
514
+ let lineWidth = 0;
515
+ const output = [];
516
+ const tokens = [...normalizeUsageSeparators(input)];
517
+ for (const [index, token] of tokens.entries()) {
518
+ if (token.programBoundary && options.maxWidth != null) {
519
+ const next = tokens.find((item, i) => i > index && item.text !== "");
520
+ if (next != null && lineWidth + token.width + next.width > options.maxWidth) {
521
+ output.push({
522
+ text: "\n",
523
+ width: -1,
524
+ scopes: []
525
+ });
526
+ lineWidth = 0;
527
+ continue;
528
+ }
529
+ }
493
530
  if (token.width === -1) {
494
531
  output.push(token);
495
532
  lineWidth = 0;
496
533
  continue;
497
534
  }
498
- if (options.maxWidth != null && lineWidth > 0 && lineWidth + token.width > options.maxWidth) {
499
- const last = output.at(-1);
500
- if (last?.text.endsWith(" ") && (!options.colors || last.scopes.length === 0)) output[output.length - 1] = {
535
+ if (options.maxWidth != null && lineWidth > 0 && token.width > 0 && lineWidth + token.width > options.maxWidth) {
536
+ let lastIndex = output.length - 1;
537
+ while (lastIndex >= 0 && output[lastIndex].text === "") lastIndex--;
538
+ const last = output[lastIndex];
539
+ if (last?.text.endsWith(" ") && (last.separator || last.legacyWhitespace && (!options.colors || last.scopes.length === 0))) output[lastIndex] = {
501
540
  ...last,
502
541
  text: last.text.slice(0, -1),
503
542
  width: last.width - 1
@@ -508,7 +547,7 @@ function layoutUsageTokens(input, options, initialWidth = 0) {
508
547
  scopes: []
509
548
  });
510
549
  lineWidth = 0;
511
- if (token.text === " ") continue;
550
+ if (token.separator || token.legacyWhitespace && token.text === " ") continue;
512
551
  }
513
552
  output.push(token);
514
553
  lineWidth += token.width;
@@ -517,7 +556,7 @@ function layoutUsageTokens(input, options, initialWidth = 0) {
517
556
  }
518
557
  function* formatUsageTermInternal(term, options) {
519
558
  function leaf(t, usage) {
520
- return fragmentTokens(formatTerminalTerm(t, options.theme, false, usage));
559
+ return usageLeafTokens(t, options, usage);
521
560
  }
522
561
  function punctuation(punctuation$1, kind) {
523
562
  return leaf({
@@ -526,11 +565,7 @@ function* formatUsageTermInternal(term, options) {
526
565
  kind
527
566
  });
528
567
  }
529
- const space = {
530
- text: " ",
531
- width: 1,
532
- scopes: []
533
- };
568
+ const space = usageSpace;
534
569
  if (term.type === "argument") yield* leaf({
535
570
  type: "metavar",
536
571
  metavar: term.metavar
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/core",
3
- "version": "1.3.0-dev.2542",
3
+ "version": "1.3.0-dev.2558",
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.2542+1201b1a0",
240
+ "@optique/env": "1.3.0-dev.2558+85f681ee",
241
241
  "@types/node": "^24.0.0",
242
242
  "fast-check": "^4.7.0",
243
243
  "tsdown": "^0.13.0",