@optique/core 0.5.0 → 0.6.0-dev.102

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/facade.js CHANGED
@@ -1,11 +1,12 @@
1
- import { formatMessage, message, optionName } from "./message.js";
1
+ import { formatMessage, message, optionName, text, value } from "./message.js";
2
2
  import { longestMatch, object } from "./constructs.js";
3
3
  import { formatUsage } from "./usage.js";
4
4
  import { formatDocPage } from "./doc.js";
5
+ import { bash, fish, pwsh, zsh } from "./completion.js";
5
6
  import { multiple, optional } from "./modifiers.js";
6
7
  import { string } from "./valueparser.js";
7
8
  import { argument, command, constant, flag } from "./primitives.js";
8
- import { getDocPage, parse } from "./parser.js";
9
+ import { getDocPage, parse, suggest } from "./parser.js";
9
10
 
10
11
  //#region src/facade.ts
11
12
  /**
@@ -118,6 +119,13 @@ function combineWithHelpVersion(originalParser, helpParsers, versionParsers) {
118
119
  value: state
119
120
  };
120
121
  },
122
+ suggest(_context, prefix) {
123
+ if ("--help".startsWith(prefix)) return [{
124
+ kind: "literal",
125
+ text: "--help"
126
+ }];
127
+ return [];
128
+ },
121
129
  getDocFragments(state) {
122
130
  return helpParsers.helpOption?.getDocFragments(state) ?? { fragments: [] };
123
131
  }
@@ -179,6 +187,13 @@ function combineWithHelpVersion(originalParser, helpParsers, versionParsers) {
179
187
  value: state
180
188
  };
181
189
  },
190
+ suggest(_context, prefix) {
191
+ if ("--version".startsWith(prefix)) return [{
192
+ kind: "literal",
193
+ text: "--version"
194
+ }];
195
+ return [];
196
+ },
182
197
  getDocFragments(state) {
183
198
  return versionParsers.versionOption?.getDocFragments(state) ?? { fragments: [] };
184
199
  }
@@ -213,9 +228,9 @@ function classifyResult(result, args) {
213
228
  type: "error",
214
229
  error: result.error
215
230
  };
216
- const value = result.value;
217
- if (typeof value === "object" && value != null && "help" in value && "version" in value) {
218
- const parsedValue = value;
231
+ const value$1 = result.value;
232
+ if (typeof value$1 === "object" && value$1 != null && "help" in value$1 && "version" in value$1) {
233
+ const parsedValue = value$1;
219
234
  const hasVersionOption = args.includes("--version");
220
235
  const hasVersionCommand = args.length > 0 && args[0] === "version";
221
236
  const hasHelpOption = args.includes("--help");
@@ -237,15 +252,59 @@ function classifyResult(result, args) {
237
252
  if ((hasVersionOption || hasVersionCommand) && (parsedValue.version || parsedValue.versionFlag)) return { type: "version" };
238
253
  return {
239
254
  type: "success",
240
- value: parsedValue.result ?? value
255
+ value: parsedValue.result ?? value$1
241
256
  };
242
257
  }
243
258
  return {
244
259
  type: "success",
245
- value
260
+ value: value$1
246
261
  };
247
262
  }
248
263
  /**
264
+ * Handles shell completion requests.
265
+ * @since 0.6.0
266
+ */
267
+ function handleCompletion(completionArgs, programName, parser, stdout, stderr, onCompletion, onError, colors) {
268
+ if (completionArgs.length === 0) {
269
+ stderr("Error: Missing shell name for completion.\n");
270
+ stderr("Usage: " + programName + " completion <shell> [args...]\n");
271
+ return onError(1);
272
+ }
273
+ const shellName = completionArgs[0];
274
+ const args = completionArgs.slice(1);
275
+ const availableShells = {
276
+ bash,
277
+ fish,
278
+ pwsh,
279
+ zsh
280
+ };
281
+ const shell = availableShells[shellName];
282
+ if (!shell) {
283
+ const available = [];
284
+ for (const shell$1 in availableShells) {
285
+ if (available.length > 0) available.push(text(", "));
286
+ available.push(value(shell$1));
287
+ }
288
+ stderr(formatMessage(message`Error: Unsupported shell ${shellName}. Available shells: ${available}.`, {
289
+ colors,
290
+ quotes: !colors
291
+ }));
292
+ return onError(1);
293
+ }
294
+ if (args.length === 0) {
295
+ const script = shell.generateScript(programName, ["completion", shellName]);
296
+ stdout(script);
297
+ } else {
298
+ const suggestions = suggest(parser, args);
299
+ for (const chunk of shell.encodeSuggestions(suggestions)) stdout(chunk);
300
+ }
301
+ try {
302
+ return onCompletion(0);
303
+ } catch {
304
+ return onCompletion();
305
+ }
306
+ }
307
+ /**
249
308
  * Runs a parser against command-line arguments with built-in help and error
250
309
  * handling.
251
310
  *
@@ -275,14 +334,31 @@ function classifyResult(result, args) {
275
334
  * @throws {RunError} When parsing fails and no `onError` callback is provided.
276
335
  */
277
336
  function run(parser, programName, args, options = {}) {
337
+ let { colors, maxWidth, showDefault, aboveError = "usage", onError = () => {
338
+ throw new RunError("Failed to parse command line arguments.");
339
+ }, stderr = console.error, stdout = console.log, brief, description, footer } = options;
340
+ const completionMode = options.completion?.mode ?? "both";
341
+ const onCompletion = options.completion?.onShow ?? (() => ({}));
342
+ if (options.completion) {
343
+ if ((completionMode === "command" || completionMode === "both") && args.length >= 1 && args[0] === "completion") return handleCompletion(args.slice(1), programName, parser, stdout, stderr, onCompletion, onError, colors);
344
+ if (completionMode === "option" || completionMode === "both") for (let i = 0; i < args.length; i++) {
345
+ const arg = args[i];
346
+ if (arg.startsWith("--completion=")) {
347
+ const shell = arg.slice(13);
348
+ const completionArgs = args.slice(i + 1);
349
+ return handleCompletion([shell, ...completionArgs], programName, parser, stdout, stderr, onCompletion, onError, colors);
350
+ } else if (arg === "--completion" && i + 1 < args.length) {
351
+ const shell = args[i + 1];
352
+ const completionArgs = args.slice(i + 2);
353
+ return handleCompletion([shell, ...completionArgs], programName, parser, stdout, stderr, onCompletion, onError, colors);
354
+ }
355
+ }
356
+ }
278
357
  const helpMode = options.help?.mode ?? "option";
279
358
  const onHelp = options.help?.onShow ?? (() => ({}));
280
359
  const versionMode = options.version?.mode ?? "option";
281
360
  const versionValue = options.version?.value ?? "";
282
361
  const onVersion = options.version?.onShow ?? (() => ({}));
283
- let { colors, maxWidth, showDefault, aboveError = "usage", onError = () => {
284
- throw new RunError("Failed to parse command line arguments.");
285
- }, stderr = console.error, stdout = console.log, brief, description, footer } = options;
286
362
  const help = options.help ? helpMode : "none";
287
363
  const version = options.version ? versionMode : "none";
288
364
  const helpParsers = help === "none" ? {
@@ -345,36 +421,38 @@ function run(parser, programName, args, options = {}) {
345
421
  return onHelp();
346
422
  }
347
423
  }
348
- case "error": break;
349
- }
350
- if (aboveError === "help") {
351
- const doc = getDocPage(args.length < 1 ? augmentedParser : parser, args);
352
- if (doc == null) aboveError = "usage";
353
- else {
354
- const augmentedDoc = {
355
- ...doc,
356
- brief: brief ?? doc.brief,
357
- description: description ?? doc.description,
358
- footer: footer ?? doc.footer
359
- };
360
- stderr(formatDocPage(programName, augmentedDoc, {
424
+ case "error": {
425
+ if (aboveError === "help") {
426
+ const doc = getDocPage(args.length < 1 ? augmentedParser : parser, args);
427
+ if (doc == null) aboveError = "usage";
428
+ else {
429
+ const augmentedDoc = {
430
+ ...doc,
431
+ brief: brief ?? doc.brief,
432
+ description: description ?? doc.description,
433
+ footer: footer ?? doc.footer
434
+ };
435
+ stderr(formatDocPage(programName, augmentedDoc, {
436
+ colors,
437
+ maxWidth,
438
+ showDefault
439
+ }));
440
+ }
441
+ }
442
+ if (aboveError === "usage") stderr(`Usage: ${indentLines(formatUsage(programName, augmentedParser.usage, {
443
+ colors,
444
+ maxWidth: maxWidth == null ? void 0 : maxWidth - 7,
445
+ expandCommands: true
446
+ }), 7)}`);
447
+ const errorMessage = formatMessage(classified.error, {
361
448
  colors,
362
- maxWidth,
363
- showDefault
364
- }));
449
+ quotes: !colors
450
+ });
451
+ stderr(`Error: ${errorMessage}`);
452
+ return onError(1);
365
453
  }
454
+ default: throw new RunError("Unexpected parse result type");
366
455
  }
367
- if (aboveError === "usage") stderr(`Usage: ${indentLines(formatUsage(programName, augmentedParser.usage, {
368
- colors,
369
- maxWidth: maxWidth == null ? void 0 : maxWidth - 7,
370
- expandCommands: true
371
- }), 7)}`);
372
- const errorMessage = formatMessage(classified.error, {
373
- colors,
374
- quotes: !colors
375
- });
376
- stderr(`Error: ${errorMessage}`);
377
- return onError(1);
378
456
  }
379
457
  /**
380
458
  * An error class used to indicate that the command line arguments
@@ -386,8 +464,8 @@ var RunError = class extends Error {
386
464
  this.name = "RunError";
387
465
  }
388
466
  };
389
- function indentLines(text, indent) {
390
- return text.split("\n").join("\n" + " ".repeat(indent));
467
+ function indentLines(text$1, indent) {
468
+ return text$1.split("\n").join("\n" + " ".repeat(indent));
391
469
  }
392
470
 
393
471
  //#endregion
package/dist/index.cjs CHANGED
@@ -2,6 +2,7 @@ const require_message = require('./message.cjs');
2
2
  const require_constructs = require('./constructs.cjs');
3
3
  const require_usage = require('./usage.cjs');
4
4
  const require_doc = require('./doc.cjs');
5
+ const require_completion = require('./completion.cjs');
5
6
  const require_modifiers = require('./modifiers.cjs');
6
7
  const require_valueparser = require('./valueparser.cjs');
7
8
  const require_primitives = require('./primitives.cjs');
@@ -11,11 +12,13 @@ const require_facade = require('./facade.cjs');
11
12
  exports.RunError = require_facade.RunError;
12
13
  exports.WithDefaultError = require_modifiers.WithDefaultError;
13
14
  exports.argument = require_primitives.argument;
15
+ exports.bash = require_completion.bash;
14
16
  exports.choice = require_valueparser.choice;
15
17
  exports.command = require_primitives.command;
16
18
  exports.concat = require_constructs.concat;
17
19
  exports.constant = require_primitives.constant;
18
20
  exports.envVar = require_message.envVar;
21
+ exports.fish = require_completion.fish;
19
22
  exports.flag = require_primitives.flag;
20
23
  exports.float = require_valueparser.float;
21
24
  exports.formatDocPage = require_doc.formatDocPage;
@@ -41,12 +44,15 @@ exports.optionNames = require_message.optionNames;
41
44
  exports.optional = require_modifiers.optional;
42
45
  exports.or = require_constructs.or;
43
46
  exports.parse = require_parser.parse;
47
+ exports.pwsh = require_completion.pwsh;
44
48
  exports.run = require_facade.run;
45
49
  exports.string = require_valueparser.string;
50
+ exports.suggest = require_parser.suggest;
46
51
  exports.text = require_message.text;
47
52
  exports.tuple = require_constructs.tuple;
48
53
  exports.url = require_valueparser.url;
49
54
  exports.uuid = require_valueparser.uuid;
50
55
  exports.value = require_message.value;
51
56
  exports.values = require_message.values;
52
- exports.withDefault = require_modifiers.withDefault;
57
+ exports.withDefault = require_modifiers.withDefault;
58
+ exports.zsh = require_completion.zsh;
package/dist/index.d.cts CHANGED
@@ -4,7 +4,8 @@ import { DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, Doc
4
4
  import { ChoiceOptions, FloatOptions, IntegerOptionsBigInt, IntegerOptionsNumber, LocaleOptions, StringOptions, UrlOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, choice, float, integer, isValueParser, locale, string, url, uuid } from "./valueparser.cjs";
5
5
  import { MultipleErrorOptions, MultipleOptions, WithDefaultError, WithDefaultOptions, map, multiple, optional, withDefault } from "./modifiers.cjs";
6
6
  import { ArgumentErrorOptions, ArgumentOptions, CommandErrorOptions, CommandOptions, FlagErrorOptions, FlagOptions, OptionErrorOptions, OptionOptions, argument, command, constant, flag, option } from "./primitives.cjs";
7
- import { DocState, InferValue, Parser, ParserContext, ParserResult, Result, getDocPage, parse } from "./parser.cjs";
7
+ import { DocState, InferValue, Parser, ParserContext, ParserResult, Result, Suggestion, getDocPage, parse, suggest } from "./parser.cjs";
8
8
  import { LongestMatchErrorOptions, LongestMatchOptions, ObjectErrorOptions, ObjectOptions, OrErrorOptions, OrOptions, concat, group, longestMatch, merge, object, or, tuple } from "./constructs.cjs";
9
9
  import { RunError, RunOptions, run } from "./facade.cjs";
10
- export { ArgumentErrorOptions, ArgumentOptions, ChoiceOptions, CommandErrorOptions, CommandOptions, DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, DocState, FlagErrorOptions, FlagOptions, FloatOptions, InferValue, IntegerOptionsBigInt, IntegerOptionsNumber, LocaleOptions, LongestMatchErrorOptions, LongestMatchOptions, Message, MessageFormatOptions, MessageTerm, MultipleErrorOptions, MultipleOptions, ObjectErrorOptions, ObjectOptions, OptionErrorOptions, OptionName, OptionOptions, OrErrorOptions, OrOptions, Parser, ParserContext, ParserResult, Result, RunError, RunOptions, ShowDefaultOptions, StringOptions, UrlOptions, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, WithDefaultError, WithDefaultOptions, argument, choice, command, concat, constant, envVar, flag, float, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getDocPage, group, integer, isValueParser, locale, longestMatch, map, merge, message, metavar, multiple, normalizeUsage, object, option, optionName, optionNames, optional, or, parse, run, string, text, tuple, url, uuid, value, values, withDefault };
10
+ import { ShellCompletion, bash, fish, pwsh, zsh } from "./completion.cjs";
11
+ export { ArgumentErrorOptions, ArgumentOptions, ChoiceOptions, CommandErrorOptions, CommandOptions, DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, DocState, FlagErrorOptions, FlagOptions, FloatOptions, InferValue, IntegerOptionsBigInt, IntegerOptionsNumber, LocaleOptions, LongestMatchErrorOptions, LongestMatchOptions, Message, MessageFormatOptions, MessageTerm, MultipleErrorOptions, MultipleOptions, ObjectErrorOptions, ObjectOptions, OptionErrorOptions, OptionName, OptionOptions, OrErrorOptions, OrOptions, Parser, ParserContext, ParserResult, Result, RunError, RunOptions, ShellCompletion, ShowDefaultOptions, StringOptions, Suggestion, UrlOptions, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, WithDefaultError, WithDefaultOptions, argument, bash, choice, command, concat, constant, envVar, fish, flag, float, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getDocPage, group, integer, isValueParser, locale, longestMatch, map, merge, message, metavar, multiple, normalizeUsage, object, option, optionName, optionNames, optional, or, parse, pwsh, run, string, suggest, text, tuple, url, uuid, value, values, withDefault, zsh };
package/dist/index.d.ts CHANGED
@@ -4,7 +4,8 @@ import { DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, Doc
4
4
  import { ChoiceOptions, FloatOptions, IntegerOptionsBigInt, IntegerOptionsNumber, LocaleOptions, StringOptions, UrlOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, choice, float, integer, isValueParser, locale, string, url, uuid } from "./valueparser.js";
5
5
  import { MultipleErrorOptions, MultipleOptions, WithDefaultError, WithDefaultOptions, map, multiple, optional, withDefault } from "./modifiers.js";
6
6
  import { ArgumentErrorOptions, ArgumentOptions, CommandErrorOptions, CommandOptions, FlagErrorOptions, FlagOptions, OptionErrorOptions, OptionOptions, argument, command, constant, flag, option } from "./primitives.js";
7
- import { DocState, InferValue, Parser, ParserContext, ParserResult, Result, getDocPage, parse } from "./parser.js";
7
+ import { DocState, InferValue, Parser, ParserContext, ParserResult, Result, Suggestion, getDocPage, parse, suggest } from "./parser.js";
8
8
  import { LongestMatchErrorOptions, LongestMatchOptions, ObjectErrorOptions, ObjectOptions, OrErrorOptions, OrOptions, concat, group, longestMatch, merge, object, or, tuple } from "./constructs.js";
9
9
  import { RunError, RunOptions, run } from "./facade.js";
10
- export { ArgumentErrorOptions, ArgumentOptions, ChoiceOptions, CommandErrorOptions, CommandOptions, DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, DocState, FlagErrorOptions, FlagOptions, FloatOptions, InferValue, IntegerOptionsBigInt, IntegerOptionsNumber, LocaleOptions, LongestMatchErrorOptions, LongestMatchOptions, Message, MessageFormatOptions, MessageTerm, MultipleErrorOptions, MultipleOptions, ObjectErrorOptions, ObjectOptions, OptionErrorOptions, OptionName, OptionOptions, OrErrorOptions, OrOptions, Parser, ParserContext, ParserResult, Result, RunError, RunOptions, ShowDefaultOptions, StringOptions, UrlOptions, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, WithDefaultError, WithDefaultOptions, argument, choice, command, concat, constant, envVar, flag, float, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getDocPage, group, integer, isValueParser, locale, longestMatch, map, merge, message, metavar, multiple, normalizeUsage, object, option, optionName, optionNames, optional, or, parse, run, string, text, tuple, url, uuid, value, values, withDefault };
10
+ import { ShellCompletion, bash, fish, pwsh, zsh } from "./completion.js";
11
+ export { ArgumentErrorOptions, ArgumentOptions, ChoiceOptions, CommandErrorOptions, CommandOptions, DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, DocState, FlagErrorOptions, FlagOptions, FloatOptions, InferValue, IntegerOptionsBigInt, IntegerOptionsNumber, LocaleOptions, LongestMatchErrorOptions, LongestMatchOptions, Message, MessageFormatOptions, MessageTerm, MultipleErrorOptions, MultipleOptions, ObjectErrorOptions, ObjectOptions, OptionErrorOptions, OptionName, OptionOptions, OrErrorOptions, OrOptions, Parser, ParserContext, ParserResult, Result, RunError, RunOptions, ShellCompletion, ShowDefaultOptions, StringOptions, Suggestion, UrlOptions, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, WithDefaultError, WithDefaultOptions, argument, bash, choice, command, concat, constant, envVar, fish, flag, float, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getDocPage, group, integer, isValueParser, locale, longestMatch, map, merge, message, metavar, multiple, normalizeUsage, object, option, optionName, optionNames, optional, or, parse, pwsh, run, string, suggest, text, tuple, url, uuid, value, values, withDefault, zsh };
package/dist/index.js CHANGED
@@ -2,10 +2,11 @@ import { envVar, formatMessage, message, metavar, optionName, optionNames, text,
2
2
  import { concat, group, longestMatch, merge, object, or, tuple } from "./constructs.js";
3
3
  import { formatUsage, formatUsageTerm, normalizeUsage } from "./usage.js";
4
4
  import { formatDocPage } from "./doc.js";
5
+ import { bash, fish, pwsh, zsh } from "./completion.js";
5
6
  import { WithDefaultError, map, multiple, optional, withDefault } from "./modifiers.js";
6
7
  import { choice, float, integer, isValueParser, locale, string, url, uuid } from "./valueparser.js";
7
8
  import { argument, command, constant, flag, option } from "./primitives.js";
8
- import { getDocPage, parse } from "./parser.js";
9
+ import { getDocPage, parse, suggest } from "./parser.js";
9
10
  import { RunError, run } from "./facade.js";
10
11
 
11
- export { RunError, WithDefaultError, argument, choice, command, concat, constant, envVar, flag, float, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getDocPage, group, integer, isValueParser, locale, longestMatch, map, merge, message, metavar, multiple, normalizeUsage, object, option, optionName, optionNames, optional, or, parse, run, string, text, tuple, url, uuid, value, values, withDefault };
12
+ export { RunError, WithDefaultError, argument, bash, choice, command, concat, constant, envVar, fish, flag, float, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getDocPage, group, integer, isValueParser, locale, longestMatch, map, merge, message, metavar, multiple, normalizeUsage, object, option, optionName, optionNames, optional, or, parse, pwsh, run, string, suggest, text, tuple, url, uuid, value, values, withDefault, zsh };
@@ -44,6 +44,13 @@ function optional(parser) {
44
44
  };
45
45
  return parser.complete(state[0]);
46
46
  },
47
+ suggest(context, prefix) {
48
+ const innerState = typeof context.state === "undefined" ? parser.initialState : context.state[0];
49
+ return parser.suggest({
50
+ ...context,
51
+ state: innerState
52
+ }, prefix);
53
+ },
47
54
  getDocFragments(state, defaultValue) {
48
55
  const innerState = state.kind === "unavailable" ? { kind: "unavailable" } : state.state === void 0 ? { kind: "unavailable" } : {
49
56
  kind: "available",
@@ -127,6 +134,13 @@ function withDefault(parser, defaultValue, options) {
127
134
  }
128
135
  return parser.complete(state[0]);
129
136
  },
137
+ suggest(context, prefix) {
138
+ const innerState = typeof context.state === "undefined" ? parser.initialState : context.state[0];
139
+ return parser.suggest({
140
+ ...context,
141
+ state: innerState
142
+ }, prefix);
143
+ },
130
144
  getDocFragments(state, upperDefaultValue) {
131
145
  const innerState = state.kind === "unavailable" ? { kind: "unavailable" } : state.state === void 0 ? { kind: "unavailable" } : {
132
146
  kind: "available",
@@ -200,6 +214,9 @@ function map(parser, transform) {
200
214
  };
201
215
  return result;
202
216
  },
217
+ suggest(context, prefix) {
218
+ return parser.suggest(context, prefix);
219
+ },
203
220
  getDocFragments(state, _defaultValue) {
204
221
  return parser.getDocFragments(state, void 0);
205
222
  }
@@ -282,6 +299,13 @@ function multiple(parser, options = {}) {
282
299
  value: result
283
300
  };
284
301
  },
302
+ suggest(context, prefix) {
303
+ const innerState = context.state.length > 0 ? context.state.at(-1) : parser.initialState;
304
+ return parser.suggest({
305
+ ...context,
306
+ state: innerState
307
+ }, prefix);
308
+ },
285
309
  getDocFragments(state, defaultValue) {
286
310
  const innerState = state.kind === "unavailable" ? { kind: "unavailable" } : state.state.length > 0 ? {
287
311
  kind: "available",
package/dist/modifiers.js CHANGED
@@ -44,6 +44,13 @@ function optional(parser) {
44
44
  };
45
45
  return parser.complete(state[0]);
46
46
  },
47
+ suggest(context, prefix) {
48
+ const innerState = typeof context.state === "undefined" ? parser.initialState : context.state[0];
49
+ return parser.suggest({
50
+ ...context,
51
+ state: innerState
52
+ }, prefix);
53
+ },
47
54
  getDocFragments(state, defaultValue) {
48
55
  const innerState = state.kind === "unavailable" ? { kind: "unavailable" } : state.state === void 0 ? { kind: "unavailable" } : {
49
56
  kind: "available",
@@ -127,6 +134,13 @@ function withDefault(parser, defaultValue, options) {
127
134
  }
128
135
  return parser.complete(state[0]);
129
136
  },
137
+ suggest(context, prefix) {
138
+ const innerState = typeof context.state === "undefined" ? parser.initialState : context.state[0];
139
+ return parser.suggest({
140
+ ...context,
141
+ state: innerState
142
+ }, prefix);
143
+ },
130
144
  getDocFragments(state, upperDefaultValue) {
131
145
  const innerState = state.kind === "unavailable" ? { kind: "unavailable" } : state.state === void 0 ? { kind: "unavailable" } : {
132
146
  kind: "available",
@@ -200,6 +214,9 @@ function map(parser, transform) {
200
214
  };
201
215
  return result;
202
216
  },
217
+ suggest(context, prefix) {
218
+ return parser.suggest(context, prefix);
219
+ },
203
220
  getDocFragments(state, _defaultValue) {
204
221
  return parser.getDocFragments(state, void 0);
205
222
  }
@@ -282,6 +299,13 @@ function multiple(parser, options = {}) {
282
299
  value: result
283
300
  };
284
301
  },
302
+ suggest(context, prefix) {
303
+ const innerState = context.state.length > 0 ? context.state.at(-1) : parser.initialState;
304
+ return parser.suggest({
305
+ ...context,
306
+ state: innerState
307
+ }, prefix);
308
+ },
285
309
  getDocFragments(state, defaultValue) {
286
310
  const innerState = state.kind === "unavailable" ? { kind: "unavailable" } : state.state.length > 0 ? {
287
311
  kind: "available",
package/dist/parser.cjs CHANGED
@@ -48,6 +48,51 @@ function parse(parser, args) {
48
48
  };
49
49
  }
50
50
  /**
51
+ * Generates command-line suggestions based on current parsing state.
52
+ * This function processes the input arguments up to the last argument,
53
+ * then calls the parser's suggest method with the remaining prefix.
54
+ * @template T The type of the value produced by the parser.
55
+ * @param parser The {@link Parser} to use for generating suggestions.
56
+ * @param args The array of command-line arguments including the partial
57
+ * argument to complete. The last element is treated as
58
+ * the prefix for suggestions.
59
+ * @returns An array of {@link Suggestion} objects containing completion
60
+ * candidates.
61
+ * @example
62
+ * ```typescript
63
+ * const parser = object({
64
+ * verbose: option("-v", "--verbose"),
65
+ * format: option("-f", "--format", choice(["json", "yaml"]))
66
+ * });
67
+ *
68
+ * // Get suggestions for options starting with "--"
69
+ * const suggestions = suggest(parser, ["--"]);
70
+ * // Returns: [{ text: "--verbose" }, { text: "--format" }]
71
+ *
72
+ * // Get suggestions after parsing some arguments
73
+ * const suggestions2 = suggest(parser, ["-v", "--format="]);
74
+ * // Returns: [{ text: "--format=json" }, { text: "--format=yaml" }]
75
+ * ```
76
+ * @since 0.6.0
77
+ */
78
+ function suggest(parser, args) {
79
+ const allButLast = args.slice(0, -1);
80
+ const prefix = args[args.length - 1];
81
+ let context = {
82
+ buffer: allButLast,
83
+ optionsTerminated: false,
84
+ state: parser.initialState
85
+ };
86
+ while (context.buffer.length > 0) {
87
+ const result = parser.parse(context);
88
+ if (!result.success) return Array.from(parser.suggest(context, prefix));
89
+ const previousBuffer = context.buffer;
90
+ context = result.next;
91
+ if (context.buffer.length > 0 && context.buffer.length === previousBuffer.length && context.buffer[0] === previousBuffer[0]) return [];
92
+ }
93
+ return Array.from(parser.suggest(context, prefix));
94
+ }
95
+ /**
51
96
  * Generates a documentation page for a parser based on its current state after
52
97
  * attempting to parse the provided arguments. This function is useful for
53
98
  * creating help documentation that reflects the current parsing context.
@@ -143,5 +188,6 @@ exports.option = require_primitives.option;
143
188
  exports.optional = require_modifiers.optional;
144
189
  exports.or = require_constructs.or;
145
190
  exports.parse = parse;
191
+ exports.suggest = suggest;
146
192
  exports.tuple = require_constructs.tuple;
147
193
  exports.withDefault = require_modifiers.withDefault;
package/dist/parser.d.cts CHANGED
@@ -78,6 +78,19 @@ interface Parser<TValue, TState> {
78
78
  * it should return an error message.
79
79
  */
80
80
  complete(state: TState): ValueParserResult<TValue>;
81
+ /**
82
+ * Generates next-step suggestions based on the current context
83
+ * and an optional prefix. This can be used to provide shell completion
84
+ * suggestions or to guide users in constructing valid commands.
85
+ * @param context The context of the parser, which includes the input buffer
86
+ * and the current state.
87
+ * @param prefix A prefix string that can be used to filter suggestions.
88
+ * Can be an empty string if no prefix is provided.
89
+ * @returns An iterable of {@link Suggestion} objects, each containing
90
+ * a suggestion text and an optional description.
91
+ * @since 0.6.0
92
+ */
93
+ suggest(context: ParserContext<TState>, prefix: string): Iterable<Suggestion>;
81
94
  /**
82
95
  * Generates a documentation fragment for this parser, which can be used
83
96
  * to describe the parser's usage, description, and default value.
@@ -113,6 +126,51 @@ interface ParserContext<TState> {
113
126
  */
114
127
  readonly optionsTerminated: boolean;
115
128
  }
129
+ /**
130
+ * Represents a suggestion for command-line completion or guidance.
131
+ * @since 0.6.0
132
+ */
133
+ type Suggestion = {
134
+ /**
135
+ * A literal text suggestion.
136
+ */
137
+ readonly kind: "literal";
138
+ /**
139
+ * The suggestion text that can be used for completion or guidance.
140
+ */
141
+ readonly text: string;
142
+ /**
143
+ * An optional description providing additional context
144
+ * or information about the suggestion.
145
+ */
146
+ readonly description?: Message;
147
+ } | {
148
+ /**
149
+ * A file system completion suggestion that uses native shell completion.
150
+ */
151
+ readonly kind: "file";
152
+ /**
153
+ * The current prefix/pattern for fallback when native completion is unavailable.
154
+ */
155
+ readonly pattern?: string;
156
+ /**
157
+ * The type of file system entries to complete.
158
+ */
159
+ readonly type: "file" | "directory" | "any";
160
+ /**
161
+ * File extensions to filter by (e.g., [".ts", ".js"]).
162
+ */
163
+ readonly extensions?: readonly string[];
164
+ /**
165
+ * Whether to include hidden files (those starting with a dot).
166
+ */
167
+ readonly includeHidden?: boolean;
168
+ /**
169
+ * An optional description providing additional context
170
+ * or information about the suggestion.
171
+ */
172
+ readonly description?: Message;
173
+ };
116
174
  /**
117
175
  * A discriminated union type representing the result of a parser operation.
118
176
  * It can either indicate a successful parse with the next state and context,
@@ -193,6 +251,35 @@ type Result<T> = {
193
251
  * failure.
194
252
  */
195
253
  declare function parse<T>(parser: Parser<T, unknown>, args: readonly string[]): Result<T>;
254
+ /**
255
+ * Generates command-line suggestions based on current parsing state.
256
+ * This function processes the input arguments up to the last argument,
257
+ * then calls the parser's suggest method with the remaining prefix.
258
+ * @template T The type of the value produced by the parser.
259
+ * @param parser The {@link Parser} to use for generating suggestions.
260
+ * @param args The array of command-line arguments including the partial
261
+ * argument to complete. The last element is treated as
262
+ * the prefix for suggestions.
263
+ * @returns An array of {@link Suggestion} objects containing completion
264
+ * candidates.
265
+ * @example
266
+ * ```typescript
267
+ * const parser = object({
268
+ * verbose: option("-v", "--verbose"),
269
+ * format: option("-f", "--format", choice(["json", "yaml"]))
270
+ * });
271
+ *
272
+ * // Get suggestions for options starting with "--"
273
+ * const suggestions = suggest(parser, ["--"]);
274
+ * // Returns: [{ text: "--verbose" }, { text: "--format" }]
275
+ *
276
+ * // Get suggestions after parsing some arguments
277
+ * const suggestions2 = suggest(parser, ["-v", "--format="]);
278
+ * // Returns: [{ text: "--format=json" }, { text: "--format=yaml" }]
279
+ * ```
280
+ * @since 0.6.0
281
+ */
282
+ declare function suggest<T>(parser: Parser<T, unknown>, args: readonly [string, ...readonly string[]]): readonly Suggestion[];
196
283
  /**
197
284
  * Generates a documentation page for a parser based on its current state after
198
285
  * attempting to parse the provided arguments. This function is useful for
@@ -228,4 +315,4 @@ declare function parse<T>(parser: Parser<T, unknown>, args: readonly string[]):
228
315
  */
229
316
  declare function getDocPage(parser: Parser<unknown, unknown>, args?: readonly string[]): DocPage | undefined;
230
317
  //#endregion
231
- export { ArgumentErrorOptions, ArgumentOptions, CommandErrorOptions, CommandOptions, DocState, FlagErrorOptions, FlagOptions, InferValue, LongestMatchErrorOptions, LongestMatchOptions, MultipleErrorOptions, MultipleOptions, ObjectErrorOptions, ObjectOptions, OptionErrorOptions, OptionOptions, OrErrorOptions, OrOptions, Parser, ParserContext, ParserResult, Result, WithDefaultError, WithDefaultOptions, argument, command, concat, constant, flag, getDocPage, group, longestMatch, map, merge, multiple, object, option, optional, or, parse, tuple, withDefault };
318
+ export { ArgumentErrorOptions, ArgumentOptions, CommandErrorOptions, CommandOptions, DocState, FlagErrorOptions, FlagOptions, InferValue, LongestMatchErrorOptions, LongestMatchOptions, MultipleErrorOptions, MultipleOptions, ObjectErrorOptions, ObjectOptions, OptionErrorOptions, OptionOptions, OrErrorOptions, OrOptions, Parser, ParserContext, ParserResult, Result, Suggestion, WithDefaultError, WithDefaultOptions, argument, command, concat, constant, flag, getDocPage, group, longestMatch, map, merge, multiple, object, option, optional, or, parse, suggest, tuple, withDefault };