@optique/core 1.0.11-dev.2656 → 1.0.12-dev.2687

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.
@@ -16,6 +16,17 @@ function getDisplayWidth(text) {
16
16
  for (const { segment } of segmenter.segment(stripped)) width += graphemeWidth(segment);
17
17
  return width;
18
18
  }
19
+ /**
20
+ * Removes ANSI escape sequences (CSI and OSC) from a string, leaving only
21
+ * the text a terminal would print.
22
+ *
23
+ * @param text The string to strip.
24
+ * @returns The string without ANSI escape sequences.
25
+ * @internal
26
+ */
27
+ function stripAnsi(text) {
28
+ return text.replace(ansiRegex, "");
29
+ }
19
30
  const zeroWidthRegex = /^[\p{Cf}\p{Mn}\p{Me}]+$/u;
20
31
  const emojiPresentationRegex = /\p{Emoji_Presentation}/u;
21
32
  const emojiWithVS16Regex = /\p{Emoji}\uFE0F/u;
@@ -41,4 +52,5 @@ function isEastAsianWide(cp) {
41
52
  }
42
53
 
43
54
  //#endregion
44
- exports.getDisplayWidth = getDisplayWidth;
55
+ exports.getDisplayWidth = getDisplayWidth;
56
+ exports.stripAnsi = stripAnsi;
@@ -15,6 +15,17 @@ function getDisplayWidth(text) {
15
15
  for (const { segment } of segmenter.segment(stripped)) width += graphemeWidth(segment);
16
16
  return width;
17
17
  }
18
+ /**
19
+ * Removes ANSI escape sequences (CSI and OSC) from a string, leaving only
20
+ * the text a terminal would print.
21
+ *
22
+ * @param text The string to strip.
23
+ * @returns The string without ANSI escape sequences.
24
+ * @internal
25
+ */
26
+ function stripAnsi(text) {
27
+ return text.replace(ansiRegex, "");
28
+ }
18
29
  const zeroWidthRegex = /^[\p{Cf}\p{Mn}\p{Me}]+$/u;
19
30
  const emojiPresentationRegex = /\p{Emoji_Presentation}/u;
20
31
  const emojiWithVS16Regex = /\p{Emoji}\uFE0F/u;
@@ -40,4 +51,4 @@ function isEastAsianWide(cp) {
40
51
  }
41
52
 
42
53
  //#endregion
43
- export { getDisplayWidth };
54
+ export { getDisplayWidth, stripAnsi };
package/dist/doc.cjs CHANGED
@@ -341,13 +341,15 @@ function formatDocPage(programName, page, options = {}) {
341
341
  const suffix = typeof options.showDefault === "object" ? options.showDefault.suffix ?? "]" : "]";
342
342
  const prefixWidth = require_displaywidth.getDisplayWidth(prefix);
343
343
  const suffixWidth = require_displaywidth.getDisplayWidth(suffix);
344
+ let defaultPrefix = prefix;
344
345
  let defaultStartWidth;
345
346
  if (descColumnWidth != null) {
346
347
  const lastW = lastLineVisibleLength(description);
347
348
  const effectiveLastW = lastW + currentExtraOffset();
348
349
  if (effectiveLastW + prefixWidth >= descColumnWidth) {
349
350
  description += "\n";
350
- defaultStartWidth = prefixWidth;
351
+ defaultPrefix = prefix.trimStart();
352
+ defaultStartWidth = require_displaywidth.getDisplayWidth(defaultPrefix);
351
353
  } else defaultStartWidth = effectiveLastW + prefixWidth;
352
354
  }
353
355
  const defaultFormatOptions = {
@@ -357,7 +359,7 @@ function formatDocPage(programName, page, options = {}) {
357
359
  startWidth: defaultStartWidth
358
360
  };
359
361
  const defaultContent = require_message.formatMessage(entry.default, defaultFormatOptions);
360
- const defaultText = `${prefix}${defaultContent}${suffix}`;
362
+ const defaultText = `${defaultPrefix}${defaultContent}${suffix}`;
361
363
  const formattedDefault = options.colors ? `\x1b[2m${defaultText}\x1b[0m` : defaultText;
362
364
  description += formattedDefault;
363
365
  }
@@ -385,6 +387,7 @@ function formatDocPage(programName, page, options = {}) {
385
387
  const choicesPrefixWidth = require_displaywidth.getDisplayWidth(prefix);
386
388
  const choicesSuffixWidth = require_displaywidth.getDisplayWidth(suffix);
387
389
  const choicesLabelWidth = require_displaywidth.getDisplayWidth(label);
390
+ let choicesHeading = `${prefix}${label}`;
388
391
  let choicesStartWidth;
389
392
  if (descColumnWidth != null) {
390
393
  const lastW = lastLineVisibleLength(description);
@@ -392,7 +395,8 @@ function formatDocPage(programName, page, options = {}) {
392
395
  const prefixLabelLen = choicesPrefixWidth + choicesLabelWidth;
393
396
  if (effectiveLastW + prefixLabelLen >= descColumnWidth) {
394
397
  description += "\n";
395
- choicesStartWidth = prefixLabelLen;
398
+ choicesHeading = choicesHeading.trimStart();
399
+ choicesStartWidth = require_displaywidth.getDisplayWidth(choicesHeading);
396
400
  } else choicesStartWidth = effectiveLastW + prefixLabelLen;
397
401
  }
398
402
  const choicesFormatOptions = {
@@ -402,7 +406,7 @@ function formatDocPage(programName, page, options = {}) {
402
406
  startWidth: choicesStartWidth
403
407
  };
404
408
  const choicesDisplay = require_message.formatMessage(truncatedTerms, choicesFormatOptions);
405
- const choicesText = `${prefix}${label}${choicesDisplay}${suffix}`;
409
+ const choicesText = `${choicesHeading}${choicesDisplay}${suffix}`;
406
410
  const formattedChoices = options.colors ? `\x1b[2m${choicesText}\x1b[0m` : choicesText;
407
411
  description += formattedChoices;
408
412
  }
package/dist/doc.js CHANGED
@@ -341,13 +341,15 @@ function formatDocPage(programName, page, options = {}) {
341
341
  const suffix = typeof options.showDefault === "object" ? options.showDefault.suffix ?? "]" : "]";
342
342
  const prefixWidth = getDisplayWidth(prefix);
343
343
  const suffixWidth = getDisplayWidth(suffix);
344
+ let defaultPrefix = prefix;
344
345
  let defaultStartWidth;
345
346
  if (descColumnWidth != null) {
346
347
  const lastW = lastLineVisibleLength(description);
347
348
  const effectiveLastW = lastW + currentExtraOffset();
348
349
  if (effectiveLastW + prefixWidth >= descColumnWidth) {
349
350
  description += "\n";
350
- defaultStartWidth = prefixWidth;
351
+ defaultPrefix = prefix.trimStart();
352
+ defaultStartWidth = getDisplayWidth(defaultPrefix);
351
353
  } else defaultStartWidth = effectiveLastW + prefixWidth;
352
354
  }
353
355
  const defaultFormatOptions = {
@@ -357,7 +359,7 @@ function formatDocPage(programName, page, options = {}) {
357
359
  startWidth: defaultStartWidth
358
360
  };
359
361
  const defaultContent = formatMessage(entry.default, defaultFormatOptions);
360
- const defaultText = `${prefix}${defaultContent}${suffix}`;
362
+ const defaultText = `${defaultPrefix}${defaultContent}${suffix}`;
361
363
  const formattedDefault = options.colors ? `\x1b[2m${defaultText}\x1b[0m` : defaultText;
362
364
  description += formattedDefault;
363
365
  }
@@ -385,6 +387,7 @@ function formatDocPage(programName, page, options = {}) {
385
387
  const choicesPrefixWidth = getDisplayWidth(prefix);
386
388
  const choicesSuffixWidth = getDisplayWidth(suffix);
387
389
  const choicesLabelWidth = getDisplayWidth(label);
390
+ let choicesHeading = `${prefix}${label}`;
388
391
  let choicesStartWidth;
389
392
  if (descColumnWidth != null) {
390
393
  const lastW = lastLineVisibleLength(description);
@@ -392,7 +395,8 @@ function formatDocPage(programName, page, options = {}) {
392
395
  const prefixLabelLen = choicesPrefixWidth + choicesLabelWidth;
393
396
  if (effectiveLastW + prefixLabelLen >= descColumnWidth) {
394
397
  description += "\n";
395
- choicesStartWidth = prefixLabelLen;
398
+ choicesHeading = choicesHeading.trimStart();
399
+ choicesStartWidth = getDisplayWidth(choicesHeading);
396
400
  } else choicesStartWidth = effectiveLastW + prefixLabelLen;
397
401
  }
398
402
  const choicesFormatOptions = {
@@ -402,7 +406,7 @@ function formatDocPage(programName, page, options = {}) {
402
406
  startWidth: choicesStartWidth
403
407
  };
404
408
  const choicesDisplay = formatMessage(truncatedTerms, choicesFormatOptions);
405
- const choicesText = `${prefix}${label}${choicesDisplay}${suffix}`;
409
+ const choicesText = `${choicesHeading}${choicesDisplay}${suffix}`;
406
410
  const formattedChoices = options.colors ? `\x1b[2m${choicesText}\x1b[0m` : choicesText;
407
411
  description += formattedChoices;
408
412
  }
package/dist/message.cjs CHANGED
@@ -324,7 +324,8 @@ function formatMessage(msg, options = {}) {
324
324
  if (match == null) break;
325
325
  yield {
326
326
  text: match[0],
327
- width: require_displaywidth.getDisplayWidth(match[0])
327
+ width: require_displaywidth.getDisplayWidth(match[0]),
328
+ trimOnWrap: true
328
329
  };
329
330
  }
330
331
  }
@@ -332,7 +333,8 @@ function formatMessage(msg, options = {}) {
332
333
  const normalizedText = rawText.replace(/\n/g, " ");
333
334
  if (normalizedText.trim() === "" && normalizedText.length > 0) yield {
334
335
  text: " ",
335
- width: 1
336
+ width: 1,
337
+ trimOnWrap: true
336
338
  };
337
339
  else {
338
340
  wordPattern.lastIndex = 0;
@@ -341,7 +343,8 @@ function formatMessage(msg, options = {}) {
341
343
  if (match == null) break;
342
344
  yield {
343
345
  text: match[0],
344
- width: require_displaywidth.getDisplayWidth(match[0])
346
+ width: require_displaywidth.getDisplayWidth(match[0]),
347
+ trimOnWrap: true
345
348
  };
346
349
  }
347
350
  }
@@ -381,7 +384,8 @@ function formatMessage(msg, options = {}) {
381
384
  } else if (term.type === "values") for (let i = 0; i < term.values.length; i++) {
382
385
  if (i > 0) yield {
383
386
  text: " ",
384
- width: 1
387
+ width: 1,
388
+ trimOnWrap: true
385
389
  };
386
390
  const value$1 = useQuotes ? JSON.stringify(term.values[i]) : term.values[i];
387
391
  yield {
@@ -425,18 +429,40 @@ function formatMessage(msg, options = {}) {
425
429
  }
426
430
  let output = "";
427
431
  let totalWidth = options.startWidth ?? 0;
428
- for (const { text: text$1, width } of stream()) {
432
+ let pendingWrap = false;
433
+ let atWrappedLineStart = false;
434
+ for (const token of stream()) {
435
+ let { text: text$1, width } = token;
429
436
  if (width === -1) {
430
437
  output += text$1;
431
438
  totalWidth = 0;
439
+ pendingWrap = false;
440
+ atWrappedLineStart = false;
441
+ continue;
442
+ }
443
+ const wraps = pendingWrap || options.maxWidth != null && totalWidth > 0 && totalWidth + width > options.maxWidth;
444
+ if ((wraps || atWrappedLineStart) && token.trimOnWrap) {
445
+ const trimmed = text$1.trimStart();
446
+ width -= require_displaywidth.getDisplayWidth(text$1.slice(0, text$1.length - trimmed.length));
447
+ text$1 = trimmed;
448
+ if (text$1 === "") {
449
+ if (wraps) pendingWrap = true;
450
+ continue;
451
+ }
452
+ }
453
+ if (pendingWrap && require_displaywidth.stripAnsi(text$1) === "") {
454
+ output += text$1;
432
455
  continue;
433
456
  }
434
- if (options.maxWidth != null && totalWidth > 0 && totalWidth + width > options.maxWidth) {
457
+ if (wraps) {
435
458
  output += "\n";
436
459
  totalWidth = 0;
460
+ pendingWrap = false;
461
+ atWrappedLineStart = true;
437
462
  }
438
463
  output += text$1;
439
464
  totalWidth += width;
465
+ if (width > 0) atWrappedLineStart = false;
440
466
  }
441
467
  return output;
442
468
  }
package/dist/message.js CHANGED
@@ -1,4 +1,4 @@
1
- import { getDisplayWidth } from "./displaywidth.js";
1
+ import { getDisplayWidth, stripAnsi } from "./displaywidth.js";
2
2
 
3
3
  //#region src/message.ts
4
4
  /**
@@ -324,7 +324,8 @@ function formatMessage(msg, options = {}) {
324
324
  if (match == null) break;
325
325
  yield {
326
326
  text: match[0],
327
- width: getDisplayWidth(match[0])
327
+ width: getDisplayWidth(match[0]),
328
+ trimOnWrap: true
328
329
  };
329
330
  }
330
331
  }
@@ -332,7 +333,8 @@ function formatMessage(msg, options = {}) {
332
333
  const normalizedText = rawText.replace(/\n/g, " ");
333
334
  if (normalizedText.trim() === "" && normalizedText.length > 0) yield {
334
335
  text: " ",
335
- width: 1
336
+ width: 1,
337
+ trimOnWrap: true
336
338
  };
337
339
  else {
338
340
  wordPattern.lastIndex = 0;
@@ -341,7 +343,8 @@ function formatMessage(msg, options = {}) {
341
343
  if (match == null) break;
342
344
  yield {
343
345
  text: match[0],
344
- width: getDisplayWidth(match[0])
346
+ width: getDisplayWidth(match[0]),
347
+ trimOnWrap: true
345
348
  };
346
349
  }
347
350
  }
@@ -381,7 +384,8 @@ function formatMessage(msg, options = {}) {
381
384
  } else if (term.type === "values") for (let i = 0; i < term.values.length; i++) {
382
385
  if (i > 0) yield {
383
386
  text: " ",
384
- width: 1
387
+ width: 1,
388
+ trimOnWrap: true
385
389
  };
386
390
  const value$1 = useQuotes ? JSON.stringify(term.values[i]) : term.values[i];
387
391
  yield {
@@ -425,18 +429,40 @@ function formatMessage(msg, options = {}) {
425
429
  }
426
430
  let output = "";
427
431
  let totalWidth = options.startWidth ?? 0;
428
- for (const { text: text$1, width } of stream()) {
432
+ let pendingWrap = false;
433
+ let atWrappedLineStart = false;
434
+ for (const token of stream()) {
435
+ let { text: text$1, width } = token;
429
436
  if (width === -1) {
430
437
  output += text$1;
431
438
  totalWidth = 0;
439
+ pendingWrap = false;
440
+ atWrappedLineStart = false;
441
+ continue;
442
+ }
443
+ const wraps = pendingWrap || options.maxWidth != null && totalWidth > 0 && totalWidth + width > options.maxWidth;
444
+ if ((wraps || atWrappedLineStart) && token.trimOnWrap) {
445
+ const trimmed = text$1.trimStart();
446
+ width -= getDisplayWidth(text$1.slice(0, text$1.length - trimmed.length));
447
+ text$1 = trimmed;
448
+ if (text$1 === "") {
449
+ if (wraps) pendingWrap = true;
450
+ continue;
451
+ }
452
+ }
453
+ if (pendingWrap && stripAnsi(text$1) === "") {
454
+ output += text$1;
432
455
  continue;
433
456
  }
434
- if (options.maxWidth != null && totalWidth > 0 && totalWidth + width > options.maxWidth) {
457
+ if (wraps) {
435
458
  output += "\n";
436
459
  totalWidth = 0;
460
+ pendingWrap = false;
461
+ atWrappedLineStart = true;
437
462
  }
438
463
  output += text$1;
439
464
  totalWidth += width;
465
+ if (width > 0) atWrappedLineStart = false;
440
466
  }
441
467
  return output;
442
468
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/core",
3
- "version": "1.0.11-dev.2656",
3
+ "version": "1.0.12-dev.2687",
4
4
  "description": "Type-safe combinatorial command-line interface parser",
5
5
  "keywords": [
6
6
  "CLI",