@optique/man 1.4.0-dev.2979 → 1.4.0-dev.2992

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/cli.cjs CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
2
  const require_chunk = require('./chunk-CUT6urMc.cjs');
3
3
  require('./roff-C8MvQ2tL.cjs');
4
- require('./man-ChbwsIhG.cjs');
5
- const require_generator = require('./generator-DYAqAqpo.cjs');
4
+ require('./man-CHy1T6Mq.cjs');
5
+ const require_generator = require('./generator-DnoyvwHV.cjs');
6
6
  const __optique_core_constructs = require_chunk.__toESM(require("@optique/core/constructs"));
7
7
  const __optique_core_primitives = require_chunk.__toESM(require("@optique/core/primitives"));
8
8
  const __optique_core_valueparser = require_chunk.__toESM(require("@optique/core/valueparser"));
@@ -18,7 +18,7 @@ const node_url = require_chunk.__toESM(require("node:url"));
18
18
 
19
19
  //#region deno.json
20
20
  var name = "@optique/man";
21
- var version = "1.4.0-dev.2979+a175cdb7";
21
+ var version = "1.4.0-dev.2992+c4f1ddf9";
22
22
  var license = "MIT";
23
23
  var exports$1 = {
24
24
  ".": "./src/index.ts",
package/dist/cli.js CHANGED
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import "./roff-Bh9bsH6u.js";
3
- import "./man-do5Ey935.js";
4
- import { generateManPageAsync } from "./generator-ChMzn8U9.js";
3
+ import "./man-B8NXqGxL.js";
4
+ import { generateManPageAsync } from "./generator-BP_7uXi7.js";
5
5
  import { object } from "@optique/core/constructs";
6
6
  import { argument, option } from "@optique/core/primitives";
7
7
  import { choice, string } from "@optique/core/valueparser";
@@ -17,7 +17,7 @@ import { fileURLToPath, pathToFileURL } from "node:url";
17
17
 
18
18
  //#region deno.json
19
19
  var name = "@optique/man";
20
- var version = "1.4.0-dev.2979+a175cdb7";
20
+ var version = "1.4.0-dev.2992+c4f1ddf9";
21
21
  var license = "MIT";
22
22
  var exports = {
23
23
  ".": "./src/index.ts",
@@ -1,4 +1,4 @@
1
- import { formatDocPageAsMan } from "./man-do5Ey935.js";
1
+ import { formatDocPageAsMan } from "./man-B8NXqGxL.js";
2
2
  import { getDocPageAsync, getDocPageSync } from "@optique/core/parser";
3
3
 
4
4
  //#region src/generator.ts
@@ -57,6 +57,8 @@ function extractParserAndOptions(parserOrProgram, options) {
57
57
  environment: programOptions.environment,
58
58
  showEnvironment: programOptions.showEnvironment,
59
59
  showAliases: programOptions.showAliases,
60
+ showDefault: programOptions.showDefault,
61
+ showChoices: programOptions.showChoices,
60
62
  files: programOptions.files,
61
63
  exitStatus: programOptions.exitStatus
62
64
  }
@@ -1,6 +1,6 @@
1
- import { ManPageOptions } from "./man-Czp4fceb.cjs";
2
- import { Mode, ModeValue, Parser } from "@optique/core/parser";
1
+ import { ManPageOptions } from "./man-DTinCsVW.js";
3
2
  import { Program } from "@optique/core/program";
3
+ import { Mode, ModeValue, Parser } from "@optique/core/parser";
4
4
 
5
5
  //#region src/generator.d.ts
6
6
 
@@ -51,6 +51,8 @@ interface GenerateManPageProgramOptions extends Partial<Omit<ManPageOptions, "se
51
51
  * @param options The man page generation options.
52
52
  * @returns The complete man page in roff format.
53
53
  * @throws {TypeError} If the input is not a valid Parser or Program.
54
+ * @throws {RangeError} If `showChoices.maxItems` is less than `1` or a man
55
+ * page section number is invalid.
54
56
  * @since 0.10.0
55
57
  */
56
58
  declare function generateManPageSync<T>(program: Program<"sync", T>, options: GenerateManPageProgramOptions): string;
@@ -82,6 +84,8 @@ declare function generateManPageSync(parser: Parser<"sync", unknown, unknown>, o
82
84
  * @param options The man page generation options.
83
85
  * @returns A promise that resolves to the complete man page in roff format.
84
86
  * @throws {TypeError} If the input is not a valid Parser or Program.
87
+ * @throws {RangeError} If `showChoices.maxItems` is less than `1` or a man
88
+ * page section number is invalid.
85
89
  * @since 0.10.0
86
90
  */
87
91
  declare function generateManPageAsync<M extends Mode, T>(program: Program<M, T>, options: GenerateManPageProgramOptions): Promise<string>;
@@ -150,6 +154,8 @@ declare function generateManPageAsync<M extends Mode>(parser: Parser<M, unknown,
150
154
  * @param options The man page generation options.
151
155
  * @returns The complete man page in roff format, or a Promise for async parsers.
152
156
  * @throws {TypeError} If the input is not a valid Parser or Program.
157
+ * @throws {RangeError} If `showChoices.maxItems` is less than `1` or a man
158
+ * page section number is invalid.
153
159
  * @since 0.10.0
154
160
  */
155
161
  declare function generateManPage<T>(program: Program<"sync", T>, options: GenerateManPageProgramOptions): string;
@@ -1,6 +1,6 @@
1
- import { ManPageOptions } from "./man-DgWDaZfO.js";
2
- import { Program } from "@optique/core/program";
1
+ import { ManPageOptions } from "./man-CZ-4svg4.cjs";
3
2
  import { Mode, ModeValue, Parser } from "@optique/core/parser";
3
+ import { Program } from "@optique/core/program";
4
4
 
5
5
  //#region src/generator.d.ts
6
6
 
@@ -51,6 +51,8 @@ interface GenerateManPageProgramOptions extends Partial<Omit<ManPageOptions, "se
51
51
  * @param options The man page generation options.
52
52
  * @returns The complete man page in roff format.
53
53
  * @throws {TypeError} If the input is not a valid Parser or Program.
54
+ * @throws {RangeError} If `showChoices.maxItems` is less than `1` or a man
55
+ * page section number is invalid.
54
56
  * @since 0.10.0
55
57
  */
56
58
  declare function generateManPageSync<T>(program: Program<"sync", T>, options: GenerateManPageProgramOptions): string;
@@ -82,6 +84,8 @@ declare function generateManPageSync(parser: Parser<"sync", unknown, unknown>, o
82
84
  * @param options The man page generation options.
83
85
  * @returns A promise that resolves to the complete man page in roff format.
84
86
  * @throws {TypeError} If the input is not a valid Parser or Program.
87
+ * @throws {RangeError} If `showChoices.maxItems` is less than `1` or a man
88
+ * page section number is invalid.
85
89
  * @since 0.10.0
86
90
  */
87
91
  declare function generateManPageAsync<M extends Mode, T>(program: Program<M, T>, options: GenerateManPageProgramOptions): Promise<string>;
@@ -150,6 +154,8 @@ declare function generateManPageAsync<M extends Mode>(parser: Parser<M, unknown,
150
154
  * @param options The man page generation options.
151
155
  * @returns The complete man page in roff format, or a Promise for async parsers.
152
156
  * @throws {TypeError} If the input is not a valid Parser or Program.
157
+ * @throws {RangeError} If `showChoices.maxItems` is less than `1` or a man
158
+ * page section number is invalid.
153
159
  * @since 0.10.0
154
160
  */
155
161
  declare function generateManPage<T>(program: Program<"sync", T>, options: GenerateManPageProgramOptions): string;
@@ -1,5 +1,5 @@
1
1
  const require_chunk = require('./chunk-CUT6urMc.cjs');
2
- const require_man = require('./man-ChbwsIhG.cjs');
2
+ const require_man = require('./man-CHy1T6Mq.cjs');
3
3
  const __optique_core_parser = require_chunk.__toESM(require("@optique/core/parser"));
4
4
 
5
5
  //#region src/generator.ts
@@ -58,6 +58,8 @@ function extractParserAndOptions(parserOrProgram, options) {
58
58
  environment: programOptions.environment,
59
59
  showEnvironment: programOptions.showEnvironment,
60
60
  showAliases: programOptions.showAliases,
61
+ showDefault: programOptions.showDefault,
62
+ showChoices: programOptions.showChoices,
61
63
  files: programOptions.files,
62
64
  exitStatus: programOptions.exitStatus
63
65
  }
@@ -1,6 +1,6 @@
1
1
  require('./roff-C8MvQ2tL.cjs');
2
- require('./man-ChbwsIhG.cjs');
3
- const require_generator = require('./generator-DYAqAqpo.cjs');
2
+ require('./man-CHy1T6Mq.cjs');
3
+ const require_generator = require('./generator-DnoyvwHV.cjs');
4
4
 
5
5
  exports.generateManPage = require_generator.generateManPage;
6
6
  exports.generateManPageAsync = require_generator.generateManPageAsync;
@@ -1,3 +1,3 @@
1
- import "./man-Czp4fceb.cjs";
2
- import { GenerateManPageOptions, GenerateManPageProgramOptions, generateManPage, generateManPageAsync, generateManPageSync } from "./generator-BOtzZZU6.cjs";
1
+ import "./man-CZ-4svg4.cjs";
2
+ import { GenerateManPageOptions, GenerateManPageProgramOptions, generateManPage, generateManPageAsync, generateManPageSync } from "./generator-Cfh2NRJt.cjs";
3
3
  export { GenerateManPageOptions, GenerateManPageProgramOptions, generateManPage, generateManPageAsync, generateManPageSync };
@@ -1,3 +1,3 @@
1
- import "./man-DgWDaZfO.js";
2
- import { GenerateManPageOptions, GenerateManPageProgramOptions, generateManPage, generateManPageAsync, generateManPageSync } from "./generator-BZBZucRz.js";
1
+ import "./man-DTinCsVW.js";
2
+ import { GenerateManPageOptions, GenerateManPageProgramOptions, generateManPage, generateManPageAsync, generateManPageSync } from "./generator-BVJg1ycJ.js";
3
3
  export { GenerateManPageOptions, GenerateManPageProgramOptions, generateManPage, generateManPageAsync, generateManPageSync };
package/dist/generator.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import "./roff-Bh9bsH6u.js";
2
- import "./man-do5Ey935.js";
3
- import { generateManPage, generateManPageAsync, generateManPageSync } from "./generator-ChMzn8U9.js";
2
+ import "./man-B8NXqGxL.js";
3
+ import { generateManPage, generateManPageAsync, generateManPageSync } from "./generator-BP_7uXi7.js";
4
4
 
5
5
  export { generateManPage, generateManPageAsync, generateManPageSync };
package/dist/index.cjs CHANGED
@@ -1,6 +1,6 @@
1
1
  const require_roff = require('./roff-C8MvQ2tL.cjs');
2
- const require_man = require('./man-ChbwsIhG.cjs');
3
- const require_generator = require('./generator-DYAqAqpo.cjs');
2
+ const require_man = require('./man-CHy1T6Mq.cjs');
3
+ const require_generator = require('./generator-DnoyvwHV.cjs');
4
4
 
5
5
  exports.escapeHyphens = require_roff.escapeHyphens;
6
6
  exports.escapeQuotedValue = require_roff.escapeQuotedValue;
package/dist/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import { ManPageOptions, formatDateForMan, formatDocPageAsMan, formatUsageTermAsRoff } from "./man-Czp4fceb.cjs";
2
- import { GenerateManPageOptions, GenerateManPageProgramOptions, generateManPage, generateManPageAsync, generateManPageSync } from "./generator-BOtzZZU6.cjs";
1
+ import { ManPageOptions, formatDateForMan, formatDocPageAsMan, formatUsageTermAsRoff } from "./man-CZ-4svg4.cjs";
2
+ import { GenerateManPageOptions, GenerateManPageProgramOptions, generateManPage, generateManPageAsync, generateManPageSync } from "./generator-Cfh2NRJt.cjs";
3
3
  import { RoffFormatOptions, escapeHyphens, escapeQuotedValue, escapeRequestArg, escapeRoff, formatMessageAsRoff } from "./roff-Cpm_FJok.cjs";
4
4
  export { type GenerateManPageOptions, type GenerateManPageProgramOptions, type ManPageOptions, type RoffFormatOptions, escapeHyphens, escapeQuotedValue, escapeRequestArg, escapeRoff, formatDateForMan, formatDocPageAsMan, formatMessageAsRoff, formatUsageTermAsRoff, generateManPage, generateManPageAsync, generateManPageSync };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ManPageOptions, formatDateForMan, formatDocPageAsMan, formatUsageTermAsRoff } from "./man-DgWDaZfO.js";
2
- import { GenerateManPageOptions, GenerateManPageProgramOptions, generateManPage, generateManPageAsync, generateManPageSync } from "./generator-BZBZucRz.js";
1
+ import { ManPageOptions, formatDateForMan, formatDocPageAsMan, formatUsageTermAsRoff } from "./man-DTinCsVW.js";
2
+ import { GenerateManPageOptions, GenerateManPageProgramOptions, generateManPage, generateManPageAsync, generateManPageSync } from "./generator-BVJg1ycJ.js";
3
3
  import { RoffFormatOptions, escapeHyphens, escapeQuotedValue, escapeRequestArg, escapeRoff, formatMessageAsRoff } from "./roff-DkJvVQ1o.js";
4
4
  export { type GenerateManPageOptions, type GenerateManPageProgramOptions, type ManPageOptions, type RoffFormatOptions, escapeHyphens, escapeQuotedValue, escapeRequestArg, escapeRoff, formatDateForMan, formatDocPageAsMan, formatMessageAsRoff, formatUsageTermAsRoff, generateManPage, generateManPageAsync, generateManPageSync };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { escapeHyphens, escapeQuotedValue, escapeRequestArg, escapeRoff, formatMessageAsRoff } from "./roff-Bh9bsH6u.js";
2
- import { formatDateForMan, formatDocPageAsMan, formatUsageTermAsRoff } from "./man-do5Ey935.js";
3
- import { generateManPage, generateManPageAsync, generateManPageSync } from "./generator-ChMzn8U9.js";
2
+ import { formatDateForMan, formatDocPageAsMan, formatUsageTermAsRoff } from "./man-B8NXqGxL.js";
3
+ import { generateManPage, generateManPageAsync, generateManPageSync } from "./generator-BP_7uXi7.js";
4
4
 
5
5
  export { escapeHyphens, escapeQuotedValue, escapeRequestArg, escapeRoff, formatDateForMan, formatDocPageAsMan, formatMessageAsRoff, formatUsageTermAsRoff, generateManPage, generateManPageAsync, generateManPageSync };
@@ -241,40 +241,61 @@ function formatAliasesAnnotation(entry, showAliases) {
241
241
  };
242
242
  }
243
243
  /**
244
- * Formats a {@link DocSection} as roff markup with .TP macros.
245
- *
246
- * @param section The section to format.
247
- * @returns The roff-formatted section content.
244
+ * Limits choices by counting value terms, preserving structured messages and
245
+ * removing the text separator immediately before the first omitted value.
248
246
  */
249
- function formatDocSectionEntries(section, showEnvironment = false, generatedEnvironment = false, showAliases = false) {
247
+ function truncateChoices(choices, maxItems) {
248
+ if (maxItems < Infinity) {
249
+ let valueCount = 0;
250
+ for (let i = 0; i < choices.length; i++) if (choices[i].type === "value" && ++valueCount > maxItems) {
251
+ const cutIndex = i > 0 && choices[i - 1].type === "text" ? i - 1 : i;
252
+ return [...choices.slice(0, cutIndex), {
253
+ type: "text",
254
+ text: ", ..."
255
+ }];
256
+ }
257
+ }
258
+ return choices;
259
+ }
260
+ /**
261
+ * Formats a {@link DocSection} as roff markup with .TP macros and the
262
+ * requested annotations.
263
+ */
264
+ function formatDocSectionEntries(section, options = {}) {
250
265
  const lines = [];
251
266
  for (const entry of section.entries) {
252
267
  const termStr = formatDocEntryTerm(entry.term);
253
268
  if (termStr === "") continue;
254
269
  lines.push(".TP");
255
270
  lines.push(termStr);
256
- const envNames = showEnvironment ? [...new Set(entry.envVars ?? [])].filter((name) => name !== "") : [];
271
+ const envNames = options.showEnvironment ? [...new Set(entry.envVars ?? [])].filter((name) => name !== "") : [];
257
272
  const envAnnotation = envNames.length === 0 ? "" : `[env: ${envNames.map((name) => formatMessageAsRoff([{
258
273
  type: "envVar",
259
274
  envVar: name
260
275
  }])).join(", ")}]`;
261
- const aliases = formatAliasesAnnotation(entry, showAliases);
262
- if (entry.description) {
263
- let desc = formatMessageAsRoff(entry.description, generatedEnvironment ? { quotes: false } : {});
264
- if (aliases != null) desc += escapeHyphens(escapeRoff(aliases.prefix + aliases.body));
265
- if (entry.default) desc += ` [${formatMessageAsRoff(entry.default)}]`;
266
- if (entry.choices) desc += ` (choices: ${formatMessageAsRoff(entry.choices, { quotes: false })})`;
267
- const separator = desc === "" || desc.endsWith("\n") ? "" : " ";
268
- const body = envAnnotation === "" ? desc : desc + separator + envAnnotation;
269
- lines.push(generatedEnvironment && body === "" ? "\\&" : body);
270
- } else if (aliases != null || entry.default || entry.choices || envAnnotation !== "") {
271
- const parts = [];
272
- if (aliases != null) parts.push(escapeHyphens(escapeRoff(aliases.prefix.trimStart() + aliases.body)));
273
- if (entry.default) parts.push(`[${formatMessageAsRoff(entry.default)}]`);
274
- if (entry.choices) parts.push(`(choices: ${formatMessageAsRoff(entry.choices, { quotes: false })})`);
275
- if (envAnnotation !== "") parts.push(envAnnotation);
276
- lines.push(parts.join(" "));
277
- } else if (generatedEnvironment) lines.push("\\&");
276
+ let body = entry.description == null ? "" : formatMessageAsRoff(entry.description, options.generatedEnvironment ? { quotes: false } : {});
277
+ const annotationPrefix = (prefix) => body === "" || body.endsWith("\n") ? prefix.replace(/^[^\S\r\n]+/, "") : prefix;
278
+ const aliases = formatAliasesAnnotation(entry, options.showAliases ?? false);
279
+ if (aliases != null) body += escapeHyphens(escapeRoff(annotationPrefix(aliases.prefix + aliases.body)));
280
+ if (options.showDefault !== false && entry.default?.length) {
281
+ const config = typeof options.showDefault === "object" ? options.showDefault : {};
282
+ body += escapeRoff(annotationPrefix(config.prefix ?? " (default: "));
283
+ body += formatMessageAsRoff(entry.default);
284
+ body += escapeRoff(config.suffix ?? ")");
285
+ }
286
+ if (options.showChoices !== false && entry.choices?.length) {
287
+ const config = typeof options.showChoices === "object" ? options.showChoices : {};
288
+ const maxItems = options.showChoices === void 0 ? Infinity : config.maxItems ?? 8;
289
+ body += escapeRoff(annotationPrefix((config.prefix ?? " (") + (config.label ?? "choices: ")));
290
+ body += formatMessageAsRoff(truncateChoices(entry.choices, maxItems), { quotes: false });
291
+ body += escapeRoff(config.suffix ?? ")");
292
+ }
293
+ if (envAnnotation !== "") {
294
+ const separator = body === "" || body.endsWith("\n") ? "" : " ";
295
+ body += separator + envAnnotation;
296
+ }
297
+ if (body !== "") lines.push(body);
298
+ else if (options.generatedEnvironment) lines.push("\\&");
278
299
  }
279
300
  return lines.join("\n");
280
301
  }
@@ -308,10 +329,12 @@ function formatDocSectionEntries(section, showEnvironment = false, generatedEnvi
308
329
  * @throws {TypeError} If the program name is empty or a generated environment
309
330
  * section title is empty, whitespace-only, or contains control characters.
310
331
  * @throws {RangeError} If the section number or any `seeAlso` entry's section
311
- * number is not a valid man page section (1–8).
332
+ * number is not a valid man page section (1–8), or `showChoices.maxItems`
333
+ * is less than `1`.
312
334
  * @since 0.10.0
313
335
  */
314
336
  function formatDocPageAsMan(page, options) {
337
+ if (typeof options.showChoices === "object" && options.showChoices.maxItems != null && options.showChoices.maxItems < 1) throw new RangeError(`showChoices.maxItems must be at least 1, but got ${options.showChoices.maxItems}.`);
315
338
  if (options.name === "") throw new TypeError("Program name must not be empty.");
316
339
  if (!Number.isInteger(options.section) || options.section < 1 || options.section > 8) {
317
340
  let repr;
@@ -352,7 +375,12 @@ function formatDocPageAsMan(page, options) {
352
375
  const inlineEnvironment = placement === "inline" || placement === "both";
353
376
  for (const section of page.sections) {
354
377
  if (section.entries.length === 0) continue;
355
- const content = formatDocSectionEntries(section, inlineEnvironment, false, options.showAliases ?? false);
378
+ const content = formatDocSectionEntries(section, {
379
+ showEnvironment: inlineEnvironment,
380
+ showAliases: options.showAliases,
381
+ showDefault: options.showDefault,
382
+ showChoices: options.showChoices
383
+ });
356
384
  if (content === "") continue;
357
385
  const title = section.title?.toUpperCase() ?? inferSectionTitle(section.entries);
358
386
  lines.push(`.SH "${escapeRequestArg(title)}"`);
@@ -364,7 +392,11 @@ function formatDocPageAsMan(page, options) {
364
392
  }) : void 0;
365
393
  const environment = options.environment ?? automaticEnvironment;
366
394
  if (environment != null && environment.entries.length > 0) {
367
- const content = formatDocSectionEntries(environment, false, automaticEnvironment != null);
395
+ const content = formatDocSectionEntries(environment, {
396
+ generatedEnvironment: automaticEnvironment != null,
397
+ showDefault: options.showDefault,
398
+ showChoices: options.showChoices
399
+ });
368
400
  if (content !== "") {
369
401
  const title = automaticEnvironment == null ? "ENVIRONMENT" : automaticEnvironment.title?.toUpperCase() ?? "ENVIRONMENT";
370
402
  lines.push(title === "ENVIRONMENT" ? ".SH ENVIRONMENT" : `.SH "${escapeRequestArg(title)}"`);
@@ -372,14 +404,20 @@ function formatDocPageAsMan(page, options) {
372
404
  }
373
405
  }
374
406
  if (options.files && options.files.entries.length > 0) {
375
- const content = formatDocSectionEntries(options.files);
407
+ const content = formatDocSectionEntries(options.files, {
408
+ showDefault: options.showDefault,
409
+ showChoices: options.showChoices
410
+ });
376
411
  if (content !== "") {
377
412
  lines.push(".SH FILES");
378
413
  lines.push(content);
379
414
  }
380
415
  }
381
416
  if (options.exitStatus && options.exitStatus.entries.length > 0) {
382
- const content = formatDocSectionEntries(options.exitStatus);
417
+ const content = formatDocSectionEntries(options.exitStatus, {
418
+ showDefault: options.showDefault,
419
+ showChoices: options.showChoices
420
+ });
383
421
  if (content !== "") {
384
422
  lines.push(".SH EXIT STATUS");
385
423
  lines.push(content);
@@ -242,40 +242,61 @@ function formatAliasesAnnotation(entry, showAliases) {
242
242
  };
243
243
  }
244
244
  /**
245
- * Formats a {@link DocSection} as roff markup with .TP macros.
246
- *
247
- * @param section The section to format.
248
- * @returns The roff-formatted section content.
245
+ * Limits choices by counting value terms, preserving structured messages and
246
+ * removing the text separator immediately before the first omitted value.
249
247
  */
250
- function formatDocSectionEntries(section, showEnvironment = false, generatedEnvironment = false, showAliases = false) {
248
+ function truncateChoices(choices, maxItems) {
249
+ if (maxItems < Infinity) {
250
+ let valueCount = 0;
251
+ for (let i = 0; i < choices.length; i++) if (choices[i].type === "value" && ++valueCount > maxItems) {
252
+ const cutIndex = i > 0 && choices[i - 1].type === "text" ? i - 1 : i;
253
+ return [...choices.slice(0, cutIndex), {
254
+ type: "text",
255
+ text: ", ..."
256
+ }];
257
+ }
258
+ }
259
+ return choices;
260
+ }
261
+ /**
262
+ * Formats a {@link DocSection} as roff markup with .TP macros and the
263
+ * requested annotations.
264
+ */
265
+ function formatDocSectionEntries(section, options = {}) {
251
266
  const lines = [];
252
267
  for (const entry of section.entries) {
253
268
  const termStr = formatDocEntryTerm(entry.term);
254
269
  if (termStr === "") continue;
255
270
  lines.push(".TP");
256
271
  lines.push(termStr);
257
- const envNames = showEnvironment ? [...new Set(entry.envVars ?? [])].filter((name) => name !== "") : [];
272
+ const envNames = options.showEnvironment ? [...new Set(entry.envVars ?? [])].filter((name) => name !== "") : [];
258
273
  const envAnnotation = envNames.length === 0 ? "" : `[env: ${envNames.map((name) => require_roff.formatMessageAsRoff([{
259
274
  type: "envVar",
260
275
  envVar: name
261
276
  }])).join(", ")}]`;
262
- const aliases = formatAliasesAnnotation(entry, showAliases);
263
- if (entry.description) {
264
- let desc = require_roff.formatMessageAsRoff(entry.description, generatedEnvironment ? { quotes: false } : {});
265
- if (aliases != null) desc += require_roff.escapeHyphens(require_roff.escapeRoff(aliases.prefix + aliases.body));
266
- if (entry.default) desc += ` [${require_roff.formatMessageAsRoff(entry.default)}]`;
267
- if (entry.choices) desc += ` (choices: ${require_roff.formatMessageAsRoff(entry.choices, { quotes: false })})`;
268
- const separator = desc === "" || desc.endsWith("\n") ? "" : " ";
269
- const body = envAnnotation === "" ? desc : desc + separator + envAnnotation;
270
- lines.push(generatedEnvironment && body === "" ? "\\&" : body);
271
- } else if (aliases != null || entry.default || entry.choices || envAnnotation !== "") {
272
- const parts = [];
273
- if (aliases != null) parts.push(require_roff.escapeHyphens(require_roff.escapeRoff(aliases.prefix.trimStart() + aliases.body)));
274
- if (entry.default) parts.push(`[${require_roff.formatMessageAsRoff(entry.default)}]`);
275
- if (entry.choices) parts.push(`(choices: ${require_roff.formatMessageAsRoff(entry.choices, { quotes: false })})`);
276
- if (envAnnotation !== "") parts.push(envAnnotation);
277
- lines.push(parts.join(" "));
278
- } else if (generatedEnvironment) lines.push("\\&");
277
+ let body = entry.description == null ? "" : require_roff.formatMessageAsRoff(entry.description, options.generatedEnvironment ? { quotes: false } : {});
278
+ const annotationPrefix = (prefix) => body === "" || body.endsWith("\n") ? prefix.replace(/^[^\S\r\n]+/, "") : prefix;
279
+ const aliases = formatAliasesAnnotation(entry, options.showAliases ?? false);
280
+ if (aliases != null) body += require_roff.escapeHyphens(require_roff.escapeRoff(annotationPrefix(aliases.prefix + aliases.body)));
281
+ if (options.showDefault !== false && entry.default?.length) {
282
+ const config = typeof options.showDefault === "object" ? options.showDefault : {};
283
+ body += require_roff.escapeRoff(annotationPrefix(config.prefix ?? " (default: "));
284
+ body += require_roff.formatMessageAsRoff(entry.default);
285
+ body += require_roff.escapeRoff(config.suffix ?? ")");
286
+ }
287
+ if (options.showChoices !== false && entry.choices?.length) {
288
+ const config = typeof options.showChoices === "object" ? options.showChoices : {};
289
+ const maxItems = options.showChoices === void 0 ? Infinity : config.maxItems ?? 8;
290
+ body += require_roff.escapeRoff(annotationPrefix((config.prefix ?? " (") + (config.label ?? "choices: ")));
291
+ body += require_roff.formatMessageAsRoff(truncateChoices(entry.choices, maxItems), { quotes: false });
292
+ body += require_roff.escapeRoff(config.suffix ?? ")");
293
+ }
294
+ if (envAnnotation !== "") {
295
+ const separator = body === "" || body.endsWith("\n") ? "" : " ";
296
+ body += separator + envAnnotation;
297
+ }
298
+ if (body !== "") lines.push(body);
299
+ else if (options.generatedEnvironment) lines.push("\\&");
279
300
  }
280
301
  return lines.join("\n");
281
302
  }
@@ -309,10 +330,12 @@ function formatDocSectionEntries(section, showEnvironment = false, generatedEnvi
309
330
  * @throws {TypeError} If the program name is empty or a generated environment
310
331
  * section title is empty, whitespace-only, or contains control characters.
311
332
  * @throws {RangeError} If the section number or any `seeAlso` entry's section
312
- * number is not a valid man page section (1–8).
333
+ * number is not a valid man page section (1–8), or `showChoices.maxItems`
334
+ * is less than `1`.
313
335
  * @since 0.10.0
314
336
  */
315
337
  function formatDocPageAsMan(page, options) {
338
+ if (typeof options.showChoices === "object" && options.showChoices.maxItems != null && options.showChoices.maxItems < 1) throw new RangeError(`showChoices.maxItems must be at least 1, but got ${options.showChoices.maxItems}.`);
316
339
  if (options.name === "") throw new TypeError("Program name must not be empty.");
317
340
  if (!Number.isInteger(options.section) || options.section < 1 || options.section > 8) {
318
341
  let repr;
@@ -353,7 +376,12 @@ function formatDocPageAsMan(page, options) {
353
376
  const inlineEnvironment = placement === "inline" || placement === "both";
354
377
  for (const section of page.sections) {
355
378
  if (section.entries.length === 0) continue;
356
- const content = formatDocSectionEntries(section, inlineEnvironment, false, options.showAliases ?? false);
379
+ const content = formatDocSectionEntries(section, {
380
+ showEnvironment: inlineEnvironment,
381
+ showAliases: options.showAliases,
382
+ showDefault: options.showDefault,
383
+ showChoices: options.showChoices
384
+ });
357
385
  if (content === "") continue;
358
386
  const title = section.title?.toUpperCase() ?? inferSectionTitle(section.entries);
359
387
  lines.push(`.SH "${require_roff.escapeRequestArg(title)}"`);
@@ -365,7 +393,11 @@ function formatDocPageAsMan(page, options) {
365
393
  }) : void 0;
366
394
  const environment = options.environment ?? automaticEnvironment;
367
395
  if (environment != null && environment.entries.length > 0) {
368
- const content = formatDocSectionEntries(environment, false, automaticEnvironment != null);
396
+ const content = formatDocSectionEntries(environment, {
397
+ generatedEnvironment: automaticEnvironment != null,
398
+ showDefault: options.showDefault,
399
+ showChoices: options.showChoices
400
+ });
369
401
  if (content !== "") {
370
402
  const title = automaticEnvironment == null ? "ENVIRONMENT" : automaticEnvironment.title?.toUpperCase() ?? "ENVIRONMENT";
371
403
  lines.push(title === "ENVIRONMENT" ? ".SH ENVIRONMENT" : `.SH "${require_roff.escapeRequestArg(title)}"`);
@@ -373,14 +405,20 @@ function formatDocPageAsMan(page, options) {
373
405
  }
374
406
  }
375
407
  if (options.files && options.files.entries.length > 0) {
376
- const content = formatDocSectionEntries(options.files);
408
+ const content = formatDocSectionEntries(options.files, {
409
+ showDefault: options.showDefault,
410
+ showChoices: options.showChoices
411
+ });
377
412
  if (content !== "") {
378
413
  lines.push(".SH FILES");
379
414
  lines.push(content);
380
415
  }
381
416
  }
382
417
  if (options.exitStatus && options.exitStatus.entries.length > 0) {
383
- const content = formatDocSectionEntries(options.exitStatus);
418
+ const content = formatDocSectionEntries(options.exitStatus, {
419
+ showDefault: options.showDefault,
420
+ showChoices: options.showChoices
421
+ });
384
422
  if (content !== "") {
385
423
  lines.push(".SH EXIT STATUS");
386
424
  lines.push(content);
@@ -1,4 +1,4 @@
1
- import { DocPage, DocSection, ShowAliasesOptions, ShowEnvironmentOptions } from "@optique/core/doc";
1
+ import { DocPage, DocSection, ShowAliasesOptions, ShowChoicesOptions, ShowDefaultOptions, ShowEnvironmentOptions } from "@optique/core/doc";
2
2
  import { Message } from "@optique/core/message";
3
3
  import { UsageTerm } from "@optique/core/usage";
4
4
 
@@ -88,6 +88,25 @@ interface ManPageOptions {
88
88
  * @since 1.4.0
89
89
  */
90
90
  readonly showAliases?: boolean | ShowAliasesOptions;
91
+ /**
92
+ * Whether and how to display default values. Omitted, `true`, and `{}`
93
+ * display `(default: value)`, with prefix `" (default: "` and suffix `")"`.
94
+ * Unlike help output, man pages show defaults automatically. Set to `false`
95
+ * to hide them, or use `{ prefix: " [", suffix: "]" }` for the old format.
96
+ * Explicit empty prefixes and suffixes are preserved.
97
+ * @since 1.4.0
98
+ */
99
+ readonly showDefault?: boolean | ShowDefaultOptions;
100
+ /**
101
+ * Whether and how to display choices. When omitted, displays all choices.
102
+ * Explicit `true` or `{}` uses prefix `" ("`, label `"choices: "`, suffix
103
+ * `")"`, and a limit of eight value terms, followed by `...` if truncated.
104
+ * Set to `false` to hide choices or set `maxItems` to `Infinity` to show all.
105
+ * Explicit empty prefixes, labels, and suffixes are preserved.
106
+ * @throws {RangeError} If `maxItems` is less than `1`.
107
+ * @since 1.4.0
108
+ */
109
+ readonly showChoices?: boolean | ShowChoicesOptions;
91
110
  /**
92
111
  * File paths to document in the FILES section.
93
112
  */
@@ -172,7 +191,8 @@ declare function formatUsageTermAsRoff(term: UsageTerm): string;
172
191
  * @throws {TypeError} If the program name is empty or a generated environment
173
192
  * section title is empty, whitespace-only, or contains control characters.
174
193
  * @throws {RangeError} If the section number or any `seeAlso` entry's section
175
- * number is not a valid man page section (1–8).
194
+ * number is not a valid man page section (1–8), or `showChoices.maxItems`
195
+ * is less than `1`.
176
196
  * @since 0.10.0
177
197
  */
178
198
  declare function formatDocPageAsMan(page: DocPage, options: ManPageOptions): string;
@@ -1,5 +1,5 @@
1
1
  import { Message } from "@optique/core/message";
2
- import { DocPage, DocSection, ShowAliasesOptions, ShowEnvironmentOptions } from "@optique/core/doc";
2
+ import { DocPage, DocSection, ShowAliasesOptions, ShowChoicesOptions, ShowDefaultOptions, ShowEnvironmentOptions } from "@optique/core/doc";
3
3
  import { UsageTerm } from "@optique/core/usage";
4
4
 
5
5
  //#region src/man.d.ts
@@ -88,6 +88,25 @@ interface ManPageOptions {
88
88
  * @since 1.4.0
89
89
  */
90
90
  readonly showAliases?: boolean | ShowAliasesOptions;
91
+ /**
92
+ * Whether and how to display default values. Omitted, `true`, and `{}`
93
+ * display `(default: value)`, with prefix `" (default: "` and suffix `")"`.
94
+ * Unlike help output, man pages show defaults automatically. Set to `false`
95
+ * to hide them, or use `{ prefix: " [", suffix: "]" }` for the old format.
96
+ * Explicit empty prefixes and suffixes are preserved.
97
+ * @since 1.4.0
98
+ */
99
+ readonly showDefault?: boolean | ShowDefaultOptions;
100
+ /**
101
+ * Whether and how to display choices. When omitted, displays all choices.
102
+ * Explicit `true` or `{}` uses prefix `" ("`, label `"choices: "`, suffix
103
+ * `")"`, and a limit of eight value terms, followed by `...` if truncated.
104
+ * Set to `false` to hide choices or set `maxItems` to `Infinity` to show all.
105
+ * Explicit empty prefixes, labels, and suffixes are preserved.
106
+ * @throws {RangeError} If `maxItems` is less than `1`.
107
+ * @since 1.4.0
108
+ */
109
+ readonly showChoices?: boolean | ShowChoicesOptions;
91
110
  /**
92
111
  * File paths to document in the FILES section.
93
112
  */
@@ -172,7 +191,8 @@ declare function formatUsageTermAsRoff(term: UsageTerm): string;
172
191
  * @throws {TypeError} If the program name is empty or a generated environment
173
192
  * section title is empty, whitespace-only, or contains control characters.
174
193
  * @throws {RangeError} If the section number or any `seeAlso` entry's section
175
- * number is not a valid man page section (1–8).
194
+ * number is not a valid man page section (1–8), or `showChoices.maxItems`
195
+ * is less than `1`.
176
196
  * @since 0.10.0
177
197
  */
178
198
  declare function formatDocPageAsMan(page: DocPage, options: ManPageOptions): string;
package/dist/man.cjs CHANGED
@@ -1,5 +1,5 @@
1
1
  require('./roff-C8MvQ2tL.cjs');
2
- const require_man = require('./man-ChbwsIhG.cjs');
2
+ const require_man = require('./man-CHy1T6Mq.cjs');
3
3
 
4
4
  exports.formatDateForMan = require_man.formatDateForMan;
5
5
  exports.formatDocPageAsMan = require_man.formatDocPageAsMan;
package/dist/man.d.cts CHANGED
@@ -1,2 +1,2 @@
1
- import { ManPageOptions, ManPageSection, formatDateForMan, formatDocPageAsMan, formatUsageTermAsRoff } from "./man-Czp4fceb.cjs";
1
+ import { ManPageOptions, ManPageSection, formatDateForMan, formatDocPageAsMan, formatUsageTermAsRoff } from "./man-CZ-4svg4.cjs";
2
2
  export { ManPageOptions, ManPageSection, formatDateForMan, formatDocPageAsMan, formatUsageTermAsRoff };
package/dist/man.d.ts CHANGED
@@ -1,2 +1,2 @@
1
- import { ManPageOptions, ManPageSection, formatDateForMan, formatDocPageAsMan, formatUsageTermAsRoff } from "./man-DgWDaZfO.js";
1
+ import { ManPageOptions, ManPageSection, formatDateForMan, formatDocPageAsMan, formatUsageTermAsRoff } from "./man-DTinCsVW.js";
2
2
  export { ManPageOptions, ManPageSection, formatDateForMan, formatDocPageAsMan, formatUsageTermAsRoff };
package/dist/man.js CHANGED
@@ -1,4 +1,4 @@
1
1
  import "./roff-Bh9bsH6u.js";
2
- import { formatDateForMan, formatDocPageAsMan, formatUsageTermAsRoff } from "./man-do5Ey935.js";
2
+ import { formatDateForMan, formatDocPageAsMan, formatUsageTermAsRoff } from "./man-B8NXqGxL.js";
3
3
 
4
4
  export { formatDateForMan, formatDocPageAsMan, formatUsageTermAsRoff };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/man",
3
- "version": "1.4.0-dev.2979",
3
+ "version": "1.4.0-dev.2992",
4
4
  "description": "Man pages and Markdown command references for Optique",
5
5
  "keywords": [
6
6
  "CLI",
@@ -98,11 +98,11 @@
98
98
  "optique-man": "./dist/cli.js"
99
99
  },
100
100
  "dependencies": {
101
- "@optique/core": "1.4.0-dev.2979+a175cdb7",
102
- "@optique/run": "1.4.0-dev.2979+a175cdb7"
101
+ "@optique/core": "1.4.0-dev.2992+c4f1ddf9",
102
+ "@optique/run": "1.4.0-dev.2992+c4f1ddf9"
103
103
  },
104
104
  "devDependencies": {
105
- "@optique/testing": "1.4.0-dev.2979+a175cdb7",
105
+ "@optique/testing": "1.4.0-dev.2992+c4f1ddf9",
106
106
  "@types/node": "^24.0.0",
107
107
  "tsdown": "^0.13.0",
108
108
  "tsx": "^4.21.0",