@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/displaywidth.cjs +12 -2
- package/dist/displaywidth.d.cts +8 -1
- package/dist/displaywidth.d.ts +8 -1
- package/dist/displaywidth.js +11 -2
- package/dist/doc.cjs +63 -83
- package/dist/doc.js +63 -83
- package/dist/facade.cjs +12 -12
- package/dist/facade.js +12 -12
- package/dist/internal/terminal.cjs +5 -1
- package/dist/internal/terminal.d.cts +2 -1
- package/dist/internal/terminal.d.ts +2 -1
- package/dist/internal/terminal.js +2 -1
- package/dist/terminal-internal.cjs +14 -6
- package/dist/terminal-internal.d.cts +2 -0
- package/dist/terminal-internal.d.ts +2 -0
- package/dist/terminal-internal.js +14 -6
- package/dist/terminal.d.cts +2 -1
- package/dist/terminal.d.ts +2 -1
- package/dist/text-layout.cjs +87 -0
- package/dist/text-layout.d.cts +54 -0
- package/dist/text-layout.d.ts +54 -0
- package/dist/text-layout.js +84 -0
- package/dist/usage.cjs +39 -8
- package/dist/usage.js +39 -8
- package/package.json +2 -2
package/dist/displaywidth.cjs
CHANGED
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
|
|
2
2
|
//#region src/displaywidth.ts
|
|
3
3
|
const ansiRegex = /\x1B(?:\[[0-9;:]*[@-~]|\][^\x1B\x07]*(?:\x1B\\|\x07))/g;
|
|
4
|
+
/**
|
|
5
|
+
* Removes ANSI CSI and OSC sequences before measuring visible text.
|
|
6
|
+
* @param text Rendered terminal text.
|
|
7
|
+
* @returns Text without terminal escape sequences.
|
|
8
|
+
* @internal
|
|
9
|
+
*/
|
|
10
|
+
function stripAnsi(text) {
|
|
11
|
+
return text.replace(ansiRegex, "");
|
|
12
|
+
}
|
|
4
13
|
const segmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
5
14
|
/**
|
|
6
15
|
* Computes the terminal display width of a string, accounting for
|
|
@@ -11,7 +20,7 @@ const segmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
|
11
20
|
* @internal
|
|
12
21
|
*/
|
|
13
22
|
function getDisplayWidth(text) {
|
|
14
|
-
const stripped = text
|
|
23
|
+
const stripped = stripAnsi(text);
|
|
15
24
|
let width = 0;
|
|
16
25
|
for (const { segment } of segmenter.segment(stripped)) width += graphemeWidth(segment);
|
|
17
26
|
return width;
|
|
@@ -41,4 +50,5 @@ function isEastAsianWide(cp) {
|
|
|
41
50
|
}
|
|
42
51
|
|
|
43
52
|
//#endregion
|
|
44
|
-
exports.getDisplayWidth = getDisplayWidth;
|
|
53
|
+
exports.getDisplayWidth = getDisplayWidth;
|
|
54
|
+
exports.stripAnsi = stripAnsi;
|
package/dist/displaywidth.d.cts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
//#region src/displaywidth.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Removes ANSI CSI and OSC sequences before measuring visible text.
|
|
4
|
+
* @param text Rendered terminal text.
|
|
5
|
+
* @returns Text without terminal escape sequences.
|
|
6
|
+
* @internal
|
|
7
|
+
*/
|
|
8
|
+
declare function stripAnsi(text: string): string;
|
|
2
9
|
/**
|
|
3
10
|
* Computes the terminal display width of a string, accounting for
|
|
4
11
|
* East Asian wide characters, combining marks, emoji, and ANSI escapes.
|
|
@@ -9,4 +16,4 @@
|
|
|
9
16
|
*/
|
|
10
17
|
declare function getDisplayWidth(text: string): number;
|
|
11
18
|
//#endregion
|
|
12
|
-
export { getDisplayWidth };
|
|
19
|
+
export { getDisplayWidth, stripAnsi };
|
package/dist/displaywidth.d.ts
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
1
|
//#region src/displaywidth.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* Removes ANSI CSI and OSC sequences before measuring visible text.
|
|
4
|
+
* @param text Rendered terminal text.
|
|
5
|
+
* @returns Text without terminal escape sequences.
|
|
6
|
+
* @internal
|
|
7
|
+
*/
|
|
8
|
+
declare function stripAnsi(text: string): string;
|
|
2
9
|
/**
|
|
3
10
|
* Computes the terminal display width of a string, accounting for
|
|
4
11
|
* East Asian wide characters, combining marks, emoji, and ANSI escapes.
|
|
@@ -9,4 +16,4 @@
|
|
|
9
16
|
*/
|
|
10
17
|
declare function getDisplayWidth(text: string): number;
|
|
11
18
|
//#endregion
|
|
12
|
-
export { getDisplayWidth };
|
|
19
|
+
export { getDisplayWidth, stripAnsi };
|
package/dist/displaywidth.js
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
//#region src/displaywidth.ts
|
|
2
2
|
const ansiRegex = /\x1B(?:\[[0-9;:]*[@-~]|\][^\x1B\x07]*(?:\x1B\\|\x07))/g;
|
|
3
|
+
/**
|
|
4
|
+
* Removes ANSI CSI and OSC sequences before measuring visible text.
|
|
5
|
+
* @param text Rendered terminal text.
|
|
6
|
+
* @returns Text without terminal escape sequences.
|
|
7
|
+
* @internal
|
|
8
|
+
*/
|
|
9
|
+
function stripAnsi(text) {
|
|
10
|
+
return text.replace(ansiRegex, "");
|
|
11
|
+
}
|
|
3
12
|
const segmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
4
13
|
/**
|
|
5
14
|
* Computes the terminal display width of a string, accounting for
|
|
@@ -10,7 +19,7 @@ const segmenter = new Intl.Segmenter(void 0, { granularity: "grapheme" });
|
|
|
10
19
|
* @internal
|
|
11
20
|
*/
|
|
12
21
|
function getDisplayWidth(text) {
|
|
13
|
-
const stripped = text
|
|
22
|
+
const stripped = stripAnsi(text);
|
|
14
23
|
let width = 0;
|
|
15
24
|
for (const { segment } of segmenter.segment(stripped)) width += graphemeWidth(segment);
|
|
16
25
|
return width;
|
|
@@ -40,4 +49,4 @@ function isEastAsianWide(cp) {
|
|
|
40
49
|
}
|
|
41
50
|
|
|
42
51
|
//#endregion
|
|
43
|
-
export { getDisplayWidth };
|
|
52
|
+
export { getDisplayWidth, stripAnsi };
|
package/dist/doc.cjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
const require_message_registry = require('./message-registry.cjs');
|
|
2
|
-
const require_displaywidth = require('./displaywidth.cjs');
|
|
3
2
|
const require_terminal_internal = require('./terminal-internal.cjs');
|
|
4
3
|
const require_message = require('./message.cjs');
|
|
5
4
|
const require_message_renderer = require('./message-renderer.cjs');
|
|
5
|
+
const require_text_layout = require('./text-layout.cjs');
|
|
6
6
|
const require_validate = require('./validate.cjs');
|
|
7
7
|
const require_usage = require('./usage.cjs');
|
|
8
8
|
|
|
@@ -251,8 +251,8 @@ function formatDocPage(programName, page, options = {}) {
|
|
|
251
251
|
}
|
|
252
252
|
return rendered;
|
|
253
253
|
};
|
|
254
|
-
const usageLabel = page.usage != null && options.showUsage !== false ? label("Usage:", "usage")
|
|
255
|
-
const usageLabelWidth =
|
|
254
|
+
const usageLabel = page.usage != null && options.showUsage !== false ? require_text_layout.spaceAfterLabel(label("Usage:", "usage")) : "";
|
|
255
|
+
const usageLabelWidth = require_text_layout.measureText(usageLabel).lastLineWidth;
|
|
256
256
|
const defaultStyle = require_terminal_internal.styleCode(options.theme?.annotationStyles?.default ?? { dim: true });
|
|
257
257
|
const choicesStyle = require_terminal_internal.styleCode(options.theme?.annotationStyles?.choices ?? { dim: true });
|
|
258
258
|
require_validate.validateProgramName(programName);
|
|
@@ -276,6 +276,18 @@ function formatDocPage(programName, page, options = {}) {
|
|
|
276
276
|
}
|
|
277
277
|
const hasContent = (msg) => Array.isArray(msg) && msg.length > 0;
|
|
278
278
|
const needsDescriptionColumn = (entry) => hasContent(entry.description) || (options.showDefault === true || typeof options.showDefault === "object") && hasContent(entry.default) || (options.showChoices === true || typeof options.showChoices === "object") && hasContent(entry.choices);
|
|
279
|
+
const annotations = /* @__PURE__ */ new Map();
|
|
280
|
+
const annotation = (kind) => {
|
|
281
|
+
const cached = annotations.get(kind);
|
|
282
|
+
if (cached != null) return cached;
|
|
283
|
+
const config = kind === "default" ? options.showDefault : options.showChoices;
|
|
284
|
+
const prefix = punctuation(typeof config === "object" ? config.prefix ?? (kind === "default" ? " [" : " (") : kind === "default" ? " [" : " (", kind === "default" ? "defaultPrefix" : "choicesPrefix");
|
|
285
|
+
const suffix = punctuation(typeof config === "object" ? config.suffix ?? (kind === "default" ? "]" : ")") : kind === "default" ? "]" : ")", kind === "default" ? "defaultSuffix" : "choicesSuffix");
|
|
286
|
+
const choicesLabel = kind === "choices" ? label(typeof options.showChoices === "object" ? options.showChoices.label ?? "choices: " : "choices: ", "choices") : "";
|
|
287
|
+
const layout = require_text_layout.measureAnnotation(prefix + choicesLabel, suffix);
|
|
288
|
+
annotations.set(kind, layout);
|
|
289
|
+
return layout;
|
|
290
|
+
};
|
|
279
291
|
const automaticTermWidth = () => {
|
|
280
292
|
let widest;
|
|
281
293
|
for (const section of page.sections) for (const entry of section.entries) {
|
|
@@ -286,7 +298,7 @@ function formatDocPage(programName, page, options = {}) {
|
|
|
286
298
|
optionsSeparator: ", ",
|
|
287
299
|
context: "doc"
|
|
288
300
|
});
|
|
289
|
-
const width =
|
|
301
|
+
const width = require_text_layout.measureText(rendered).maxLineWidth;
|
|
290
302
|
widest = widest == null ? width : Math.max(widest, width);
|
|
291
303
|
}
|
|
292
304
|
return widest;
|
|
@@ -303,16 +315,9 @@ function formatDocPage(programName, page, options = {}) {
|
|
|
303
315
|
}
|
|
304
316
|
if (options.maxWidth != null) {
|
|
305
317
|
let minDescWidth = 1;
|
|
306
|
-
if (needsDescColumn) {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
minDescWidth = Math.max(minDescWidth, require_displaywidth.getDisplayWidth(prefix));
|
|
310
|
-
}
|
|
311
|
-
if (options.showChoices && page.sections.some((s) => s.entries.some((e) => hasContent(e.choices)))) {
|
|
312
|
-
const prefix = punctuation(typeof options.showChoices === "object" ? options.showChoices.prefix ?? " (" : " (", "choicesPrefix");
|
|
313
|
-
const choicesLabelText = label(typeof options.showChoices === "object" ? options.showChoices.label ?? "choices: " : "choices: ", "choices");
|
|
314
|
-
minDescWidth = Math.max(minDescWidth, require_displaywidth.getDisplayWidth(prefix) + require_displaywidth.getDisplayWidth(choicesLabelText));
|
|
315
|
-
}
|
|
318
|
+
if (needsDescColumn) for (const kind of ["default", "choices"]) {
|
|
319
|
+
const enabled = kind === "default" ? options.showDefault : options.showChoices;
|
|
320
|
+
if (enabled && page.sections.some((section) => section.entries.some((entry) => hasContent(entry[kind])))) minDescWidth = Math.max(minDescWidth, annotation(kind).minWidth);
|
|
316
321
|
}
|
|
317
322
|
const splitEntryMin = termIndent + 2 + Math.max(2, 2 * minDescWidth - 1);
|
|
318
323
|
const fixedEntryMin = termIndent + 2 + termWidth + minDescWidth;
|
|
@@ -321,11 +326,17 @@ function formatDocPage(programName, page, options = {}) {
|
|
|
321
326
|
type: "programName",
|
|
322
327
|
programName
|
|
323
328
|
}, options.theme))].map((token) => token.width)) : 0;
|
|
324
|
-
const usageMin = page.usage != null && showUsage ? usageLabelWidth + Math.max(programNameWidth, Math.min(maxVisibleAtomicWidth(page.usage, options.theme), programNameWidth + usageLabelWidth)) : 1;
|
|
329
|
+
const usageMin = page.usage != null && showUsage ? Math.max(require_text_layout.measureText(usageLabel).maxLineWidth, usageLabelWidth + Math.max(programNameWidth, Math.min(maxVisibleAtomicWidth(page.usage, options.theme), programNameWidth + usageLabelWidth))) : 1;
|
|
325
330
|
let sectionMin = 1;
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
331
|
+
for (const kind of [
|
|
332
|
+
"examples",
|
|
333
|
+
"author",
|
|
334
|
+
"bugs"
|
|
335
|
+
]) {
|
|
336
|
+
if (!hasContent(page[kind])) continue;
|
|
337
|
+
const title = kind === "examples" ? "Examples:" : kind === "author" ? "Author:" : "Bugs:";
|
|
338
|
+
sectionMin = Math.max(sectionMin, 3, require_text_layout.measureText(label(title, kind)).maxLineWidth);
|
|
339
|
+
}
|
|
329
340
|
const minWidth = Math.max(entryMin, usageMin, sectionMin);
|
|
330
341
|
if (options.maxWidth < minWidth) throw new RangeError(`maxWidth must be at least ${minWidth}, got ${options.maxWidth}.`);
|
|
331
342
|
if (needsDescColumn && minDescWidth > 1) {
|
|
@@ -383,17 +394,21 @@ function formatDocPage(programName, page, options = {}) {
|
|
|
383
394
|
output += sectionLabel;
|
|
384
395
|
}
|
|
385
396
|
for (const entry of section.entries) {
|
|
386
|
-
const
|
|
397
|
+
const termOptions = {
|
|
387
398
|
colors: options.colors,
|
|
388
399
|
theme: options.theme,
|
|
389
400
|
optionsSeparator: ", ",
|
|
390
|
-
context: "doc"
|
|
391
|
-
|
|
401
|
+
context: "doc"
|
|
402
|
+
};
|
|
403
|
+
const unwrappedTerm = require_usage.formatUsageTerm(entry.term, termOptions);
|
|
404
|
+
const multilineReplacement = require_text_layout.measureText(unwrappedTerm).lineCount > 1;
|
|
405
|
+
const term = options.maxWidth == null ? unwrappedTerm : require_usage.formatUsageTerm(entry.term, {
|
|
406
|
+
...termOptions,
|
|
407
|
+
maxWidth: options.maxWidth - termIndent
|
|
392
408
|
});
|
|
393
409
|
const descColumnWidth = options.maxWidth == null ? void 0 : options.maxWidth - termIndent - effectiveTermWidth - 2;
|
|
394
|
-
const termVisibleWidth =
|
|
410
|
+
const termVisibleWidth = require_text_layout.measureText(term).lastLineWidth;
|
|
395
411
|
const extraTermOffset = descColumnWidth != null ? Math.max(0, termVisibleWidth - effectiveTermWidth) : 0;
|
|
396
|
-
const currentExtraOffset = () => description.includes("\n") ? 0 : extraTermOffset;
|
|
397
412
|
const descFormatOptions = {
|
|
398
413
|
colors: options.colors,
|
|
399
414
|
quotes: !options.colors,
|
|
@@ -401,35 +416,26 @@ function formatDocPage(programName, page, options = {}) {
|
|
|
401
416
|
initialWidth: extraTermOffset > 0 ? extraTermOffset : void 0
|
|
402
417
|
};
|
|
403
418
|
let description = entry.description == null ? "" : formatMessage(entry.description, descFormatOptions);
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
if (
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
const
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
};
|
|
424
|
-
const defaultContent = formatMessage(entry.default, defaultFormatOptions, options.theme?.annotationStyles?.default ?? { dim: true });
|
|
425
|
-
const defaultText = `${prefix}${defaultContent}${opaqueFormatter && options.colors ? defaultStyle : ""}${suffix}`;
|
|
426
|
-
const formattedDefault = options.colors ? `${defaultStyle}${defaultText}${defaultStyle ? "\x1B[0m" : ""}` : defaultText;
|
|
427
|
-
description += formattedDefault;
|
|
428
|
-
}
|
|
419
|
+
let cursor = require_text_layout.placeText(description, {
|
|
420
|
+
line: "",
|
|
421
|
+
column: extraTermOffset
|
|
422
|
+
}).cursor;
|
|
423
|
+
const appendAnnotation = (content, layout, style, ambient, quotes) => {
|
|
424
|
+
const prefix = require_text_layout.placeText(layout.prefix, cursor, descColumnWidth, 1);
|
|
425
|
+
if (prefix.text !== layout.prefix) description += "\n";
|
|
426
|
+
const rendered = formatMessage(content, {
|
|
427
|
+
colors: options.colors ? { resetSuffix: style } : false,
|
|
428
|
+
quotes,
|
|
429
|
+
maxWidth: descColumnWidth == null ? void 0 : descColumnWidth - layout.suffixMetrics.firstLineWidth,
|
|
430
|
+
initialWidth: descColumnWidth != null || layout.prefixMetrics.lineCount > 1 ? prefix.cursor.column : void 0
|
|
431
|
+
}, ambient);
|
|
432
|
+
const suffix = require_text_layout.placeText(layout.suffix, require_text_layout.placeText(rendered, prefix.cursor).cursor, descColumnWidth);
|
|
433
|
+
const annotationText = `${layout.prefix}${rendered}${opaqueFormatter && options.colors ? style : ""}${suffix.text}`;
|
|
434
|
+
description += options.colors ? `${style}${annotationText}${style ? "\x1B[0m" : ""}` : annotationText;
|
|
435
|
+
cursor = suffix.cursor;
|
|
436
|
+
};
|
|
437
|
+
if (options.showDefault && hasContent(entry.default)) appendAnnotation(entry.default, annotation("default"), defaultStyle, defaultAmbient, !options.colors);
|
|
429
438
|
if (options.showChoices && hasContent(entry.choices)) {
|
|
430
|
-
const prefix = punctuation(typeof options.showChoices === "object" ? options.showChoices.prefix ?? " (" : " (", "choicesPrefix");
|
|
431
|
-
const suffix = punctuation(typeof options.showChoices === "object" ? options.showChoices.suffix ?? ")" : ")", "choicesSuffix");
|
|
432
|
-
const choicesLabelText = label(typeof options.showChoices === "object" ? options.showChoices.label ?? "choices: " : "choices: ", "choices");
|
|
433
439
|
const maxItems = typeof options.showChoices === "object" ? options.showChoices.maxItems ?? 8 : 8;
|
|
434
440
|
const terms = Array.isArray(entry.choices) ? entry.choices : [];
|
|
435
441
|
let truncatedTerms = terms;
|
|
@@ -447,31 +453,10 @@ function formatDocPage(programName, page, options = {}) {
|
|
|
447
453
|
}
|
|
448
454
|
if (truncated) truncatedTerms = [...terms.slice(0, cutIndex), require_message.text(", ...")];
|
|
449
455
|
}
|
|
450
|
-
|
|
451
|
-
const choicesSuffixWidth = require_displaywidth.getDisplayWidth(suffix);
|
|
452
|
-
const choicesLabelWidth = require_displaywidth.getDisplayWidth(choicesLabelText);
|
|
453
|
-
let choicesStartWidth;
|
|
454
|
-
if (descColumnWidth != null) {
|
|
455
|
-
const lastW = lastLineVisibleLength(description);
|
|
456
|
-
const effectiveLastW = lastW + currentExtraOffset();
|
|
457
|
-
const prefixLabelLen = choicesPrefixWidth + choicesLabelWidth;
|
|
458
|
-
if (effectiveLastW + prefixLabelLen >= descColumnWidth) {
|
|
459
|
-
description += "\n";
|
|
460
|
-
choicesStartWidth = prefixLabelLen;
|
|
461
|
-
} else choicesStartWidth = effectiveLastW + prefixLabelLen;
|
|
462
|
-
}
|
|
463
|
-
const choicesFormatOptions = {
|
|
464
|
-
colors: options.colors ? { resetSuffix: choicesStyle } : false,
|
|
465
|
-
quotes: false,
|
|
466
|
-
maxWidth: descColumnWidth == null ? void 0 : descColumnWidth - choicesSuffixWidth,
|
|
467
|
-
initialWidth: choicesStartWidth
|
|
468
|
-
};
|
|
469
|
-
const choicesDisplay = formatMessage(truncatedTerms, choicesFormatOptions, options.theme?.annotationStyles?.choices ?? { dim: true });
|
|
470
|
-
const choicesText = `${prefix}${choicesLabelText}${choicesDisplay}${opaqueFormatter && options.colors ? choicesStyle : ""}${suffix}`;
|
|
471
|
-
const formattedChoices = options.colors ? `${choicesStyle}${choicesText}${choicesStyle ? "\x1B[0m" : ""}` : choicesText;
|
|
472
|
-
description += formattedChoices;
|
|
456
|
+
appendAnnotation(truncatedTerms, annotation("choices"), choicesStyle, choicesAmbient, false);
|
|
473
457
|
}
|
|
474
|
-
|
|
458
|
+
const paddedTerm = ansiAwareRightPad(term, effectiveTermWidth);
|
|
459
|
+
output += `${" ".repeat(termIndent)}${multilineReplacement ? indentLines(paddedTerm, termIndent) : paddedTerm}${description === "" ? "" : ` ${indentLines(description, termIndent + effectiveTermWidth + 2)}`}\n`;
|
|
475
460
|
}
|
|
476
461
|
}
|
|
477
462
|
if (hasContent(page.examples)) {
|
|
@@ -530,21 +515,16 @@ function indentLines(text$1, indent) {
|
|
|
530
515
|
* contribute to the rendered width.
|
|
531
516
|
*/
|
|
532
517
|
function maxVisibleAtomicWidth(usage, theme) {
|
|
533
|
-
return
|
|
518
|
+
return usage.reduce((widest, term) => Math.max(widest, require_text_layout.measureText(require_usage.formatUsageTerm(term, {
|
|
534
519
|
theme,
|
|
535
520
|
maxWidth: 1
|
|
536
|
-
})
|
|
521
|
+
})).maxLineWidth), 0);
|
|
537
522
|
}
|
|
538
523
|
function ansiAwareRightPad(text$1, length, char = " ") {
|
|
539
|
-
const visibleWidth =
|
|
524
|
+
const visibleWidth = require_text_layout.measureText(text$1).lastLineWidth;
|
|
540
525
|
if (visibleWidth >= length) return text$1;
|
|
541
526
|
return text$1 + char.repeat(length - visibleWidth);
|
|
542
527
|
}
|
|
543
|
-
function lastLineVisibleLength(text$1) {
|
|
544
|
-
const lastNewline = text$1.lastIndexOf("\n");
|
|
545
|
-
const lastLine = lastNewline === -1 ? text$1 : text$1.slice(lastNewline + 1);
|
|
546
|
-
return require_displaywidth.getDisplayWidth(lastLine);
|
|
547
|
-
}
|
|
548
528
|
|
|
549
529
|
//#endregion
|
|
550
530
|
exports.cloneDocEntry = cloneDocEntry;
|
package/dist/doc.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import { messageRenderers } from "./message-registry.js";
|
|
2
|
-
import { getDisplayWidth } from "./displaywidth.js";
|
|
3
2
|
import { cacheTerminalTheme, formatTerminalTerm, fragmentTokens, renderTerminalTerm, styleCode } from "./terminal-internal.js";
|
|
4
3
|
import { cloneMessage, text } from "./message.js";
|
|
5
4
|
import { resolveMessageFormatter } from "./message-renderer.js";
|
|
5
|
+
import { measureAnnotation, measureText, placeText, spaceAfterLabel } from "./text-layout.js";
|
|
6
6
|
import { validateLabel, validateProgramName } from "./validate.js";
|
|
7
7
|
import { cloneUsageTerm, formatUsage, formatUsageTerm, isDocHidden } from "./usage.js";
|
|
8
8
|
|
|
@@ -251,8 +251,8 @@ function formatDocPage(programName, page, options = {}) {
|
|
|
251
251
|
}
|
|
252
252
|
return rendered;
|
|
253
253
|
};
|
|
254
|
-
const usageLabel = page.usage != null && options.showUsage !== false ? label("Usage:", "usage")
|
|
255
|
-
const usageLabelWidth =
|
|
254
|
+
const usageLabel = page.usage != null && options.showUsage !== false ? spaceAfterLabel(label("Usage:", "usage")) : "";
|
|
255
|
+
const usageLabelWidth = measureText(usageLabel).lastLineWidth;
|
|
256
256
|
const defaultStyle = styleCode(options.theme?.annotationStyles?.default ?? { dim: true });
|
|
257
257
|
const choicesStyle = styleCode(options.theme?.annotationStyles?.choices ?? { dim: true });
|
|
258
258
|
validateProgramName(programName);
|
|
@@ -276,6 +276,18 @@ function formatDocPage(programName, page, options = {}) {
|
|
|
276
276
|
}
|
|
277
277
|
const hasContent = (msg) => Array.isArray(msg) && msg.length > 0;
|
|
278
278
|
const needsDescriptionColumn = (entry) => hasContent(entry.description) || (options.showDefault === true || typeof options.showDefault === "object") && hasContent(entry.default) || (options.showChoices === true || typeof options.showChoices === "object") && hasContent(entry.choices);
|
|
279
|
+
const annotations = /* @__PURE__ */ new Map();
|
|
280
|
+
const annotation = (kind) => {
|
|
281
|
+
const cached = annotations.get(kind);
|
|
282
|
+
if (cached != null) return cached;
|
|
283
|
+
const config = kind === "default" ? options.showDefault : options.showChoices;
|
|
284
|
+
const prefix = punctuation(typeof config === "object" ? config.prefix ?? (kind === "default" ? " [" : " (") : kind === "default" ? " [" : " (", kind === "default" ? "defaultPrefix" : "choicesPrefix");
|
|
285
|
+
const suffix = punctuation(typeof config === "object" ? config.suffix ?? (kind === "default" ? "]" : ")") : kind === "default" ? "]" : ")", kind === "default" ? "defaultSuffix" : "choicesSuffix");
|
|
286
|
+
const choicesLabel = kind === "choices" ? label(typeof options.showChoices === "object" ? options.showChoices.label ?? "choices: " : "choices: ", "choices") : "";
|
|
287
|
+
const layout = measureAnnotation(prefix + choicesLabel, suffix);
|
|
288
|
+
annotations.set(kind, layout);
|
|
289
|
+
return layout;
|
|
290
|
+
};
|
|
279
291
|
const automaticTermWidth = () => {
|
|
280
292
|
let widest;
|
|
281
293
|
for (const section of page.sections) for (const entry of section.entries) {
|
|
@@ -286,7 +298,7 @@ function formatDocPage(programName, page, options = {}) {
|
|
|
286
298
|
optionsSeparator: ", ",
|
|
287
299
|
context: "doc"
|
|
288
300
|
});
|
|
289
|
-
const width =
|
|
301
|
+
const width = measureText(rendered).maxLineWidth;
|
|
290
302
|
widest = widest == null ? width : Math.max(widest, width);
|
|
291
303
|
}
|
|
292
304
|
return widest;
|
|
@@ -303,16 +315,9 @@ function formatDocPage(programName, page, options = {}) {
|
|
|
303
315
|
}
|
|
304
316
|
if (options.maxWidth != null) {
|
|
305
317
|
let minDescWidth = 1;
|
|
306
|
-
if (needsDescColumn) {
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
minDescWidth = Math.max(minDescWidth, getDisplayWidth(prefix));
|
|
310
|
-
}
|
|
311
|
-
if (options.showChoices && page.sections.some((s) => s.entries.some((e) => hasContent(e.choices)))) {
|
|
312
|
-
const prefix = punctuation(typeof options.showChoices === "object" ? options.showChoices.prefix ?? " (" : " (", "choicesPrefix");
|
|
313
|
-
const choicesLabelText = label(typeof options.showChoices === "object" ? options.showChoices.label ?? "choices: " : "choices: ", "choices");
|
|
314
|
-
minDescWidth = Math.max(minDescWidth, getDisplayWidth(prefix) + getDisplayWidth(choicesLabelText));
|
|
315
|
-
}
|
|
318
|
+
if (needsDescColumn) for (const kind of ["default", "choices"]) {
|
|
319
|
+
const enabled = kind === "default" ? options.showDefault : options.showChoices;
|
|
320
|
+
if (enabled && page.sections.some((section) => section.entries.some((entry) => hasContent(entry[kind])))) minDescWidth = Math.max(minDescWidth, annotation(kind).minWidth);
|
|
316
321
|
}
|
|
317
322
|
const splitEntryMin = termIndent + 2 + Math.max(2, 2 * minDescWidth - 1);
|
|
318
323
|
const fixedEntryMin = termIndent + 2 + termWidth + minDescWidth;
|
|
@@ -321,11 +326,17 @@ function formatDocPage(programName, page, options = {}) {
|
|
|
321
326
|
type: "programName",
|
|
322
327
|
programName
|
|
323
328
|
}, options.theme))].map((token) => token.width)) : 0;
|
|
324
|
-
const usageMin = page.usage != null && showUsage ? usageLabelWidth + Math.max(programNameWidth, Math.min(maxVisibleAtomicWidth(page.usage, options.theme), programNameWidth + usageLabelWidth)) : 1;
|
|
329
|
+
const usageMin = page.usage != null && showUsage ? Math.max(measureText(usageLabel).maxLineWidth, usageLabelWidth + Math.max(programNameWidth, Math.min(maxVisibleAtomicWidth(page.usage, options.theme), programNameWidth + usageLabelWidth))) : 1;
|
|
325
330
|
let sectionMin = 1;
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
331
|
+
for (const kind of [
|
|
332
|
+
"examples",
|
|
333
|
+
"author",
|
|
334
|
+
"bugs"
|
|
335
|
+
]) {
|
|
336
|
+
if (!hasContent(page[kind])) continue;
|
|
337
|
+
const title = kind === "examples" ? "Examples:" : kind === "author" ? "Author:" : "Bugs:";
|
|
338
|
+
sectionMin = Math.max(sectionMin, 3, measureText(label(title, kind)).maxLineWidth);
|
|
339
|
+
}
|
|
329
340
|
const minWidth = Math.max(entryMin, usageMin, sectionMin);
|
|
330
341
|
if (options.maxWidth < minWidth) throw new RangeError(`maxWidth must be at least ${minWidth}, got ${options.maxWidth}.`);
|
|
331
342
|
if (needsDescColumn && minDescWidth > 1) {
|
|
@@ -383,17 +394,21 @@ function formatDocPage(programName, page, options = {}) {
|
|
|
383
394
|
output += sectionLabel;
|
|
384
395
|
}
|
|
385
396
|
for (const entry of section.entries) {
|
|
386
|
-
const
|
|
397
|
+
const termOptions = {
|
|
387
398
|
colors: options.colors,
|
|
388
399
|
theme: options.theme,
|
|
389
400
|
optionsSeparator: ", ",
|
|
390
|
-
context: "doc"
|
|
391
|
-
|
|
401
|
+
context: "doc"
|
|
402
|
+
};
|
|
403
|
+
const unwrappedTerm = formatUsageTerm(entry.term, termOptions);
|
|
404
|
+
const multilineReplacement = measureText(unwrappedTerm).lineCount > 1;
|
|
405
|
+
const term = options.maxWidth == null ? unwrappedTerm : formatUsageTerm(entry.term, {
|
|
406
|
+
...termOptions,
|
|
407
|
+
maxWidth: options.maxWidth - termIndent
|
|
392
408
|
});
|
|
393
409
|
const descColumnWidth = options.maxWidth == null ? void 0 : options.maxWidth - termIndent - effectiveTermWidth - 2;
|
|
394
|
-
const termVisibleWidth =
|
|
410
|
+
const termVisibleWidth = measureText(term).lastLineWidth;
|
|
395
411
|
const extraTermOffset = descColumnWidth != null ? Math.max(0, termVisibleWidth - effectiveTermWidth) : 0;
|
|
396
|
-
const currentExtraOffset = () => description.includes("\n") ? 0 : extraTermOffset;
|
|
397
412
|
const descFormatOptions = {
|
|
398
413
|
colors: options.colors,
|
|
399
414
|
quotes: !options.colors,
|
|
@@ -401,35 +416,26 @@ function formatDocPage(programName, page, options = {}) {
|
|
|
401
416
|
initialWidth: extraTermOffset > 0 ? extraTermOffset : void 0
|
|
402
417
|
};
|
|
403
418
|
let description = entry.description == null ? "" : formatMessage(entry.description, descFormatOptions);
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
if (
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
const
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
};
|
|
424
|
-
const defaultContent = formatMessage(entry.default, defaultFormatOptions, options.theme?.annotationStyles?.default ?? { dim: true });
|
|
425
|
-
const defaultText = `${prefix}${defaultContent}${opaqueFormatter && options.colors ? defaultStyle : ""}${suffix}`;
|
|
426
|
-
const formattedDefault = options.colors ? `${defaultStyle}${defaultText}${defaultStyle ? "\x1B[0m" : ""}` : defaultText;
|
|
427
|
-
description += formattedDefault;
|
|
428
|
-
}
|
|
419
|
+
let cursor = placeText(description, {
|
|
420
|
+
line: "",
|
|
421
|
+
column: extraTermOffset
|
|
422
|
+
}).cursor;
|
|
423
|
+
const appendAnnotation = (content, layout, style, ambient, quotes) => {
|
|
424
|
+
const prefix = placeText(layout.prefix, cursor, descColumnWidth, 1);
|
|
425
|
+
if (prefix.text !== layout.prefix) description += "\n";
|
|
426
|
+
const rendered = formatMessage(content, {
|
|
427
|
+
colors: options.colors ? { resetSuffix: style } : false,
|
|
428
|
+
quotes,
|
|
429
|
+
maxWidth: descColumnWidth == null ? void 0 : descColumnWidth - layout.suffixMetrics.firstLineWidth,
|
|
430
|
+
initialWidth: descColumnWidth != null || layout.prefixMetrics.lineCount > 1 ? prefix.cursor.column : void 0
|
|
431
|
+
}, ambient);
|
|
432
|
+
const suffix = placeText(layout.suffix, placeText(rendered, prefix.cursor).cursor, descColumnWidth);
|
|
433
|
+
const annotationText = `${layout.prefix}${rendered}${opaqueFormatter && options.colors ? style : ""}${suffix.text}`;
|
|
434
|
+
description += options.colors ? `${style}${annotationText}${style ? "\x1B[0m" : ""}` : annotationText;
|
|
435
|
+
cursor = suffix.cursor;
|
|
436
|
+
};
|
|
437
|
+
if (options.showDefault && hasContent(entry.default)) appendAnnotation(entry.default, annotation("default"), defaultStyle, defaultAmbient, !options.colors);
|
|
429
438
|
if (options.showChoices && hasContent(entry.choices)) {
|
|
430
|
-
const prefix = punctuation(typeof options.showChoices === "object" ? options.showChoices.prefix ?? " (" : " (", "choicesPrefix");
|
|
431
|
-
const suffix = punctuation(typeof options.showChoices === "object" ? options.showChoices.suffix ?? ")" : ")", "choicesSuffix");
|
|
432
|
-
const choicesLabelText = label(typeof options.showChoices === "object" ? options.showChoices.label ?? "choices: " : "choices: ", "choices");
|
|
433
439
|
const maxItems = typeof options.showChoices === "object" ? options.showChoices.maxItems ?? 8 : 8;
|
|
434
440
|
const terms = Array.isArray(entry.choices) ? entry.choices : [];
|
|
435
441
|
let truncatedTerms = terms;
|
|
@@ -447,31 +453,10 @@ function formatDocPage(programName, page, options = {}) {
|
|
|
447
453
|
}
|
|
448
454
|
if (truncated) truncatedTerms = [...terms.slice(0, cutIndex), text(", ...")];
|
|
449
455
|
}
|
|
450
|
-
|
|
451
|
-
const choicesSuffixWidth = getDisplayWidth(suffix);
|
|
452
|
-
const choicesLabelWidth = getDisplayWidth(choicesLabelText);
|
|
453
|
-
let choicesStartWidth;
|
|
454
|
-
if (descColumnWidth != null) {
|
|
455
|
-
const lastW = lastLineVisibleLength(description);
|
|
456
|
-
const effectiveLastW = lastW + currentExtraOffset();
|
|
457
|
-
const prefixLabelLen = choicesPrefixWidth + choicesLabelWidth;
|
|
458
|
-
if (effectiveLastW + prefixLabelLen >= descColumnWidth) {
|
|
459
|
-
description += "\n";
|
|
460
|
-
choicesStartWidth = prefixLabelLen;
|
|
461
|
-
} else choicesStartWidth = effectiveLastW + prefixLabelLen;
|
|
462
|
-
}
|
|
463
|
-
const choicesFormatOptions = {
|
|
464
|
-
colors: options.colors ? { resetSuffix: choicesStyle } : false,
|
|
465
|
-
quotes: false,
|
|
466
|
-
maxWidth: descColumnWidth == null ? void 0 : descColumnWidth - choicesSuffixWidth,
|
|
467
|
-
initialWidth: choicesStartWidth
|
|
468
|
-
};
|
|
469
|
-
const choicesDisplay = formatMessage(truncatedTerms, choicesFormatOptions, options.theme?.annotationStyles?.choices ?? { dim: true });
|
|
470
|
-
const choicesText = `${prefix}${choicesLabelText}${choicesDisplay}${opaqueFormatter && options.colors ? choicesStyle : ""}${suffix}`;
|
|
471
|
-
const formattedChoices = options.colors ? `${choicesStyle}${choicesText}${choicesStyle ? "\x1B[0m" : ""}` : choicesText;
|
|
472
|
-
description += formattedChoices;
|
|
456
|
+
appendAnnotation(truncatedTerms, annotation("choices"), choicesStyle, choicesAmbient, false);
|
|
473
457
|
}
|
|
474
|
-
|
|
458
|
+
const paddedTerm = ansiAwareRightPad(term, effectiveTermWidth);
|
|
459
|
+
output += `${" ".repeat(termIndent)}${multilineReplacement ? indentLines(paddedTerm, termIndent) : paddedTerm}${description === "" ? "" : ` ${indentLines(description, termIndent + effectiveTermWidth + 2)}`}\n`;
|
|
475
460
|
}
|
|
476
461
|
}
|
|
477
462
|
if (hasContent(page.examples)) {
|
|
@@ -530,21 +515,16 @@ function indentLines(text$1, indent) {
|
|
|
530
515
|
* contribute to the rendered width.
|
|
531
516
|
*/
|
|
532
517
|
function maxVisibleAtomicWidth(usage, theme) {
|
|
533
|
-
return
|
|
518
|
+
return usage.reduce((widest, term) => Math.max(widest, measureText(formatUsageTerm(term, {
|
|
534
519
|
theme,
|
|
535
520
|
maxWidth: 1
|
|
536
|
-
})
|
|
521
|
+
})).maxLineWidth), 0);
|
|
537
522
|
}
|
|
538
523
|
function ansiAwareRightPad(text$1, length, char = " ") {
|
|
539
|
-
const visibleWidth =
|
|
524
|
+
const visibleWidth = measureText(text$1).lastLineWidth;
|
|
540
525
|
if (visibleWidth >= length) return text$1;
|
|
541
526
|
return text$1 + char.repeat(length - visibleWidth);
|
|
542
527
|
}
|
|
543
|
-
function lastLineVisibleLength(text$1) {
|
|
544
|
-
const lastNewline = text$1.lastIndexOf("\n");
|
|
545
|
-
const lastLine = lastNewline === -1 ? text$1 : text$1.slice(lastNewline + 1);
|
|
546
|
-
return getDisplayWidth(lastLine);
|
|
547
|
-
}
|
|
548
528
|
|
|
549
529
|
//#endregion
|
|
550
530
|
export { cloneDocEntry, deduplicateDocEntries, deduplicateDocFragments, formatDocPage, isDocEntryHidden };
|