@optique/core 1.3.0-dev.2540 → 1.3.0-dev.2554

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.cjs CHANGED
@@ -196,8 +196,8 @@ function formatUsage(programName, usage, options = {}) {
196
196
  if (usage.length > 0 && usage.slice(0, -1).every((t) => t.type === "command") && lastTerm.type === "exclusive" && lastTerm.terms.every((t) => t.length > 0 && (t[0].type === "command" || t[0].type === "option" || t[0].type === "argument" || t[0].type === "optional" && t[0].terms.length === 1 && (t[0].terms[0].type === "command" || t[0].terms[0].type === "option" || t[0].terms[0].type === "argument")))) {
197
197
  const lines = [];
198
198
  for (let command of lastTerm.terms) {
199
- const firstTerm = command[0];
200
- if (firstTerm?.type === "command" && isUsageHidden(firstTerm.hidden)) continue;
199
+ const firstTerm$1 = command[0];
200
+ if (firstTerm$1?.type === "command" && isUsageHidden(firstTerm$1.hidden)) continue;
201
201
  if (usage.length > 1) command = [...usage.slice(0, -1), ...command];
202
202
  lines.push(formatUsage(programName, command, options));
203
203
  }
@@ -208,10 +208,13 @@ function formatUsage(programName, usage, options = {}) {
208
208
  type: "programName",
209
209
  programName
210
210
  }, options.theme)), options);
211
- const terms = [...formatUsageTerms(usage, options)];
211
+ const terms = [...normalizeUsageSeparators(formatUsageTerms(usage, options))];
212
212
  if (terms.length === 0) return require_terminal_internal.serializeTokens(programTokens, options.colors);
213
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 ? {
214
+ const firstTerm = terms.find((token) => token.text !== "");
215
+ const lastProgram = programTokens.findLast((token) => token.text !== "");
216
+ if (lastProgram == null || firstTerm == null || lastProgram.width === -1 || firstTerm.width === -1) return require_terminal_internal.serializeTokens(programTokens, options.colors) + wrapUsageTokens(terms, options, width);
217
+ const separator = options.maxWidth != null && width + 1 + firstTerm.width > options.maxWidth ? {
215
218
  text: "\n",
216
219
  width: -1,
217
220
  scopes: []
@@ -456,7 +459,8 @@ function* formatUsageTerms(terms, options) {
456
459
  if (i > 0) yield {
457
460
  text: " ",
458
461
  width: 1,
459
- scopes: []
462
+ scopes: [],
463
+ separator: true
460
464
  };
461
465
  yield* formatUsageTermInternal(t, options);
462
466
  i++;
@@ -486,11 +490,37 @@ function formatUsageTerm(term, options = {}) {
486
490
  function wrapUsageTokens(input, options, initialWidth = 0) {
487
491
  return require_terminal_internal.serializeTokens(layoutUsageTokens(input, options, initialWidth), options.colors);
488
492
  }
493
+ /** Keeps one layout-owned space between nonempty outputs on the same line. */
494
+ function* normalizeUsageSeparators(input) {
495
+ let previous;
496
+ let pending = [];
497
+ for (const token of input) {
498
+ if (token.separator || token.text === "") {
499
+ pending.push(token);
500
+ continue;
501
+ }
502
+ let separated = false;
503
+ for (const item of pending) if (!item.separator) yield item;
504
+ else if (!separated && previous != null && previous.width !== -1 && token.width !== -1) {
505
+ yield item;
506
+ separated = true;
507
+ }
508
+ pending = [];
509
+ yield token;
510
+ previous = token;
511
+ }
512
+ for (const item of pending) if (!item.separator) yield item;
513
+ }
489
514
  function layoutUsageTokens(input, options, initialWidth = 0) {
490
515
  let lineWidth = initialWidth;
491
516
  const output = [];
492
- for (const token of input) {
493
- if (options.maxWidth != null && lineWidth > 0 && lineWidth + token.width > options.maxWidth) {
517
+ for (const token of normalizeUsageSeparators(input)) {
518
+ if (token.width === -1) {
519
+ output.push(token);
520
+ lineWidth = 0;
521
+ continue;
522
+ }
523
+ if (options.maxWidth != null && lineWidth > 0 && token.width > 0 && lineWidth + token.width > options.maxWidth) {
494
524
  const last = output.at(-1);
495
525
  if (last?.text.endsWith(" ") && (!options.colors || last.scopes.length === 0)) output[output.length - 1] = {
496
526
  ...last,
@@ -524,7 +554,8 @@ function* formatUsageTermInternal(term, options) {
524
554
  const space = {
525
555
  text: " ",
526
556
  width: 1,
527
- scopes: []
557
+ scopes: [],
558
+ separator: true
528
559
  };
529
560
  if (term.type === "argument") yield* leaf({
530
561
  type: "metavar",
package/dist/usage.js CHANGED
@@ -196,8 +196,8 @@ function formatUsage(programName, usage, options = {}) {
196
196
  if (usage.length > 0 && usage.slice(0, -1).every((t) => t.type === "command") && lastTerm.type === "exclusive" && lastTerm.terms.every((t) => t.length > 0 && (t[0].type === "command" || t[0].type === "option" || t[0].type === "argument" || t[0].type === "optional" && t[0].terms.length === 1 && (t[0].terms[0].type === "command" || t[0].terms[0].type === "option" || t[0].terms[0].type === "argument")))) {
197
197
  const lines = [];
198
198
  for (let command of lastTerm.terms) {
199
- const firstTerm = command[0];
200
- if (firstTerm?.type === "command" && isUsageHidden(firstTerm.hidden)) continue;
199
+ const firstTerm$1 = command[0];
200
+ if (firstTerm$1?.type === "command" && isUsageHidden(firstTerm$1.hidden)) continue;
201
201
  if (usage.length > 1) command = [...usage.slice(0, -1), ...command];
202
202
  lines.push(formatUsage(programName, command, options));
203
203
  }
@@ -208,10 +208,13 @@ function formatUsage(programName, usage, options = {}) {
208
208
  type: "programName",
209
209
  programName
210
210
  }, options.theme)), options);
211
- const terms = [...formatUsageTerms(usage, options)];
211
+ const terms = [...normalizeUsageSeparators(formatUsageTerms(usage, options))];
212
212
  if (terms.length === 0) return serializeTokens(programTokens, options.colors);
213
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 ? {
214
+ const firstTerm = terms.find((token) => token.text !== "");
215
+ const lastProgram = programTokens.findLast((token) => token.text !== "");
216
+ if (lastProgram == null || firstTerm == null || lastProgram.width === -1 || firstTerm.width === -1) return serializeTokens(programTokens, options.colors) + wrapUsageTokens(terms, options, width);
217
+ const separator = options.maxWidth != null && width + 1 + firstTerm.width > options.maxWidth ? {
215
218
  text: "\n",
216
219
  width: -1,
217
220
  scopes: []
@@ -456,7 +459,8 @@ function* formatUsageTerms(terms, options) {
456
459
  if (i > 0) yield {
457
460
  text: " ",
458
461
  width: 1,
459
- scopes: []
462
+ scopes: [],
463
+ separator: true
460
464
  };
461
465
  yield* formatUsageTermInternal(t, options);
462
466
  i++;
@@ -486,11 +490,37 @@ function formatUsageTerm(term, options = {}) {
486
490
  function wrapUsageTokens(input, options, initialWidth = 0) {
487
491
  return serializeTokens(layoutUsageTokens(input, options, initialWidth), options.colors);
488
492
  }
493
+ /** Keeps one layout-owned space between nonempty outputs on the same line. */
494
+ function* normalizeUsageSeparators(input) {
495
+ let previous;
496
+ let pending = [];
497
+ for (const token of input) {
498
+ if (token.separator || token.text === "") {
499
+ pending.push(token);
500
+ continue;
501
+ }
502
+ let separated = false;
503
+ for (const item of pending) if (!item.separator) yield item;
504
+ else if (!separated && previous != null && previous.width !== -1 && token.width !== -1) {
505
+ yield item;
506
+ separated = true;
507
+ }
508
+ pending = [];
509
+ yield token;
510
+ previous = token;
511
+ }
512
+ for (const item of pending) if (!item.separator) yield item;
513
+ }
489
514
  function layoutUsageTokens(input, options, initialWidth = 0) {
490
515
  let lineWidth = initialWidth;
491
516
  const output = [];
492
- for (const token of input) {
493
- if (options.maxWidth != null && lineWidth > 0 && lineWidth + token.width > options.maxWidth) {
517
+ for (const token of normalizeUsageSeparators(input)) {
518
+ if (token.width === -1) {
519
+ output.push(token);
520
+ lineWidth = 0;
521
+ continue;
522
+ }
523
+ if (options.maxWidth != null && lineWidth > 0 && token.width > 0 && lineWidth + token.width > options.maxWidth) {
494
524
  const last = output.at(-1);
495
525
  if (last?.text.endsWith(" ") && (!options.colors || last.scopes.length === 0)) output[output.length - 1] = {
496
526
  ...last,
@@ -524,7 +554,8 @@ function* formatUsageTermInternal(term, options) {
524
554
  const space = {
525
555
  text: " ",
526
556
  width: 1,
527
- scopes: []
557
+ scopes: [],
558
+ separator: true
528
559
  };
529
560
  if (term.type === "argument") yield* leaf({
530
561
  type: "metavar",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/core",
3
- "version": "1.3.0-dev.2540",
3
+ "version": "1.3.0-dev.2554",
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.2540+a24fec00",
240
+ "@optique/env": "1.3.0-dev.2554+5fdcbbad",
241
241
  "@types/node": "^24.0.0",
242
242
  "fast-check": "^4.7.0",
243
243
  "tsdown": "^0.13.0",