@optique/core 1.0.0-dev.1590 → 1.0.0-dev.1596

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.
@@ -0,0 +1,44 @@
1
+
2
+ //#region src/displaywidth.ts
3
+ const ansiRegex = /\x1B(?:\[[0-9;:]*[@-~]|\][^\x1B\x07]*(?:\x1B\\|\x07))/g;
4
+ const segmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
5
+ /**
6
+ * Computes the terminal display width of a string, accounting for
7
+ * East Asian wide characters, combining marks, emoji, and ANSI escapes.
8
+ *
9
+ * @param text The string to measure.
10
+ * @returns The number of terminal columns the string occupies.
11
+ * @internal
12
+ */
13
+ function getDisplayWidth(text) {
14
+ const stripped = text.replace(ansiRegex, "");
15
+ let width = 0;
16
+ for (const { segment } of segmenter.segment(stripped)) width += graphemeWidth(segment);
17
+ return width;
18
+ }
19
+ const zeroWidthRegex = /^[\p{Cf}\p{Mn}\p{Me}]+$/u;
20
+ const emojiPresentationRegex = /\p{Emoji_Presentation}/u;
21
+ const emojiWithVS16Regex = /\p{Emoji}\uFE0F/u;
22
+ const keycapEmojiRegex = /^[#*0-9]\uFE0F?\u20E3$/u;
23
+ const textPresentationRegex = /\uFE0E/u;
24
+ const regionalIndicatorRegex = /[\u{1F1E6}-\u{1F1FF}]/u;
25
+ function graphemeWidth(grapheme) {
26
+ const cp = grapheme.codePointAt(0);
27
+ if (cp == null) return 0;
28
+ if (cp === 9) return 1;
29
+ if (cp < 32 || cp >= 127 && cp < 160) return 0;
30
+ if (zeroWidthRegex.test(grapheme)) return 0;
31
+ if (keycapEmojiRegex.test(grapheme) || !textPresentationRegex.test(grapheme) && (emojiPresentationRegex.test(grapheme) || emojiWithVS16Regex.test(grapheme) || regionalIndicatorRegex.test(grapheme))) return 2;
32
+ let width = isEastAsianWide(cp) ? 2 : 1;
33
+ for (let i = cp > 65535 ? 2 : 1; i < grapheme.length; i++) {
34
+ const c = grapheme.charCodeAt(i);
35
+ if (c === 65438 || c === 65439) width += 1;
36
+ }
37
+ return width;
38
+ }
39
+ function isEastAsianWide(cp) {
40
+ return cp >= 4352 && cp <= 4447 || cp >= 9001 && cp <= 9002 || cp >= 9776 && cp <= 9783 || cp >= 9866 && cp <= 9871 || cp >= 11904 && cp <= 12255 || cp >= 12272 && cp <= 12350 || cp >= 12353 && cp <= 40959 || cp >= 40960 && cp <= 42191 || cp >= 43360 && cp <= 43391 || cp >= 44032 && cp <= 55215 || cp >= 63744 && cp <= 64255 || cp >= 65040 && cp <= 65049 || cp >= 65072 && cp <= 65135 || cp >= 65281 && cp <= 65376 || cp >= 65504 && cp <= 65510 || cp >= 94176 && cp <= 101759 || cp >= 110576 && cp <= 111359 || cp >= 127488 && cp <= 127490 || cp >= 127504 && cp <= 127547 || cp >= 127552 && cp <= 127560 || cp >= 127568 && cp <= 127569 || cp >= 127584 && cp <= 127589 || cp >= 131072 && cp <= 196605 || cp >= 196608 && cp <= 262141;
41
+ }
42
+
43
+ //#endregion
44
+ exports.getDisplayWidth = getDisplayWidth;
@@ -0,0 +1,43 @@
1
+ //#region src/displaywidth.ts
2
+ const ansiRegex = /\x1B(?:\[[0-9;:]*[@-~]|\][^\x1B\x07]*(?:\x1B\\|\x07))/g;
3
+ const segmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
4
+ /**
5
+ * Computes the terminal display width of a string, accounting for
6
+ * East Asian wide characters, combining marks, emoji, and ANSI escapes.
7
+ *
8
+ * @param text The string to measure.
9
+ * @returns The number of terminal columns the string occupies.
10
+ * @internal
11
+ */
12
+ function getDisplayWidth(text) {
13
+ const stripped = text.replace(ansiRegex, "");
14
+ let width = 0;
15
+ for (const { segment } of segmenter.segment(stripped)) width += graphemeWidth(segment);
16
+ return width;
17
+ }
18
+ const zeroWidthRegex = /^[\p{Cf}\p{Mn}\p{Me}]+$/u;
19
+ const emojiPresentationRegex = /\p{Emoji_Presentation}/u;
20
+ const emojiWithVS16Regex = /\p{Emoji}\uFE0F/u;
21
+ const keycapEmojiRegex = /^[#*0-9]\uFE0F?\u20E3$/u;
22
+ const textPresentationRegex = /\uFE0E/u;
23
+ const regionalIndicatorRegex = /[\u{1F1E6}-\u{1F1FF}]/u;
24
+ function graphemeWidth(grapheme) {
25
+ const cp = grapheme.codePointAt(0);
26
+ if (cp == null) return 0;
27
+ if (cp === 9) return 1;
28
+ if (cp < 32 || cp >= 127 && cp < 160) return 0;
29
+ if (zeroWidthRegex.test(grapheme)) return 0;
30
+ if (keycapEmojiRegex.test(grapheme) || !textPresentationRegex.test(grapheme) && (emojiPresentationRegex.test(grapheme) || emojiWithVS16Regex.test(grapheme) || regionalIndicatorRegex.test(grapheme))) return 2;
31
+ let width = isEastAsianWide(cp) ? 2 : 1;
32
+ for (let i = cp > 65535 ? 2 : 1; i < grapheme.length; i++) {
33
+ const c = grapheme.charCodeAt(i);
34
+ if (c === 65438 || c === 65439) width += 1;
35
+ }
36
+ return width;
37
+ }
38
+ function isEastAsianWide(cp) {
39
+ return cp >= 4352 && cp <= 4447 || cp >= 9001 && cp <= 9002 || cp >= 9776 && cp <= 9783 || cp >= 9866 && cp <= 9871 || cp >= 11904 && cp <= 12255 || cp >= 12272 && cp <= 12350 || cp >= 12353 && cp <= 40959 || cp >= 40960 && cp <= 42191 || cp >= 43360 && cp <= 43391 || cp >= 44032 && cp <= 55215 || cp >= 63744 && cp <= 64255 || cp >= 65040 && cp <= 65049 || cp >= 65072 && cp <= 65135 || cp >= 65281 && cp <= 65376 || cp >= 65504 && cp <= 65510 || cp >= 94176 && cp <= 101759 || cp >= 110576 && cp <= 111359 || cp >= 127488 && cp <= 127490 || cp >= 127504 && cp <= 127547 || cp >= 127552 && cp <= 127560 || cp >= 127568 && cp <= 127569 || cp >= 127584 && cp <= 127589 || cp >= 131072 && cp <= 196605 || cp >= 196608 && cp <= 262141;
40
+ }
41
+
42
+ //#endregion
43
+ export { getDisplayWidth };
package/dist/doc.cjs CHANGED
@@ -1,3 +1,4 @@
1
+ const require_displaywidth = require('./displaywidth.cjs');
1
2
  const require_message = require('./message.cjs');
2
3
  const require_validate = require('./validate.cjs');
3
4
  const require_usage = require('./usage.cjs');
@@ -237,18 +238,19 @@ function formatDocPage(programName, page, options = {}) {
237
238
  if (needsDescColumn) {
238
239
  if (options.showDefault && page.sections.some((s) => s.entries.some((e) => hasContent(e.default)))) {
239
240
  const prefix = typeof options.showDefault === "object" ? options.showDefault.prefix ?? " [" : " [";
240
- minDescWidth = Math.max(minDescWidth, prefix.length);
241
+ minDescWidth = Math.max(minDescWidth, require_displaywidth.getDisplayWidth(prefix));
241
242
  }
242
243
  if (options.showChoices && page.sections.some((s) => s.entries.some((e) => hasContent(e.choices)))) {
243
244
  const prefix = typeof options.showChoices === "object" ? options.showChoices.prefix ?? " (" : " (";
244
245
  const label = typeof options.showChoices === "object" ? options.showChoices.label ?? "choices: " : "choices: ";
245
- minDescWidth = Math.max(minDescWidth, prefix.length + label.length);
246
+ minDescWidth = Math.max(minDescWidth, require_displaywidth.getDisplayWidth(prefix) + require_displaywidth.getDisplayWidth(label));
246
247
  }
247
248
  }
248
249
  const splitEntryMin = termIndent + 2 + Math.max(2, 2 * minDescWidth - 1);
249
250
  const fixedEntryMin = termIndent + 2 + termWidth + minDescWidth;
250
251
  const entryMin = needsDescColumn ? Math.min(splitEntryMin, fixedEntryMin) : hasEntries ? termIndent + 1 : 1;
251
- const usageMin = page.usage != null ? 7 + Math.max(programName.length, Math.min(maxVisibleAtomicWidth(page.usage), programName.length + 7)) : 1;
252
+ const programNameWidth = require_displaywidth.getDisplayWidth(programName);
253
+ const usageMin = page.usage != null ? 7 + Math.max(programNameWidth, Math.min(maxVisibleAtomicWidth(page.usage), programNameWidth + 7)) : 1;
252
254
  let sectionMin = 1;
253
255
  if (hasContent(page.examples)) sectionMin = Math.max(sectionMin, 9);
254
256
  if (hasContent(page.author)) sectionMin = Math.max(sectionMin, 7);
@@ -337,19 +339,21 @@ function formatDocPage(programName, page, options = {}) {
337
339
  if (options.showDefault && hasContent(entry.default)) {
338
340
  const prefix = typeof options.showDefault === "object" ? options.showDefault.prefix ?? " [" : " [";
339
341
  const suffix = typeof options.showDefault === "object" ? options.showDefault.suffix ?? "]" : "]";
342
+ const prefixWidth = require_displaywidth.getDisplayWidth(prefix);
343
+ const suffixWidth = require_displaywidth.getDisplayWidth(suffix);
340
344
  let defaultStartWidth;
341
345
  if (descColumnWidth != null) {
342
346
  const lastW = lastLineVisibleLength(description);
343
347
  const effectiveLastW = lastW + currentExtraOffset();
344
- if (effectiveLastW + prefix.length >= descColumnWidth) {
348
+ if (effectiveLastW + prefixWidth >= descColumnWidth) {
345
349
  description += "\n";
346
- defaultStartWidth = prefix.length;
347
- } else defaultStartWidth = effectiveLastW + prefix.length;
350
+ defaultStartWidth = prefixWidth;
351
+ } else defaultStartWidth = effectiveLastW + prefixWidth;
348
352
  }
349
353
  const defaultFormatOptions = {
350
354
  colors: options.colors ? { resetSuffix: "\x1B[2m" } : false,
351
355
  quotes: !options.colors,
352
- maxWidth: descColumnWidth == null ? void 0 : descColumnWidth - suffix.length,
356
+ maxWidth: descColumnWidth == null ? void 0 : descColumnWidth - suffixWidth,
353
357
  startWidth: defaultStartWidth
354
358
  };
355
359
  const defaultContent = require_message.formatMessage(entry.default, defaultFormatOptions);
@@ -378,11 +382,14 @@ function formatDocPage(programName, page, options = {}) {
378
382
  }
379
383
  if (truncated) truncatedTerms = [...terms.slice(0, cutIndex), require_message.text(", ...")];
380
384
  }
385
+ const choicesPrefixWidth = require_displaywidth.getDisplayWidth(prefix);
386
+ const choicesSuffixWidth = require_displaywidth.getDisplayWidth(suffix);
387
+ const choicesLabelWidth = require_displaywidth.getDisplayWidth(label);
381
388
  let choicesStartWidth;
382
389
  if (descColumnWidth != null) {
383
390
  const lastW = lastLineVisibleLength(description);
384
391
  const effectiveLastW = lastW + currentExtraOffset();
385
- const prefixLabelLen = prefix.length + label.length;
392
+ const prefixLabelLen = choicesPrefixWidth + choicesLabelWidth;
386
393
  if (effectiveLastW + prefixLabelLen >= descColumnWidth) {
387
394
  description += "\n";
388
395
  choicesStartWidth = prefixLabelLen;
@@ -391,7 +398,7 @@ function formatDocPage(programName, page, options = {}) {
391
398
  const choicesFormatOptions = {
392
399
  colors: options.colors ? { resetSuffix: "\x1B[2m" } : false,
393
400
  quotes: false,
394
- maxWidth: descColumnWidth == null ? void 0 : descColumnWidth - suffix.length,
401
+ maxWidth: descColumnWidth == null ? void 0 : descColumnWidth - choicesSuffixWidth,
395
402
  startWidth: choicesStartWidth
396
403
  };
397
404
  const choicesDisplay = require_message.formatMessage(truncatedTerms, choicesFormatOptions);
@@ -461,16 +468,16 @@ function maxVisibleAtomicWidth(usage) {
461
468
  let max = 0;
462
469
  for (const term of usage) switch (term.type) {
463
470
  case "argument":
464
- if (!require_usage.isUsageHidden(term.hidden)) max = Math.max(max, term.metavar.length);
471
+ if (!require_usage.isUsageHidden(term.hidden)) max = Math.max(max, require_displaywidth.getDisplayWidth(term.metavar));
465
472
  break;
466
473
  case "option":
467
474
  if (!require_usage.isUsageHidden(term.hidden) && term.names.length > 0) {
468
- for (const name of term.names) max = Math.max(max, name.length);
469
- if (term.metavar != null) max = Math.max(max, term.metavar.length);
475
+ for (const name of term.names) max = Math.max(max, require_displaywidth.getDisplayWidth(name));
476
+ if (term.metavar != null) max = Math.max(max, require_displaywidth.getDisplayWidth(term.metavar));
470
477
  }
471
478
  break;
472
479
  case "command":
473
- if (!require_usage.isUsageHidden(term.hidden)) max = Math.max(max, term.name.length);
480
+ if (!require_usage.isUsageHidden(term.hidden)) max = Math.max(max, require_displaywidth.getDisplayWidth(term.name));
474
481
  break;
475
482
  case "passthrough":
476
483
  if (!require_usage.isUsageHidden(term.hidden)) max = Math.max(max, 5);
@@ -491,7 +498,7 @@ function maxVisibleAtomicWidth(usage) {
491
498
  }
492
499
  break;
493
500
  case "literal":
494
- if (term.value !== "") max = Math.max(max, term.value.length);
501
+ if (term.value !== "") max = Math.max(max, require_displaywidth.getDisplayWidth(term.value));
495
502
  break;
496
503
  case "ellipsis":
497
504
  max = Math.max(max, 3);
@@ -499,16 +506,15 @@ function maxVisibleAtomicWidth(usage) {
499
506
  }
500
507
  return max;
501
508
  }
502
- const ansiEscapeCodeRegex = /\x1B\[[0-9;]*[a-zA-Z]/g;
503
509
  function ansiAwareRightPad(text$1, length, char = " ") {
504
- const strippedText = text$1.replace(ansiEscapeCodeRegex, "");
505
- if (strippedText.length >= length) return text$1;
506
- return text$1 + char.repeat(length - strippedText.length);
510
+ const visibleWidth = lastLineVisibleLength(text$1);
511
+ if (visibleWidth >= length) return text$1;
512
+ return text$1 + char.repeat(length - visibleWidth);
507
513
  }
508
514
  function lastLineVisibleLength(text$1) {
509
515
  const lastNewline = text$1.lastIndexOf("\n");
510
516
  const lastLine = lastNewline === -1 ? text$1 : text$1.slice(lastNewline + 1);
511
- return lastLine.replace(ansiEscapeCodeRegex, "").length;
517
+ return require_displaywidth.getDisplayWidth(lastLine);
512
518
  }
513
519
 
514
520
  //#endregion
package/dist/doc.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { getDisplayWidth } from "./displaywidth.js";
1
2
  import { cloneMessage, formatMessage, text } from "./message.js";
2
3
  import { validateLabel, validateProgramName } from "./validate.js";
3
4
  import { cloneUsageTerm, formatUsage, formatUsageTerm, isDocHidden, isUsageHidden } from "./usage.js";
@@ -237,18 +238,19 @@ function formatDocPage(programName, page, options = {}) {
237
238
  if (needsDescColumn) {
238
239
  if (options.showDefault && page.sections.some((s) => s.entries.some((e) => hasContent(e.default)))) {
239
240
  const prefix = typeof options.showDefault === "object" ? options.showDefault.prefix ?? " [" : " [";
240
- minDescWidth = Math.max(minDescWidth, prefix.length);
241
+ minDescWidth = Math.max(minDescWidth, getDisplayWidth(prefix));
241
242
  }
242
243
  if (options.showChoices && page.sections.some((s) => s.entries.some((e) => hasContent(e.choices)))) {
243
244
  const prefix = typeof options.showChoices === "object" ? options.showChoices.prefix ?? " (" : " (";
244
245
  const label = typeof options.showChoices === "object" ? options.showChoices.label ?? "choices: " : "choices: ";
245
- minDescWidth = Math.max(minDescWidth, prefix.length + label.length);
246
+ minDescWidth = Math.max(minDescWidth, getDisplayWidth(prefix) + getDisplayWidth(label));
246
247
  }
247
248
  }
248
249
  const splitEntryMin = termIndent + 2 + Math.max(2, 2 * minDescWidth - 1);
249
250
  const fixedEntryMin = termIndent + 2 + termWidth + minDescWidth;
250
251
  const entryMin = needsDescColumn ? Math.min(splitEntryMin, fixedEntryMin) : hasEntries ? termIndent + 1 : 1;
251
- const usageMin = page.usage != null ? 7 + Math.max(programName.length, Math.min(maxVisibleAtomicWidth(page.usage), programName.length + 7)) : 1;
252
+ const programNameWidth = getDisplayWidth(programName);
253
+ const usageMin = page.usage != null ? 7 + Math.max(programNameWidth, Math.min(maxVisibleAtomicWidth(page.usage), programNameWidth + 7)) : 1;
252
254
  let sectionMin = 1;
253
255
  if (hasContent(page.examples)) sectionMin = Math.max(sectionMin, 9);
254
256
  if (hasContent(page.author)) sectionMin = Math.max(sectionMin, 7);
@@ -337,19 +339,21 @@ function formatDocPage(programName, page, options = {}) {
337
339
  if (options.showDefault && hasContent(entry.default)) {
338
340
  const prefix = typeof options.showDefault === "object" ? options.showDefault.prefix ?? " [" : " [";
339
341
  const suffix = typeof options.showDefault === "object" ? options.showDefault.suffix ?? "]" : "]";
342
+ const prefixWidth = getDisplayWidth(prefix);
343
+ const suffixWidth = getDisplayWidth(suffix);
340
344
  let defaultStartWidth;
341
345
  if (descColumnWidth != null) {
342
346
  const lastW = lastLineVisibleLength(description);
343
347
  const effectiveLastW = lastW + currentExtraOffset();
344
- if (effectiveLastW + prefix.length >= descColumnWidth) {
348
+ if (effectiveLastW + prefixWidth >= descColumnWidth) {
345
349
  description += "\n";
346
- defaultStartWidth = prefix.length;
347
- } else defaultStartWidth = effectiveLastW + prefix.length;
350
+ defaultStartWidth = prefixWidth;
351
+ } else defaultStartWidth = effectiveLastW + prefixWidth;
348
352
  }
349
353
  const defaultFormatOptions = {
350
354
  colors: options.colors ? { resetSuffix: "\x1B[2m" } : false,
351
355
  quotes: !options.colors,
352
- maxWidth: descColumnWidth == null ? void 0 : descColumnWidth - suffix.length,
356
+ maxWidth: descColumnWidth == null ? void 0 : descColumnWidth - suffixWidth,
353
357
  startWidth: defaultStartWidth
354
358
  };
355
359
  const defaultContent = formatMessage(entry.default, defaultFormatOptions);
@@ -378,11 +382,14 @@ function formatDocPage(programName, page, options = {}) {
378
382
  }
379
383
  if (truncated) truncatedTerms = [...terms.slice(0, cutIndex), text(", ...")];
380
384
  }
385
+ const choicesPrefixWidth = getDisplayWidth(prefix);
386
+ const choicesSuffixWidth = getDisplayWidth(suffix);
387
+ const choicesLabelWidth = getDisplayWidth(label);
381
388
  let choicesStartWidth;
382
389
  if (descColumnWidth != null) {
383
390
  const lastW = lastLineVisibleLength(description);
384
391
  const effectiveLastW = lastW + currentExtraOffset();
385
- const prefixLabelLen = prefix.length + label.length;
392
+ const prefixLabelLen = choicesPrefixWidth + choicesLabelWidth;
386
393
  if (effectiveLastW + prefixLabelLen >= descColumnWidth) {
387
394
  description += "\n";
388
395
  choicesStartWidth = prefixLabelLen;
@@ -391,7 +398,7 @@ function formatDocPage(programName, page, options = {}) {
391
398
  const choicesFormatOptions = {
392
399
  colors: options.colors ? { resetSuffix: "\x1B[2m" } : false,
393
400
  quotes: false,
394
- maxWidth: descColumnWidth == null ? void 0 : descColumnWidth - suffix.length,
401
+ maxWidth: descColumnWidth == null ? void 0 : descColumnWidth - choicesSuffixWidth,
395
402
  startWidth: choicesStartWidth
396
403
  };
397
404
  const choicesDisplay = formatMessage(truncatedTerms, choicesFormatOptions);
@@ -461,16 +468,16 @@ function maxVisibleAtomicWidth(usage) {
461
468
  let max = 0;
462
469
  for (const term of usage) switch (term.type) {
463
470
  case "argument":
464
- if (!isUsageHidden(term.hidden)) max = Math.max(max, term.metavar.length);
471
+ if (!isUsageHidden(term.hidden)) max = Math.max(max, getDisplayWidth(term.metavar));
465
472
  break;
466
473
  case "option":
467
474
  if (!isUsageHidden(term.hidden) && term.names.length > 0) {
468
- for (const name of term.names) max = Math.max(max, name.length);
469
- if (term.metavar != null) max = Math.max(max, term.metavar.length);
475
+ for (const name of term.names) max = Math.max(max, getDisplayWidth(name));
476
+ if (term.metavar != null) max = Math.max(max, getDisplayWidth(term.metavar));
470
477
  }
471
478
  break;
472
479
  case "command":
473
- if (!isUsageHidden(term.hidden)) max = Math.max(max, term.name.length);
480
+ if (!isUsageHidden(term.hidden)) max = Math.max(max, getDisplayWidth(term.name));
474
481
  break;
475
482
  case "passthrough":
476
483
  if (!isUsageHidden(term.hidden)) max = Math.max(max, 5);
@@ -491,7 +498,7 @@ function maxVisibleAtomicWidth(usage) {
491
498
  }
492
499
  break;
493
500
  case "literal":
494
- if (term.value !== "") max = Math.max(max, term.value.length);
501
+ if (term.value !== "") max = Math.max(max, getDisplayWidth(term.value));
495
502
  break;
496
503
  case "ellipsis":
497
504
  max = Math.max(max, 3);
@@ -499,16 +506,15 @@ function maxVisibleAtomicWidth(usage) {
499
506
  }
500
507
  return max;
501
508
  }
502
- const ansiEscapeCodeRegex = /\x1B\[[0-9;]*[a-zA-Z]/g;
503
509
  function ansiAwareRightPad(text$1, length, char = " ") {
504
- const strippedText = text$1.replace(ansiEscapeCodeRegex, "");
505
- if (strippedText.length >= length) return text$1;
506
- return text$1 + char.repeat(length - strippedText.length);
510
+ const visibleWidth = lastLineVisibleLength(text$1);
511
+ if (visibleWidth >= length) return text$1;
512
+ return text$1 + char.repeat(length - visibleWidth);
507
513
  }
508
514
  function lastLineVisibleLength(text$1) {
509
515
  const lastNewline = text$1.lastIndexOf("\n");
510
516
  const lastLine = lastNewline === -1 ? text$1 : text$1.slice(lastNewline + 1);
511
- return lastLine.replace(ansiEscapeCodeRegex, "").length;
517
+ return getDisplayWidth(lastLine);
512
518
  }
513
519
 
514
520
  //#endregion
package/dist/message.cjs CHANGED
@@ -1,3 +1,4 @@
1
+ const require_displaywidth = require('./displaywidth.cjs');
1
2
 
2
3
  //#region src/message.ts
3
4
  /**
@@ -323,7 +324,7 @@ function formatMessage(msg, options = {}) {
323
324
  if (match == null) break;
324
325
  yield {
325
326
  text: match[0],
326
- width: match[0].length
327
+ width: require_displaywidth.getDisplayWidth(match[0])
327
328
  };
328
329
  }
329
330
  }
@@ -340,7 +341,7 @@ function formatMessage(msg, options = {}) {
340
341
  if (match == null) break;
341
342
  yield {
342
343
  text: match[0],
343
- width: match[0].length
344
+ width: require_displaywidth.getDisplayWidth(match[0])
344
345
  };
345
346
  }
346
347
  }
@@ -349,7 +350,7 @@ function formatMessage(msg, options = {}) {
349
350
  const name = useQuotes ? `\`${term.optionName}\`` : term.optionName;
350
351
  yield {
351
352
  text: useColors ? `\x1b[3m${name}${resetSequence}` : name,
352
- width: name.length
353
+ width: require_displaywidth.getDisplayWidth(name)
353
354
  };
354
355
  } else if (term.type === "optionNames") {
355
356
  const names = term.optionNames.map((name) => useQuotes ? `\`${name}\`` : name);
@@ -361,7 +362,7 @@ function formatMessage(msg, options = {}) {
361
362
  };
362
363
  yield {
363
364
  text: useColors ? `\x1b[3m${name}${resetSequence}` : name,
364
- width: name.length
365
+ width: require_displaywidth.getDisplayWidth(name)
365
366
  };
366
367
  i++;
367
368
  }
@@ -369,13 +370,13 @@ function formatMessage(msg, options = {}) {
369
370
  const metavar$1 = useQuotes ? `\`${term.metavar}\`` : term.metavar;
370
371
  yield {
371
372
  text: useColors ? `\x1b[1m${metavar$1}${resetSequence}` : metavar$1,
372
- width: metavar$1.length
373
+ width: require_displaywidth.getDisplayWidth(metavar$1)
373
374
  };
374
375
  } else if (term.type === "value") {
375
376
  const value$1 = useQuotes ? `${JSON.stringify(term.value)}` : term.value;
376
377
  yield {
377
378
  text: useColors ? `\x1b[32m${value$1}${resetSequence}` : value$1,
378
- width: value$1.length
379
+ width: require_displaywidth.getDisplayWidth(value$1)
379
380
  };
380
381
  } else if (term.type === "values") for (let i = 0; i < term.values.length; i++) {
381
382
  if (i > 0) yield {
@@ -385,20 +386,20 @@ function formatMessage(msg, options = {}) {
385
386
  const value$1 = useQuotes ? JSON.stringify(term.values[i]) : term.values[i];
386
387
  yield {
387
388
  text: useColors ? i <= 0 ? `\x1b[32m${value$1}` : i + 1 >= term.values.length ? `${value$1}${resetSequence}` : value$1 : value$1,
388
- width: value$1.length
389
+ width: require_displaywidth.getDisplayWidth(value$1)
389
390
  };
390
391
  }
391
392
  else if (term.type === "envVar") {
392
393
  const envVar$1 = useQuotes ? `\`${term.envVar}\`` : term.envVar;
393
394
  yield {
394
395
  text: useColors ? `\x1b[1;4m${envVar$1}${resetSequence}` : envVar$1,
395
- width: envVar$1.length
396
+ width: require_displaywidth.getDisplayWidth(envVar$1)
396
397
  };
397
398
  } else if (term.type === "commandLine") {
398
399
  const cmd = useQuotes ? `\`${term.commandLine}\`` : term.commandLine;
399
400
  yield {
400
401
  text: useColors ? `\x1b[36m${cmd}${resetSequence}` : cmd,
401
- width: cmd.length
402
+ width: require_displaywidth.getDisplayWidth(cmd)
402
403
  };
403
404
  } else if (term.type === "lineBreak") {
404
405
  yield {
@@ -413,11 +414,11 @@ function formatMessage(msg, options = {}) {
413
414
  const hyperlink = `\x1b]8;;${urlString}\x1b\\${displayText}\x1b]8;;\x1b\\${resetSuffix}`;
414
415
  yield {
415
416
  text: hyperlink,
416
- width: displayText.length
417
+ width: require_displaywidth.getDisplayWidth(displayText)
417
418
  };
418
419
  } else yield {
419
420
  text: displayText,
420
- width: displayText.length
421
+ width: require_displaywidth.getDisplayWidth(displayText)
421
422
  };
422
423
  } else throw new TypeError(`Invalid MessageTerm type: ${term["type"]}.`);
423
424
  }
package/dist/message.js CHANGED
@@ -1,3 +1,5 @@
1
+ import { getDisplayWidth } from "./displaywidth.js";
2
+
1
3
  //#region src/message.ts
2
4
  /**
3
5
  * Creates a deep clone of a {@link MessageTerm}. Most variants contain only
@@ -322,7 +324,7 @@ function formatMessage(msg, options = {}) {
322
324
  if (match == null) break;
323
325
  yield {
324
326
  text: match[0],
325
- width: match[0].length
327
+ width: getDisplayWidth(match[0])
326
328
  };
327
329
  }
328
330
  }
@@ -339,7 +341,7 @@ function formatMessage(msg, options = {}) {
339
341
  if (match == null) break;
340
342
  yield {
341
343
  text: match[0],
342
- width: match[0].length
344
+ width: getDisplayWidth(match[0])
343
345
  };
344
346
  }
345
347
  }
@@ -348,7 +350,7 @@ function formatMessage(msg, options = {}) {
348
350
  const name = useQuotes ? `\`${term.optionName}\`` : term.optionName;
349
351
  yield {
350
352
  text: useColors ? `\x1b[3m${name}${resetSequence}` : name,
351
- width: name.length
353
+ width: getDisplayWidth(name)
352
354
  };
353
355
  } else if (term.type === "optionNames") {
354
356
  const names = term.optionNames.map((name) => useQuotes ? `\`${name}\`` : name);
@@ -360,7 +362,7 @@ function formatMessage(msg, options = {}) {
360
362
  };
361
363
  yield {
362
364
  text: useColors ? `\x1b[3m${name}${resetSequence}` : name,
363
- width: name.length
365
+ width: getDisplayWidth(name)
364
366
  };
365
367
  i++;
366
368
  }
@@ -368,13 +370,13 @@ function formatMessage(msg, options = {}) {
368
370
  const metavar$1 = useQuotes ? `\`${term.metavar}\`` : term.metavar;
369
371
  yield {
370
372
  text: useColors ? `\x1b[1m${metavar$1}${resetSequence}` : metavar$1,
371
- width: metavar$1.length
373
+ width: getDisplayWidth(metavar$1)
372
374
  };
373
375
  } else if (term.type === "value") {
374
376
  const value$1 = useQuotes ? `${JSON.stringify(term.value)}` : term.value;
375
377
  yield {
376
378
  text: useColors ? `\x1b[32m${value$1}${resetSequence}` : value$1,
377
- width: value$1.length
379
+ width: getDisplayWidth(value$1)
378
380
  };
379
381
  } else if (term.type === "values") for (let i = 0; i < term.values.length; i++) {
380
382
  if (i > 0) yield {
@@ -384,20 +386,20 @@ function formatMessage(msg, options = {}) {
384
386
  const value$1 = useQuotes ? JSON.stringify(term.values[i]) : term.values[i];
385
387
  yield {
386
388
  text: useColors ? i <= 0 ? `\x1b[32m${value$1}` : i + 1 >= term.values.length ? `${value$1}${resetSequence}` : value$1 : value$1,
387
- width: value$1.length
389
+ width: getDisplayWidth(value$1)
388
390
  };
389
391
  }
390
392
  else if (term.type === "envVar") {
391
393
  const envVar$1 = useQuotes ? `\`${term.envVar}\`` : term.envVar;
392
394
  yield {
393
395
  text: useColors ? `\x1b[1;4m${envVar$1}${resetSequence}` : envVar$1,
394
- width: envVar$1.length
396
+ width: getDisplayWidth(envVar$1)
395
397
  };
396
398
  } else if (term.type === "commandLine") {
397
399
  const cmd = useQuotes ? `\`${term.commandLine}\`` : term.commandLine;
398
400
  yield {
399
401
  text: useColors ? `\x1b[36m${cmd}${resetSequence}` : cmd,
400
- width: cmd.length
402
+ width: getDisplayWidth(cmd)
401
403
  };
402
404
  } else if (term.type === "lineBreak") {
403
405
  yield {
@@ -412,11 +414,11 @@ function formatMessage(msg, options = {}) {
412
414
  const hyperlink = `\x1b]8;;${urlString}\x1b\\${displayText}\x1b]8;;\x1b\\${resetSuffix}`;
413
415
  yield {
414
416
  text: hyperlink,
415
- width: displayText.length
417
+ width: getDisplayWidth(displayText)
416
418
  };
417
419
  } else yield {
418
420
  text: displayText,
419
- width: displayText.length
421
+ width: getDisplayWidth(displayText)
420
422
  };
421
423
  } else throw new TypeError(`Invalid MessageTerm type: ${term["type"]}.`);
422
424
  }
package/dist/usage.cjs CHANGED
@@ -1,3 +1,4 @@
1
+ const require_displaywidth = require('./displaywidth.cjs');
1
2
  const require_validate = require('./validate.cjs');
2
3
 
3
4
  //#region src/usage.ts
@@ -195,7 +196,7 @@ function formatUsage(programName, usage, options = {}) {
195
196
  }
196
197
  }
197
198
  let output = options.colors ? `\x1b[1m${programName}\x1b[0m` : programName;
198
- let lineWidth = programName.length;
199
+ let lineWidth = require_displaywidth.getDisplayWidth(programName);
199
200
  let first = true;
200
201
  for (const { text, width } of formatUsageTerms(usage, options)) {
201
202
  if (first) {
@@ -466,24 +467,24 @@ function* formatUsageTermInternal(term, options) {
466
467
  const optionsSeparator = options.optionsSeparator ?? "/";
467
468
  if (term.type === "argument") yield {
468
469
  text: options?.colors ? `\x1b[4m${term.metavar}\x1b[0m` : term.metavar,
469
- width: term.metavar.length
470
+ width: require_displaywidth.getDisplayWidth(term.metavar)
470
471
  };
471
472
  else if (term.type === "option") if (options?.onlyShortestOptions) {
472
- const shortestName = term.names.reduce((a, b) => a.length <= b.length ? a : b);
473
+ const shortestName = term.names.reduce((a, b) => require_displaywidth.getDisplayWidth(a) <= require_displaywidth.getDisplayWidth(b) ? a : b);
473
474
  yield {
474
475
  text: options?.colors ? `\x1b[3m${shortestName}\x1b[0m` : shortestName,
475
- width: shortestName.length
476
+ width: require_displaywidth.getDisplayWidth(shortestName)
476
477
  };
477
478
  } else {
478
479
  let i = 0;
479
480
  for (const optionName of term.names) {
480
481
  if (i > 0) yield {
481
482
  text: options?.colors ? `\x1b[2m${optionsSeparator}\x1b[0m` : optionsSeparator,
482
- width: optionsSeparator.length
483
+ width: require_displaywidth.getDisplayWidth(optionsSeparator)
483
484
  };
484
485
  yield {
485
486
  text: options?.colors ? `\x1b[3m${optionName}\x1b[0m` : optionName,
486
- width: optionName.length
487
+ width: require_displaywidth.getDisplayWidth(optionName)
487
488
  };
488
489
  i++;
489
490
  }
@@ -494,13 +495,13 @@ function* formatUsageTermInternal(term, options) {
494
495
  };
495
496
  yield {
496
497
  text: options?.colors ? `\x1b[4m\x1b[2m${term.metavar}\x1b[0m` : term.metavar,
497
- width: term.metavar.length
498
+ width: require_displaywidth.getDisplayWidth(term.metavar)
498
499
  };
499
500
  }
500
501
  }
501
502
  else if (term.type === "command") yield {
502
503
  text: options?.colors ? `\x1b[1m${term.name}\x1b[0m` : term.name,
503
- width: term.name.length
504
+ width: require_displaywidth.getDisplayWidth(term.name)
504
505
  };
505
506
  else if (term.type === "optional") {
506
507
  yield {
@@ -562,7 +563,7 @@ function* formatUsageTermInternal(term, options) {
562
563
  };
563
564
  } else if (term.type === "literal") yield {
564
565
  text: term.value,
565
- width: term.value.length
566
+ width: require_displaywidth.getDisplayWidth(term.value)
566
567
  };
567
568
  else if (term.type === "passthrough") {
568
569
  const text = "[...]";
package/dist/usage.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { getDisplayWidth } from "./displaywidth.js";
1
2
  import { validateProgramName } from "./validate.js";
2
3
 
3
4
  //#region src/usage.ts
@@ -195,7 +196,7 @@ function formatUsage(programName, usage, options = {}) {
195
196
  }
196
197
  }
197
198
  let output = options.colors ? `\x1b[1m${programName}\x1b[0m` : programName;
198
- let lineWidth = programName.length;
199
+ let lineWidth = getDisplayWidth(programName);
199
200
  let first = true;
200
201
  for (const { text, width } of formatUsageTerms(usage, options)) {
201
202
  if (first) {
@@ -466,24 +467,24 @@ function* formatUsageTermInternal(term, options) {
466
467
  const optionsSeparator = options.optionsSeparator ?? "/";
467
468
  if (term.type === "argument") yield {
468
469
  text: options?.colors ? `\x1b[4m${term.metavar}\x1b[0m` : term.metavar,
469
- width: term.metavar.length
470
+ width: getDisplayWidth(term.metavar)
470
471
  };
471
472
  else if (term.type === "option") if (options?.onlyShortestOptions) {
472
- const shortestName = term.names.reduce((a, b) => a.length <= b.length ? a : b);
473
+ const shortestName = term.names.reduce((a, b) => getDisplayWidth(a) <= getDisplayWidth(b) ? a : b);
473
474
  yield {
474
475
  text: options?.colors ? `\x1b[3m${shortestName}\x1b[0m` : shortestName,
475
- width: shortestName.length
476
+ width: getDisplayWidth(shortestName)
476
477
  };
477
478
  } else {
478
479
  let i = 0;
479
480
  for (const optionName of term.names) {
480
481
  if (i > 0) yield {
481
482
  text: options?.colors ? `\x1b[2m${optionsSeparator}\x1b[0m` : optionsSeparator,
482
- width: optionsSeparator.length
483
+ width: getDisplayWidth(optionsSeparator)
483
484
  };
484
485
  yield {
485
486
  text: options?.colors ? `\x1b[3m${optionName}\x1b[0m` : optionName,
486
- width: optionName.length
487
+ width: getDisplayWidth(optionName)
487
488
  };
488
489
  i++;
489
490
  }
@@ -494,13 +495,13 @@ function* formatUsageTermInternal(term, options) {
494
495
  };
495
496
  yield {
496
497
  text: options?.colors ? `\x1b[4m\x1b[2m${term.metavar}\x1b[0m` : term.metavar,
497
- width: term.metavar.length
498
+ width: getDisplayWidth(term.metavar)
498
499
  };
499
500
  }
500
501
  }
501
502
  else if (term.type === "command") yield {
502
503
  text: options?.colors ? `\x1b[1m${term.name}\x1b[0m` : term.name,
503
- width: term.name.length
504
+ width: getDisplayWidth(term.name)
504
505
  };
505
506
  else if (term.type === "optional") {
506
507
  yield {
@@ -562,7 +563,7 @@ function* formatUsageTermInternal(term, options) {
562
563
  };
563
564
  } else if (term.type === "literal") yield {
564
565
  text: term.value,
565
- width: term.value.length
566
+ width: getDisplayWidth(term.value)
566
567
  };
567
568
  else if (term.type === "passthrough") {
568
569
  const text = "[...]";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/core",
3
- "version": "1.0.0-dev.1590+eb65aeba",
3
+ "version": "1.0.0-dev.1596+91310a38",
4
4
  "description": "Type-safe combinatorial command-line interface parser",
5
5
  "keywords": [
6
6
  "CLI",