@optique/core 0.3.0-dev.39 → 0.3.0-dev.41

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.cjs CHANGED
@@ -38,22 +38,48 @@ function run(parser, programName, args, options = {}) {
38
38
  let { colors, maxWidth, help = "none", onHelp = () => {}, aboveError = "usage", onError = () => {
39
39
  throw new RunError("Failed to parse command line arguments.");
40
40
  }, stderr = console.error, stdout = console.log } = options;
41
+ const contextualHelpParser = require_parser.object({
42
+ help: require_parser.constant(true),
43
+ commands: require_parser.multiple(require_parser.argument(require_valueparser.string({ metavar: "COMMAND" }))),
44
+ __help: require_parser.flag("--help")
45
+ });
41
46
  const helpCommand = require_parser.command("help", require_parser.multiple(require_parser.argument(require_valueparser.string({ metavar: "COMMAND" }))), { description: require_message.message`Show help information.` });
42
47
  const helpOption = require_parser.option("--help", { description: require_message.message`Show help information.` });
43
- const augmentedParser = help === "none" ? require_parser.object({
48
+ const augmentedParser = help === "none" ? parser : help === "command" ? require_parser.longestMatch(require_parser.object({
49
+ help: require_parser.constant(true),
50
+ commands: helpCommand
51
+ }), require_parser.object({
44
52
  help: require_parser.constant(false),
45
53
  result: parser
46
- }) : require_parser.or(require_parser.object({
54
+ })) : help === "option" ? require_parser.longestMatch(require_parser.object({
55
+ help: require_parser.constant(false),
56
+ result: parser
57
+ }), contextualHelpParser, require_parser.merge(require_parser.object({
58
+ help: require_parser.constant(true),
59
+ commands: require_parser.constant([])
60
+ }), helpOption)) : require_parser.longestMatch(require_parser.object({
47
61
  help: require_parser.constant(false),
48
62
  result: parser
49
63
  }), require_parser.object({
50
64
  help: require_parser.constant(true),
51
- command: help === "both" ? require_parser.or(helpCommand, helpOption) : help === "command" ? helpCommand : helpOption
52
- }));
65
+ commands: helpCommand
66
+ }), contextualHelpParser, require_parser.merge(require_parser.object({
67
+ help: require_parser.constant(true),
68
+ commands: require_parser.constant([])
69
+ }), helpOption));
53
70
  const result = require_parser.parse(augmentedParser, args);
54
71
  if (result.success) {
55
- if (!result.value.help) return result.value.result;
56
- const doc = require_parser.getDocPage(typeof result.value.command === "boolean" || result.value.command.length < 1 ? augmentedParser : parser, typeof result.value.command === "boolean" ? [] : result.value.command);
72
+ const value = result.value;
73
+ if (help === "none") return value;
74
+ if (typeof value === "object" && value != null && "help" in value) {
75
+ const helpValue$1 = value;
76
+ if (!helpValue$1.help) return helpValue$1.result;
77
+ } else return value;
78
+ let commandContext = [];
79
+ const helpValue = value;
80
+ if (Array.isArray(helpValue.commands)) commandContext = helpValue.commands;
81
+ else if (typeof helpValue.commands === "object" && helpValue.commands != null && "length" in helpValue.commands) commandContext = helpValue.commands;
82
+ const doc = require_parser.getDocPage(commandContext.length < 1 ? augmentedParser : parser, commandContext);
57
83
  if (doc != null) stdout(require_doc.formatDocPage(programName, doc, {
58
84
  colors,
59
85
  maxWidth
package/dist/facade.js CHANGED
@@ -2,7 +2,7 @@ import { formatMessage, message } from "./message.js";
2
2
  import { formatUsage } from "./usage.js";
3
3
  import { formatDocPage } from "./doc.js";
4
4
  import { string } from "./valueparser.js";
5
- import { argument, command, constant, getDocPage, multiple, object, option, or, parse } from "./parser.js";
5
+ import { argument, command, constant, flag, getDocPage, longestMatch, merge, multiple, object, option, parse } from "./parser.js";
6
6
 
7
7
  //#region src/facade.ts
8
8
  /**
@@ -38,22 +38,48 @@ function run(parser, programName, args, options = {}) {
38
38
  let { colors, maxWidth, help = "none", onHelp = () => {}, aboveError = "usage", onError = () => {
39
39
  throw new RunError("Failed to parse command line arguments.");
40
40
  }, stderr = console.error, stdout = console.log } = options;
41
+ const contextualHelpParser = object({
42
+ help: constant(true),
43
+ commands: multiple(argument(string({ metavar: "COMMAND" }))),
44
+ __help: flag("--help")
45
+ });
41
46
  const helpCommand = command("help", multiple(argument(string({ metavar: "COMMAND" }))), { description: message`Show help information.` });
42
47
  const helpOption = option("--help", { description: message`Show help information.` });
43
- const augmentedParser = help === "none" ? object({
48
+ const augmentedParser = help === "none" ? parser : help === "command" ? longestMatch(object({
49
+ help: constant(true),
50
+ commands: helpCommand
51
+ }), object({
44
52
  help: constant(false),
45
53
  result: parser
46
- }) : or(object({
54
+ })) : help === "option" ? longestMatch(object({
55
+ help: constant(false),
56
+ result: parser
57
+ }), contextualHelpParser, merge(object({
58
+ help: constant(true),
59
+ commands: constant([])
60
+ }), helpOption)) : longestMatch(object({
47
61
  help: constant(false),
48
62
  result: parser
49
63
  }), object({
50
64
  help: constant(true),
51
- command: help === "both" ? or(helpCommand, helpOption) : help === "command" ? helpCommand : helpOption
52
- }));
65
+ commands: helpCommand
66
+ }), contextualHelpParser, merge(object({
67
+ help: constant(true),
68
+ commands: constant([])
69
+ }), helpOption));
53
70
  const result = parse(augmentedParser, args);
54
71
  if (result.success) {
55
- if (!result.value.help) return result.value.result;
56
- const doc = getDocPage(typeof result.value.command === "boolean" || result.value.command.length < 1 ? augmentedParser : parser, typeof result.value.command === "boolean" ? [] : result.value.command);
72
+ const value = result.value;
73
+ if (help === "none") return value;
74
+ if (typeof value === "object" && value != null && "help" in value) {
75
+ const helpValue$1 = value;
76
+ if (!helpValue$1.help) return helpValue$1.result;
77
+ } else return value;
78
+ let commandContext = [];
79
+ const helpValue = value;
80
+ if (Array.isArray(helpValue.commands)) commandContext = helpValue.commands;
81
+ else if (typeof helpValue.commands === "object" && helpValue.commands != null && "length" in helpValue.commands) commandContext = helpValue.commands;
82
+ const doc = getDocPage(commandContext.length < 1 ? augmentedParser : parser, commandContext);
57
83
  if (doc != null) stdout(formatDocPage(programName, doc, {
58
84
  colors,
59
85
  maxWidth
package/dist/index.cjs CHANGED
@@ -21,6 +21,7 @@ exports.getDocPage = require_parser.getDocPage;
21
21
  exports.integer = require_valueparser.integer;
22
22
  exports.isValueParser = require_valueparser.isValueParser;
23
23
  exports.locale = require_valueparser.locale;
24
+ exports.longestMatch = require_parser.longestMatch;
24
25
  exports.map = require_parser.map;
25
26
  exports.merge = require_parser.merge;
26
27
  exports.message = require_message.message;
package/dist/index.d.cts CHANGED
@@ -2,6 +2,6 @@ import { Message, MessageFormatOptions, MessageTerm, formatMessage, message, met
2
2
  import { OptionName, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, formatUsage, formatUsageTerm, normalizeUsage } from "./usage.cjs";
3
3
  import { DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, formatDocPage } from "./doc.cjs";
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
- import { ArgumentOptions, CommandOptions, FlagOptions, InferValue, MultipleOptions, OptionOptions, Parser, ParserContext, ParserResult, Result, argument, command, concat, constant, flag, getDocPage, map, merge, multiple, object, option, optional, or, parse, tuple, withDefault } from "./parser.cjs";
5
+ import { ArgumentOptions, CommandOptions, DocState, FlagOptions, InferValue, MultipleOptions, OptionOptions, Parser, ParserContext, ParserResult, Result, argument, command, concat, constant, flag, getDocPage, longestMatch, map, merge, multiple, object, option, optional, or, parse, tuple, withDefault } from "./parser.cjs";
6
6
  import { RunError, RunOptions, run } from "./facade.cjs";
7
- export { ArgumentOptions, ChoiceOptions, CommandOptions, DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, FlagOptions, FloatOptions, InferValue, IntegerOptionsBigInt, IntegerOptionsNumber, LocaleOptions, Message, MessageFormatOptions, MessageTerm, MultipleOptions, OptionName, OptionOptions, Parser, ParserContext, ParserResult, Result, RunError, RunOptions, StringOptions, UrlOptions, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, argument, choice, command, concat, constant, flag, float, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getDocPage, integer, isValueParser, locale, map, merge, message, metavar, multiple, normalizeUsage, object, option, optionName, optionNames, optional, or, parse, run, string, text, tuple, url, uuid, value, values, withDefault };
7
+ export { ArgumentOptions, ChoiceOptions, CommandOptions, DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, DocState, FlagOptions, FloatOptions, InferValue, IntegerOptionsBigInt, IntegerOptionsNumber, LocaleOptions, Message, MessageFormatOptions, MessageTerm, MultipleOptions, OptionName, OptionOptions, Parser, ParserContext, ParserResult, Result, RunError, RunOptions, StringOptions, UrlOptions, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, argument, choice, command, concat, constant, flag, float, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getDocPage, 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 };
package/dist/index.d.ts CHANGED
@@ -2,6 +2,6 @@ import { Message, MessageFormatOptions, MessageTerm, formatMessage, message, met
2
2
  import { OptionName, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, formatUsage, formatUsageTerm, normalizeUsage } from "./usage.js";
3
3
  import { DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, formatDocPage } from "./doc.js";
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
- import { ArgumentOptions, CommandOptions, FlagOptions, InferValue, MultipleOptions, OptionOptions, Parser, ParserContext, ParserResult, Result, argument, command, concat, constant, flag, getDocPage, map, merge, multiple, object, option, optional, or, parse, tuple, withDefault } from "./parser.js";
5
+ import { ArgumentOptions, CommandOptions, DocState, FlagOptions, InferValue, MultipleOptions, OptionOptions, Parser, ParserContext, ParserResult, Result, argument, command, concat, constant, flag, getDocPage, longestMatch, map, merge, multiple, object, option, optional, or, parse, tuple, withDefault } from "./parser.js";
6
6
  import { RunError, RunOptions, run } from "./facade.js";
7
- export { ArgumentOptions, ChoiceOptions, CommandOptions, DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, FlagOptions, FloatOptions, InferValue, IntegerOptionsBigInt, IntegerOptionsNumber, LocaleOptions, Message, MessageFormatOptions, MessageTerm, MultipleOptions, OptionName, OptionOptions, Parser, ParserContext, ParserResult, Result, RunError, RunOptions, StringOptions, UrlOptions, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, argument, choice, command, concat, constant, flag, float, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getDocPage, integer, isValueParser, locale, map, merge, message, metavar, multiple, normalizeUsage, object, option, optionName, optionNames, optional, or, parse, run, string, text, tuple, url, uuid, value, values, withDefault };
7
+ export { ArgumentOptions, ChoiceOptions, CommandOptions, DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, DocState, FlagOptions, FloatOptions, InferValue, IntegerOptionsBigInt, IntegerOptionsNumber, LocaleOptions, Message, MessageFormatOptions, MessageTerm, MultipleOptions, OptionName, OptionOptions, Parser, ParserContext, ParserResult, Result, RunError, RunOptions, StringOptions, UrlOptions, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, argument, choice, command, concat, constant, flag, float, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getDocPage, 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 };
package/dist/index.js CHANGED
@@ -2,7 +2,7 @@ import { formatMessage, message, metavar, optionName, optionNames, text, value,
2
2
  import { formatUsage, formatUsageTerm, normalizeUsage } from "./usage.js";
3
3
  import { formatDocPage } from "./doc.js";
4
4
  import { choice, float, integer, isValueParser, locale, string, url, uuid } from "./valueparser.js";
5
- import { argument, command, concat, constant, flag, getDocPage, map, merge, multiple, object, option, optional, or, parse, tuple, withDefault } from "./parser.js";
5
+ import { argument, command, concat, constant, flag, getDocPage, longestMatch, map, merge, multiple, object, option, optional, or, parse, tuple, withDefault } from "./parser.js";
6
6
  import { RunError, run } from "./facade.js";
7
7
 
8
- export { RunError, argument, choice, command, concat, constant, flag, float, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getDocPage, integer, isValueParser, locale, map, merge, message, metavar, multiple, normalizeUsage, object, option, optionName, optionNames, optional, or, parse, run, string, text, tuple, url, uuid, value, values, withDefault };
8
+ export { RunError, argument, choice, command, concat, constant, flag, float, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getDocPage, 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 };
package/dist/parser.cjs CHANGED
@@ -514,7 +514,11 @@ function optional(parser) {
514
514
  return parser.complete(state[0]);
515
515
  },
516
516
  getDocFragments(state, defaultValue) {
517
- return parser.getDocFragments(typeof state === "undefined" ? parser.initialState : state[0], defaultValue);
517
+ const innerState = state.kind === "unavailable" ? { kind: "unavailable" } : state.state === void 0 ? { kind: "unavailable" } : {
518
+ kind: "available",
519
+ state: state.state[0]
520
+ };
521
+ return parser.getDocFragments(innerState, defaultValue);
518
522
  }
519
523
  };
520
524
  }
@@ -566,7 +570,11 @@ function withDefault(parser, defaultValue) {
566
570
  return parser.complete(state[0]);
567
571
  },
568
572
  getDocFragments(state, upperDefaultValue) {
569
- return parser.getDocFragments(typeof state === "undefined" ? parser.initialState : state[0], upperDefaultValue != null ? upperDefaultValue : typeof defaultValue === "function" ? defaultValue() : defaultValue);
573
+ const innerState = state.kind === "unavailable" ? { kind: "unavailable" } : state.state === void 0 ? { kind: "unavailable" } : {
574
+ kind: "available",
575
+ state: state.state[0]
576
+ };
577
+ return parser.getDocFragments(innerState, upperDefaultValue != null ? upperDefaultValue : typeof defaultValue === "function" ? defaultValue() : defaultValue);
570
578
  }
571
579
  };
572
580
  }
@@ -697,7 +705,11 @@ function multiple(parser, options = {}) {
697
705
  };
698
706
  },
699
707
  getDocFragments(state, defaultValue) {
700
- return parser.getDocFragments(state.at(-1) ?? parser.initialState, defaultValue != null && defaultValue.length > 0 ? defaultValue[0] : void 0);
708
+ const innerState = state.kind === "unavailable" ? { kind: "unavailable" } : state.state.length > 0 ? {
709
+ kind: "available",
710
+ state: state.state.at(-1)
711
+ } : { kind: "unavailable" };
712
+ return parser.getDocFragments(innerState, defaultValue != null && defaultValue.length > 0 ? defaultValue[0] : void 0);
701
713
  }
702
714
  };
703
715
  }
@@ -788,7 +800,13 @@ function object(labelOrParsers, maybeParsers) {
788
800
  };
789
801
  },
790
802
  getDocFragments(state, defaultValue) {
791
- const fragments = parserPairs.flatMap(([field, p]) => p.getDocFragments(state[field], defaultValue?.[field]).fragments);
803
+ const fragments = parserPairs.flatMap(([field, p]) => {
804
+ const fieldState = state.kind === "unavailable" ? { kind: "unavailable" } : {
805
+ kind: "available",
806
+ state: state.state[field]
807
+ };
808
+ return p.getDocFragments(fieldState, defaultValue?.[field]).fragments;
809
+ });
792
810
  const entries = fragments.filter((d) => d.type === "entry");
793
811
  const sections = [];
794
812
  for (const fragment of fragments) {
@@ -892,7 +910,13 @@ function tuple(labelOrParsers, maybeParsers) {
892
910
  };
893
911
  },
894
912
  getDocFragments(state, defaultValue) {
895
- const fragments = parsers.flatMap((p, i) => p.getDocFragments(state[i], defaultValue?.[i]).fragments);
913
+ const fragments = parsers.flatMap((p, i) => {
914
+ const indexState = state.kind === "unavailable" ? { kind: "unavailable" } : {
915
+ kind: "available",
916
+ state: state.state[i]
917
+ };
918
+ return p.getDocFragments(indexState, defaultValue?.[i]).fragments;
919
+ });
896
920
  const entries = fragments.filter((d) => d.type === "entry");
897
921
  const sections = [];
898
922
  for (const fragment of fragments) {
@@ -976,10 +1000,14 @@ function or(...parsers) {
976
1000
  getDocFragments(state, _defaultValue) {
977
1001
  let description;
978
1002
  let fragments;
979
- if (state == null) fragments = parsers.flatMap((p) => p.getDocFragments(p.initialState, void 0).fragments);
1003
+ if (state.kind === "unavailable" || state.state == null) fragments = parsers.flatMap((p) => p.getDocFragments({ kind: "unavailable" }, void 0).fragments);
980
1004
  else {
981
- const [index, parserResult] = state;
982
- const docFragments = parsers[index].getDocFragments(parserResult.success ? parserResult.next.state : parsers[index].initialState, void 0);
1005
+ const [index, parserResult] = state.state;
1006
+ const innerState = parserResult.success ? {
1007
+ kind: "available",
1008
+ state: parserResult.next.state
1009
+ } : { kind: "unavailable" };
1010
+ const docFragments = parsers[index].getDocFragments(innerState, void 0);
983
1011
  description = docFragments.description;
984
1012
  fragments = docFragments.fragments;
985
1013
  }
@@ -1003,6 +1031,86 @@ function or(...parsers) {
1003
1031
  }
1004
1032
  };
1005
1033
  }
1034
+ function longestMatch(...parsers) {
1035
+ return {
1036
+ $valueType: [],
1037
+ $stateType: [],
1038
+ priority: Math.max(...parsers.map((p) => p.priority)),
1039
+ usage: [{
1040
+ type: "exclusive",
1041
+ terms: parsers.map((p) => p.usage)
1042
+ }],
1043
+ initialState: void 0,
1044
+ complete(state) {
1045
+ if (state == null) return {
1046
+ success: false,
1047
+ error: require_message.message`No parser matched.`
1048
+ };
1049
+ const [i, result] = state;
1050
+ if (result.success) return parsers[i].complete(result.next.state);
1051
+ return {
1052
+ success: false,
1053
+ error: result.error
1054
+ };
1055
+ },
1056
+ parse(context) {
1057
+ let bestMatch = null;
1058
+ let error = {
1059
+ consumed: 0,
1060
+ error: context.buffer.length < 1 ? require_message.message`No parser matched.` : require_message.message`Unexpected option or subcommand: ${require_message.optionName(context.buffer[0])}.`
1061
+ };
1062
+ for (let i = 0; i < parsers.length; i++) {
1063
+ const parser = parsers[i];
1064
+ const result = parser.parse({
1065
+ ...context,
1066
+ state: context.state == null || context.state[0] !== i || !context.state[1].success ? parser.initialState : context.state[1].next.state
1067
+ });
1068
+ if (result.success) {
1069
+ const consumed = context.buffer.length - result.next.buffer.length;
1070
+ if (bestMatch === null || consumed > bestMatch.consumed) bestMatch = {
1071
+ index: i,
1072
+ result,
1073
+ consumed
1074
+ };
1075
+ } else if (error.consumed < result.consumed) error = result;
1076
+ }
1077
+ if (bestMatch && bestMatch.result.success) return {
1078
+ success: true,
1079
+ next: {
1080
+ ...context,
1081
+ buffer: bestMatch.result.next.buffer,
1082
+ optionsTerminated: bestMatch.result.next.optionsTerminated,
1083
+ state: [bestMatch.index, bestMatch.result]
1084
+ },
1085
+ consumed: bestMatch.result.consumed
1086
+ };
1087
+ return {
1088
+ ...error,
1089
+ success: false
1090
+ };
1091
+ },
1092
+ getDocFragments(state, _defaultValue) {
1093
+ let description;
1094
+ let fragments;
1095
+ if (state.kind === "unavailable" || state.state == null) fragments = parsers.flatMap((p) => p.getDocFragments({ kind: "unavailable" }).fragments);
1096
+ else {
1097
+ const [i, result] = state.state;
1098
+ if (result.success) {
1099
+ const docResult = parsers[i].getDocFragments({
1100
+ kind: "available",
1101
+ state: result.next.state
1102
+ });
1103
+ description = docResult.description;
1104
+ fragments = docResult.fragments;
1105
+ } else fragments = parsers.flatMap((p) => p.getDocFragments({ kind: "unavailable" }).fragments);
1106
+ }
1107
+ return {
1108
+ description,
1109
+ fragments
1110
+ };
1111
+ }
1112
+ };
1113
+ }
1006
1114
  function merge(...parsers) {
1007
1115
  parsers = parsers.toSorted((a, b) => b.priority - a.priority);
1008
1116
  const initialState = {};
@@ -1082,7 +1190,13 @@ function merge(...parsers) {
1082
1190
  };
1083
1191
  },
1084
1192
  getDocFragments(state, _defaultValue) {
1085
- const fragments = parsers.flatMap((p) => p.getDocFragments(state, void 0).fragments);
1193
+ const fragments = parsers.flatMap((p) => {
1194
+ const parserState = p.initialState === void 0 ? { kind: "unavailable" } : state.kind === "unavailable" ? { kind: "unavailable" } : {
1195
+ kind: "available",
1196
+ state: state.state
1197
+ };
1198
+ return p.getDocFragments(parserState, void 0).fragments;
1199
+ });
1086
1200
  const entries = fragments.filter((f) => f.type === "entry");
1087
1201
  const sections = [];
1088
1202
  for (const fragment of fragments) {
@@ -1183,7 +1297,13 @@ function concat(...parsers) {
1183
1297
  };
1184
1298
  },
1185
1299
  getDocFragments(state, _defaultValue) {
1186
- const fragments = parsers.flatMap((p, index) => p.getDocFragments(state[index], void 0).fragments);
1300
+ const fragments = parsers.flatMap((p, index) => {
1301
+ const indexState = state.kind === "unavailable" ? { kind: "unavailable" } : {
1302
+ kind: "available",
1303
+ state: state.state[index]
1304
+ };
1305
+ return p.getDocFragments(indexState, void 0).fragments;
1306
+ });
1187
1307
  const entries = fragments.filter((f) => f.type === "entry");
1188
1308
  const sections = [];
1189
1309
  for (const fragment of fragments) {
@@ -1290,7 +1410,7 @@ function command(name, parser, options = {}) {
1290
1410
  };
1291
1411
  },
1292
1412
  getDocFragments(state, defaultValue) {
1293
- if (typeof state === "undefined") return {
1413
+ if (state.kind === "unavailable" || typeof state.state === "undefined") return {
1294
1414
  description: options.description,
1295
1415
  fragments: [{
1296
1416
  type: "entry",
@@ -1301,7 +1421,11 @@ function command(name, parser, options = {}) {
1301
1421
  description: options.description
1302
1422
  }]
1303
1423
  };
1304
- const innerFragments = parser.getDocFragments(state[0] === "parsing" ? state[1] : parser.initialState, defaultValue);
1424
+ const innerState = state.state[0] === "parsing" ? {
1425
+ kind: "available",
1426
+ state: state.state[1]
1427
+ } : { kind: "unavailable" };
1428
+ const innerFragments = parser.getDocFragments(innerState, defaultValue);
1305
1429
  return {
1306
1430
  ...innerFragments,
1307
1431
  description: innerFragments.description ?? options.description
@@ -1398,7 +1522,10 @@ function getDocPage(parser, args = []) {
1398
1522
  if (!result.success) break;
1399
1523
  context = result.next;
1400
1524
  } while (context.buffer.length > 0);
1401
- const { description, fragments } = parser.getDocFragments(context.state, void 0);
1525
+ const { description, fragments } = parser.getDocFragments({
1526
+ kind: "available",
1527
+ state: context.state
1528
+ }, void 0);
1402
1529
  const entries = fragments.filter((f) => f.type === "entry");
1403
1530
  const sections = [];
1404
1531
  for (const fragment of fragments) {
@@ -1436,6 +1563,7 @@ exports.concat = concat;
1436
1563
  exports.constant = constant;
1437
1564
  exports.flag = flag;
1438
1565
  exports.getDocPage = getDocPage;
1566
+ exports.longestMatch = longestMatch;
1439
1567
  exports.map = map;
1440
1568
  exports.merge = merge;
1441
1569
  exports.multiple = multiple;
package/dist/parser.d.cts CHANGED
@@ -5,6 +5,19 @@ import { ValueParser, ValueParserResult } from "./valueparser.cjs";
5
5
 
6
6
  //#region src/parser.d.ts
7
7
 
8
+ /**
9
+ * Represents the state passed to getDocFragments.
10
+ * Can be either the actual parser state or an explicit indicator
11
+ * that no state is available.
12
+ * @template TState The type of the actual state when available.
13
+ * @since 0.3.0
14
+ */
15
+ type DocState<TState> = {
16
+ readonly kind: "available";
17
+ readonly state: TState;
18
+ } | {
19
+ readonly kind: "unavailable";
20
+ };
8
21
  /**
9
22
  * Parser interface for command-line argument parsing.
10
23
  * @template TValue The type of the value returned by the parser.
@@ -65,15 +78,14 @@ interface Parser<TValue, TState> {
65
78
  /**
66
79
  * Generates a documentation fragment for this parser, which can be used
67
80
  * to describe the parser's usage, description, and default value.
68
- * @param state The current state of the parser, which may contain
69
- * accumulated data or context needed to produce
70
- * the documentation.
81
+ * @param state The current state of the parser, wrapped in a DocState
82
+ * to indicate whether the actual state is available or not.
71
83
  * @param defaultValue An optional default value that can be used
72
84
  * to provide a default value in the documentation.
73
85
  * @returns {@link DocFragments} object containing documentation
74
86
  * fragments for this parser.
75
87
  */
76
- getDocFragments(state: TState, defaultValue?: TValue): DocFragments;
88
+ getDocFragments(state: DocState<TState>, defaultValue?: TValue): DocFragments;
77
89
  }
78
90
  /**
79
91
  * The context of the parser, which includes the input buffer and the state.
@@ -636,6 +648,84 @@ declare function or<TA, TB, TC, TD, TE, TF, TG, TH, TI, TStateA, TStateB, TState
636
648
  * in order, returning the result of the first successful parser.
637
649
  */
638
650
  declare function or<TA, TB, TC, TD, TE, TF, TG, TH, TI, TJ, TStateA, TStateB, TStateC, TStateD, TStateE, TStateF, TStateG, TStateH, TStateI, TStateJ>(a: Parser<TA, TStateA>, b: Parser<TB, TStateB>, c: Parser<TC, TStateC>, d: Parser<TD, TStateD>, e: Parser<TE, TStateE>, f: Parser<TF, TStateF>, g: Parser<TG, TStateG>, h: Parser<TH, TStateH>, i: Parser<TI, TStateI>, j: Parser<TJ, TStateJ>): Parser<TA | TB | TC | TD | TE | TF | TG | TH | TI | TJ, undefined | [0, ParserResult<TStateA>] | [1, ParserResult<TStateB>] | [2, ParserResult<TStateC>] | [3, ParserResult<TStateD>] | [4, ParserResult<TStateE>] | [5, ParserResult<TStateF>] | [6, ParserResult<TStateG>] | [7, ParserResult<TStateH>] | [8, ParserResult<TStateI>] | [9, ParserResult<TStateJ>]>;
651
+ /**
652
+ * Creates a parser that combines two mutually exclusive parsers into one,
653
+ * selecting the parser that consumes the most tokens.
654
+ * The resulting parser will try both parsers and return the result
655
+ * of the parser that consumed more input tokens.
656
+ * @template TA The type of the value returned by the first parser.
657
+ * @template TB The type of the value returned by the second parser.
658
+ * @template TStateA The type of the state used by the first parser.
659
+ * @template TStateB The type of the state used by the second parser.
660
+ * @param a The first {@link Parser} to try.
661
+ * @param b The second {@link Parser} to try.
662
+ * @returns A {@link Parser} that tries to parse using both parsers
663
+ * and returns the result of the parser that consumed more tokens.
664
+ */
665
+ declare function longestMatch<TA, TB, TStateA, TStateB>(a: Parser<TA, TStateA>, b: Parser<TB, TStateB>): Parser<TA | TB, undefined | [0, ParserResult<TStateA>] | [1, ParserResult<TStateB>]>;
666
+ /**
667
+ * Creates a parser that combines three mutually exclusive parsers into one,
668
+ * selecting the parser that consumes the most tokens.
669
+ * The resulting parser will try all parsers and return the result
670
+ * of the parser that consumed the most input tokens.
671
+ * @template TA The type of the value returned by the first parser.
672
+ * @template TB The type of the value returned by the second parser.
673
+ * @template TC The type of the value returned by the third parser.
674
+ * @template TStateA The type of the state used by the first parser.
675
+ * @template TStateB The type of the state used by the second parser.
676
+ * @template TStateC The type of the state used by the third parser.
677
+ * @param a The first {@link Parser} to try.
678
+ * @param b The second {@link Parser} to try.
679
+ * @param c The third {@link Parser} to try.
680
+ * @returns A {@link Parser} that tries to parse using all parsers
681
+ * and returns the result of the parser that consumed the most tokens.
682
+ */
683
+ declare function longestMatch<TA, TB, TC, TStateA, TStateB, TStateC>(a: Parser<TA, TStateA>, b: Parser<TB, TStateB>, c: Parser<TC, TStateC>): Parser<TA | TB | TC, undefined | [0, ParserResult<TStateA>] | [1, ParserResult<TStateB>] | [2, ParserResult<TStateC>]>;
684
+ /**
685
+ * Creates a parser that combines four mutually exclusive parsers into one,
686
+ * selecting the parser that consumes the most tokens.
687
+ * The resulting parser will try all parsers and return the result
688
+ * of the parser that consumed the most input tokens.
689
+ * @template TA The type of the value returned by the first parser.
690
+ * @template TB The type of the value returned by the second parser.
691
+ * @template TC The type of the value returned by the third parser.
692
+ * @template TD The type of the value returned by the fourth parser.
693
+ * @template TStateA The type of the state used by the first parser.
694
+ * @template TStateB The type of the state used by the second parser.
695
+ * @template TStateC The type of the state used by the third parser.
696
+ * @template TStateD The type of the state used by the fourth parser.
697
+ * @param a The first {@link Parser} to try.
698
+ * @param b The second {@link Parser} to try.
699
+ * @param c The third {@link Parser} to try.
700
+ * @param d The fourth {@link Parser} to try.
701
+ * @returns A {@link Parser} that tries to parse using all parsers
702
+ * and returns the result of the parser that consumed the most tokens.
703
+ */
704
+ declare function longestMatch<TA, TB, TC, TD, TStateA, TStateB, TStateC, TStateD>(a: Parser<TA, TStateA>, b: Parser<TB, TStateB>, c: Parser<TC, TStateC>, d: Parser<TD, TStateD>): Parser<TA | TB | TC | TD, undefined | [0, ParserResult<TStateA>] | [1, ParserResult<TStateB>] | [2, ParserResult<TStateC>] | [3, ParserResult<TStateD>]>;
705
+ /**
706
+ * Creates a parser that combines five mutually exclusive parsers into one,
707
+ * selecting the parser that consumes the most tokens.
708
+ * The resulting parser will try all parsers and return the result
709
+ * of the parser that consumed the most input tokens.
710
+ * @template TA The type of the value returned by the first parser.
711
+ * @template TB The type of the value returned by the second parser.
712
+ * @template TC The type of the value returned by the third parser.
713
+ * @template TD The type of the value returned by the fourth parser.
714
+ * @template TE The type of the value returned by the fifth parser.
715
+ * @template TStateA The type of the state used by the first parser.
716
+ * @template TStateB The type of the state used by the second parser.
717
+ * @template TStateC The type of the state used by the third parser.
718
+ * @template TStateD The type of the state used by the fourth parser.
719
+ * @template TStateE The type of the state used by the fifth parser.
720
+ * @param a The first {@link Parser} to try.
721
+ * @param b The second {@link Parser} to try.
722
+ * @param c The third {@link Parser} to try.
723
+ * @param d The fourth {@link Parser} to try.
724
+ * @param e The fifth {@link Parser} to try.
725
+ * @returns A {@link Parser} that tries to parse using all parsers
726
+ * and returns the result of the parser that consumed the most tokens.
727
+ */
728
+ declare function longestMatch<TA, TB, TC, TD, TE, TStateA, TStateB, TStateC, TStateD, TStateE>(a: Parser<TA, TStateA>, b: Parser<TB, TStateB>, c: Parser<TC, TStateC>, d: Parser<TD, TStateD>, e: Parser<TE, TStateE>): Parser<TA | TB | TC | TD | TE, undefined | [0, ParserResult<TStateA>] | [1, ParserResult<TStateB>] | [2, ParserResult<TStateC>] | [3, ParserResult<TStateD>] | [4, ParserResult<TStateE>]>;
639
729
  /**
640
730
  * Helper type to check if all members of a union are object-like.
641
731
  * This allows merge() to work with parsers like withDefault() that produce union types.
@@ -924,4 +1014,4 @@ declare function parse<T>(parser: Parser<T, unknown>, args: readonly string[]):
924
1014
  */
925
1015
  declare function getDocPage(parser: Parser<unknown, unknown>, args?: readonly string[]): DocPage | undefined;
926
1016
  //#endregion
927
- export { ArgumentOptions, CommandOptions, FlagOptions, InferValue, MultipleOptions, OptionOptions, Parser, ParserContext, ParserResult, Result, argument, command, concat, constant, flag, getDocPage, map, merge, multiple, object, option, optional, or, parse, tuple, withDefault };
1017
+ export { ArgumentOptions, CommandOptions, DocState, FlagOptions, InferValue, MultipleOptions, OptionOptions, Parser, ParserContext, ParserResult, Result, argument, command, concat, constant, flag, getDocPage, longestMatch, map, merge, multiple, object, option, optional, or, parse, tuple, withDefault };
package/dist/parser.d.ts CHANGED
@@ -5,6 +5,19 @@ import { ValueParser, ValueParserResult } from "./valueparser.js";
5
5
 
6
6
  //#region src/parser.d.ts
7
7
 
8
+ /**
9
+ * Represents the state passed to getDocFragments.
10
+ * Can be either the actual parser state or an explicit indicator
11
+ * that no state is available.
12
+ * @template TState The type of the actual state when available.
13
+ * @since 0.3.0
14
+ */
15
+ type DocState<TState> = {
16
+ readonly kind: "available";
17
+ readonly state: TState;
18
+ } | {
19
+ readonly kind: "unavailable";
20
+ };
8
21
  /**
9
22
  * Parser interface for command-line argument parsing.
10
23
  * @template TValue The type of the value returned by the parser.
@@ -65,15 +78,14 @@ interface Parser<TValue, TState> {
65
78
  /**
66
79
  * Generates a documentation fragment for this parser, which can be used
67
80
  * to describe the parser's usage, description, and default value.
68
- * @param state The current state of the parser, which may contain
69
- * accumulated data or context needed to produce
70
- * the documentation.
81
+ * @param state The current state of the parser, wrapped in a DocState
82
+ * to indicate whether the actual state is available or not.
71
83
  * @param defaultValue An optional default value that can be used
72
84
  * to provide a default value in the documentation.
73
85
  * @returns {@link DocFragments} object containing documentation
74
86
  * fragments for this parser.
75
87
  */
76
- getDocFragments(state: TState, defaultValue?: TValue): DocFragments;
88
+ getDocFragments(state: DocState<TState>, defaultValue?: TValue): DocFragments;
77
89
  }
78
90
  /**
79
91
  * The context of the parser, which includes the input buffer and the state.
@@ -636,6 +648,84 @@ declare function or<TA, TB, TC, TD, TE, TF, TG, TH, TI, TStateA, TStateB, TState
636
648
  * in order, returning the result of the first successful parser.
637
649
  */
638
650
  declare function or<TA, TB, TC, TD, TE, TF, TG, TH, TI, TJ, TStateA, TStateB, TStateC, TStateD, TStateE, TStateF, TStateG, TStateH, TStateI, TStateJ>(a: Parser<TA, TStateA>, b: Parser<TB, TStateB>, c: Parser<TC, TStateC>, d: Parser<TD, TStateD>, e: Parser<TE, TStateE>, f: Parser<TF, TStateF>, g: Parser<TG, TStateG>, h: Parser<TH, TStateH>, i: Parser<TI, TStateI>, j: Parser<TJ, TStateJ>): Parser<TA | TB | TC | TD | TE | TF | TG | TH | TI | TJ, undefined | [0, ParserResult<TStateA>] | [1, ParserResult<TStateB>] | [2, ParserResult<TStateC>] | [3, ParserResult<TStateD>] | [4, ParserResult<TStateE>] | [5, ParserResult<TStateF>] | [6, ParserResult<TStateG>] | [7, ParserResult<TStateH>] | [8, ParserResult<TStateI>] | [9, ParserResult<TStateJ>]>;
651
+ /**
652
+ * Creates a parser that combines two mutually exclusive parsers into one,
653
+ * selecting the parser that consumes the most tokens.
654
+ * The resulting parser will try both parsers and return the result
655
+ * of the parser that consumed more input tokens.
656
+ * @template TA The type of the value returned by the first parser.
657
+ * @template TB The type of the value returned by the second parser.
658
+ * @template TStateA The type of the state used by the first parser.
659
+ * @template TStateB The type of the state used by the second parser.
660
+ * @param a The first {@link Parser} to try.
661
+ * @param b The second {@link Parser} to try.
662
+ * @returns A {@link Parser} that tries to parse using both parsers
663
+ * and returns the result of the parser that consumed more tokens.
664
+ */
665
+ declare function longestMatch<TA, TB, TStateA, TStateB>(a: Parser<TA, TStateA>, b: Parser<TB, TStateB>): Parser<TA | TB, undefined | [0, ParserResult<TStateA>] | [1, ParserResult<TStateB>]>;
666
+ /**
667
+ * Creates a parser that combines three mutually exclusive parsers into one,
668
+ * selecting the parser that consumes the most tokens.
669
+ * The resulting parser will try all parsers and return the result
670
+ * of the parser that consumed the most input tokens.
671
+ * @template TA The type of the value returned by the first parser.
672
+ * @template TB The type of the value returned by the second parser.
673
+ * @template TC The type of the value returned by the third parser.
674
+ * @template TStateA The type of the state used by the first parser.
675
+ * @template TStateB The type of the state used by the second parser.
676
+ * @template TStateC The type of the state used by the third parser.
677
+ * @param a The first {@link Parser} to try.
678
+ * @param b The second {@link Parser} to try.
679
+ * @param c The third {@link Parser} to try.
680
+ * @returns A {@link Parser} that tries to parse using all parsers
681
+ * and returns the result of the parser that consumed the most tokens.
682
+ */
683
+ declare function longestMatch<TA, TB, TC, TStateA, TStateB, TStateC>(a: Parser<TA, TStateA>, b: Parser<TB, TStateB>, c: Parser<TC, TStateC>): Parser<TA | TB | TC, undefined | [0, ParserResult<TStateA>] | [1, ParserResult<TStateB>] | [2, ParserResult<TStateC>]>;
684
+ /**
685
+ * Creates a parser that combines four mutually exclusive parsers into one,
686
+ * selecting the parser that consumes the most tokens.
687
+ * The resulting parser will try all parsers and return the result
688
+ * of the parser that consumed the most input tokens.
689
+ * @template TA The type of the value returned by the first parser.
690
+ * @template TB The type of the value returned by the second parser.
691
+ * @template TC The type of the value returned by the third parser.
692
+ * @template TD The type of the value returned by the fourth parser.
693
+ * @template TStateA The type of the state used by the first parser.
694
+ * @template TStateB The type of the state used by the second parser.
695
+ * @template TStateC The type of the state used by the third parser.
696
+ * @template TStateD The type of the state used by the fourth parser.
697
+ * @param a The first {@link Parser} to try.
698
+ * @param b The second {@link Parser} to try.
699
+ * @param c The third {@link Parser} to try.
700
+ * @param d The fourth {@link Parser} to try.
701
+ * @returns A {@link Parser} that tries to parse using all parsers
702
+ * and returns the result of the parser that consumed the most tokens.
703
+ */
704
+ declare function longestMatch<TA, TB, TC, TD, TStateA, TStateB, TStateC, TStateD>(a: Parser<TA, TStateA>, b: Parser<TB, TStateB>, c: Parser<TC, TStateC>, d: Parser<TD, TStateD>): Parser<TA | TB | TC | TD, undefined | [0, ParserResult<TStateA>] | [1, ParserResult<TStateB>] | [2, ParserResult<TStateC>] | [3, ParserResult<TStateD>]>;
705
+ /**
706
+ * Creates a parser that combines five mutually exclusive parsers into one,
707
+ * selecting the parser that consumes the most tokens.
708
+ * The resulting parser will try all parsers and return the result
709
+ * of the parser that consumed the most input tokens.
710
+ * @template TA The type of the value returned by the first parser.
711
+ * @template TB The type of the value returned by the second parser.
712
+ * @template TC The type of the value returned by the third parser.
713
+ * @template TD The type of the value returned by the fourth parser.
714
+ * @template TE The type of the value returned by the fifth parser.
715
+ * @template TStateA The type of the state used by the first parser.
716
+ * @template TStateB The type of the state used by the second parser.
717
+ * @template TStateC The type of the state used by the third parser.
718
+ * @template TStateD The type of the state used by the fourth parser.
719
+ * @template TStateE The type of the state used by the fifth parser.
720
+ * @param a The first {@link Parser} to try.
721
+ * @param b The second {@link Parser} to try.
722
+ * @param c The third {@link Parser} to try.
723
+ * @param d The fourth {@link Parser} to try.
724
+ * @param e The fifth {@link Parser} to try.
725
+ * @returns A {@link Parser} that tries to parse using all parsers
726
+ * and returns the result of the parser that consumed the most tokens.
727
+ */
728
+ declare function longestMatch<TA, TB, TC, TD, TE, TStateA, TStateB, TStateC, TStateD, TStateE>(a: Parser<TA, TStateA>, b: Parser<TB, TStateB>, c: Parser<TC, TStateC>, d: Parser<TD, TStateD>, e: Parser<TE, TStateE>): Parser<TA | TB | TC | TD | TE, undefined | [0, ParserResult<TStateA>] | [1, ParserResult<TStateB>] | [2, ParserResult<TStateC>] | [3, ParserResult<TStateD>] | [4, ParserResult<TStateE>]>;
639
729
  /**
640
730
  * Helper type to check if all members of a union are object-like.
641
731
  * This allows merge() to work with parsers like withDefault() that produce union types.
@@ -924,4 +1014,4 @@ declare function parse<T>(parser: Parser<T, unknown>, args: readonly string[]):
924
1014
  */
925
1015
  declare function getDocPage(parser: Parser<unknown, unknown>, args?: readonly string[]): DocPage | undefined;
926
1016
  //#endregion
927
- export { ArgumentOptions, CommandOptions, FlagOptions, InferValue, MultipleOptions, OptionOptions, Parser, ParserContext, ParserResult, Result, argument, command, concat, constant, flag, getDocPage, map, merge, multiple, object, option, optional, or, parse, tuple, withDefault };
1017
+ export { ArgumentOptions, CommandOptions, DocState, FlagOptions, InferValue, MultipleOptions, OptionOptions, Parser, ParserContext, ParserResult, Result, argument, command, concat, constant, flag, getDocPage, longestMatch, map, merge, multiple, object, option, optional, or, parse, tuple, withDefault };
package/dist/parser.js CHANGED
@@ -514,7 +514,11 @@ function optional(parser) {
514
514
  return parser.complete(state[0]);
515
515
  },
516
516
  getDocFragments(state, defaultValue) {
517
- return parser.getDocFragments(typeof state === "undefined" ? parser.initialState : state[0], defaultValue);
517
+ const innerState = state.kind === "unavailable" ? { kind: "unavailable" } : state.state === void 0 ? { kind: "unavailable" } : {
518
+ kind: "available",
519
+ state: state.state[0]
520
+ };
521
+ return parser.getDocFragments(innerState, defaultValue);
518
522
  }
519
523
  };
520
524
  }
@@ -566,7 +570,11 @@ function withDefault(parser, defaultValue) {
566
570
  return parser.complete(state[0]);
567
571
  },
568
572
  getDocFragments(state, upperDefaultValue) {
569
- return parser.getDocFragments(typeof state === "undefined" ? parser.initialState : state[0], upperDefaultValue != null ? upperDefaultValue : typeof defaultValue === "function" ? defaultValue() : defaultValue);
573
+ const innerState = state.kind === "unavailable" ? { kind: "unavailable" } : state.state === void 0 ? { kind: "unavailable" } : {
574
+ kind: "available",
575
+ state: state.state[0]
576
+ };
577
+ return parser.getDocFragments(innerState, upperDefaultValue != null ? upperDefaultValue : typeof defaultValue === "function" ? defaultValue() : defaultValue);
570
578
  }
571
579
  };
572
580
  }
@@ -697,7 +705,11 @@ function multiple(parser, options = {}) {
697
705
  };
698
706
  },
699
707
  getDocFragments(state, defaultValue) {
700
- return parser.getDocFragments(state.at(-1) ?? parser.initialState, defaultValue != null && defaultValue.length > 0 ? defaultValue[0] : void 0);
708
+ const innerState = state.kind === "unavailable" ? { kind: "unavailable" } : state.state.length > 0 ? {
709
+ kind: "available",
710
+ state: state.state.at(-1)
711
+ } : { kind: "unavailable" };
712
+ return parser.getDocFragments(innerState, defaultValue != null && defaultValue.length > 0 ? defaultValue[0] : void 0);
701
713
  }
702
714
  };
703
715
  }
@@ -788,7 +800,13 @@ function object(labelOrParsers, maybeParsers) {
788
800
  };
789
801
  },
790
802
  getDocFragments(state, defaultValue) {
791
- const fragments = parserPairs.flatMap(([field, p]) => p.getDocFragments(state[field], defaultValue?.[field]).fragments);
803
+ const fragments = parserPairs.flatMap(([field, p]) => {
804
+ const fieldState = state.kind === "unavailable" ? { kind: "unavailable" } : {
805
+ kind: "available",
806
+ state: state.state[field]
807
+ };
808
+ return p.getDocFragments(fieldState, defaultValue?.[field]).fragments;
809
+ });
792
810
  const entries = fragments.filter((d) => d.type === "entry");
793
811
  const sections = [];
794
812
  for (const fragment of fragments) {
@@ -892,7 +910,13 @@ function tuple(labelOrParsers, maybeParsers) {
892
910
  };
893
911
  },
894
912
  getDocFragments(state, defaultValue) {
895
- const fragments = parsers.flatMap((p, i) => p.getDocFragments(state[i], defaultValue?.[i]).fragments);
913
+ const fragments = parsers.flatMap((p, i) => {
914
+ const indexState = state.kind === "unavailable" ? { kind: "unavailable" } : {
915
+ kind: "available",
916
+ state: state.state[i]
917
+ };
918
+ return p.getDocFragments(indexState, defaultValue?.[i]).fragments;
919
+ });
896
920
  const entries = fragments.filter((d) => d.type === "entry");
897
921
  const sections = [];
898
922
  for (const fragment of fragments) {
@@ -976,10 +1000,14 @@ function or(...parsers) {
976
1000
  getDocFragments(state, _defaultValue) {
977
1001
  let description;
978
1002
  let fragments;
979
- if (state == null) fragments = parsers.flatMap((p) => p.getDocFragments(p.initialState, void 0).fragments);
1003
+ if (state.kind === "unavailable" || state.state == null) fragments = parsers.flatMap((p) => p.getDocFragments({ kind: "unavailable" }, void 0).fragments);
980
1004
  else {
981
- const [index, parserResult] = state;
982
- const docFragments = parsers[index].getDocFragments(parserResult.success ? parserResult.next.state : parsers[index].initialState, void 0);
1005
+ const [index, parserResult] = state.state;
1006
+ const innerState = parserResult.success ? {
1007
+ kind: "available",
1008
+ state: parserResult.next.state
1009
+ } : { kind: "unavailable" };
1010
+ const docFragments = parsers[index].getDocFragments(innerState, void 0);
983
1011
  description = docFragments.description;
984
1012
  fragments = docFragments.fragments;
985
1013
  }
@@ -1003,6 +1031,86 @@ function or(...parsers) {
1003
1031
  }
1004
1032
  };
1005
1033
  }
1034
+ function longestMatch(...parsers) {
1035
+ return {
1036
+ $valueType: [],
1037
+ $stateType: [],
1038
+ priority: Math.max(...parsers.map((p) => p.priority)),
1039
+ usage: [{
1040
+ type: "exclusive",
1041
+ terms: parsers.map((p) => p.usage)
1042
+ }],
1043
+ initialState: void 0,
1044
+ complete(state) {
1045
+ if (state == null) return {
1046
+ success: false,
1047
+ error: message`No parser matched.`
1048
+ };
1049
+ const [i, result] = state;
1050
+ if (result.success) return parsers[i].complete(result.next.state);
1051
+ return {
1052
+ success: false,
1053
+ error: result.error
1054
+ };
1055
+ },
1056
+ parse(context) {
1057
+ let bestMatch = null;
1058
+ let error = {
1059
+ consumed: 0,
1060
+ error: context.buffer.length < 1 ? message`No parser matched.` : message`Unexpected option or subcommand: ${optionName(context.buffer[0])}.`
1061
+ };
1062
+ for (let i = 0; i < parsers.length; i++) {
1063
+ const parser = parsers[i];
1064
+ const result = parser.parse({
1065
+ ...context,
1066
+ state: context.state == null || context.state[0] !== i || !context.state[1].success ? parser.initialState : context.state[1].next.state
1067
+ });
1068
+ if (result.success) {
1069
+ const consumed = context.buffer.length - result.next.buffer.length;
1070
+ if (bestMatch === null || consumed > bestMatch.consumed) bestMatch = {
1071
+ index: i,
1072
+ result,
1073
+ consumed
1074
+ };
1075
+ } else if (error.consumed < result.consumed) error = result;
1076
+ }
1077
+ if (bestMatch && bestMatch.result.success) return {
1078
+ success: true,
1079
+ next: {
1080
+ ...context,
1081
+ buffer: bestMatch.result.next.buffer,
1082
+ optionsTerminated: bestMatch.result.next.optionsTerminated,
1083
+ state: [bestMatch.index, bestMatch.result]
1084
+ },
1085
+ consumed: bestMatch.result.consumed
1086
+ };
1087
+ return {
1088
+ ...error,
1089
+ success: false
1090
+ };
1091
+ },
1092
+ getDocFragments(state, _defaultValue) {
1093
+ let description;
1094
+ let fragments;
1095
+ if (state.kind === "unavailable" || state.state == null) fragments = parsers.flatMap((p) => p.getDocFragments({ kind: "unavailable" }).fragments);
1096
+ else {
1097
+ const [i, result] = state.state;
1098
+ if (result.success) {
1099
+ const docResult = parsers[i].getDocFragments({
1100
+ kind: "available",
1101
+ state: result.next.state
1102
+ });
1103
+ description = docResult.description;
1104
+ fragments = docResult.fragments;
1105
+ } else fragments = parsers.flatMap((p) => p.getDocFragments({ kind: "unavailable" }).fragments);
1106
+ }
1107
+ return {
1108
+ description,
1109
+ fragments
1110
+ };
1111
+ }
1112
+ };
1113
+ }
1006
1114
  function merge(...parsers) {
1007
1115
  parsers = parsers.toSorted((a, b) => b.priority - a.priority);
1008
1116
  const initialState = {};
@@ -1082,7 +1190,13 @@ function merge(...parsers) {
1082
1190
  };
1083
1191
  },
1084
1192
  getDocFragments(state, _defaultValue) {
1085
- const fragments = parsers.flatMap((p) => p.getDocFragments(state, void 0).fragments);
1193
+ const fragments = parsers.flatMap((p) => {
1194
+ const parserState = p.initialState === void 0 ? { kind: "unavailable" } : state.kind === "unavailable" ? { kind: "unavailable" } : {
1195
+ kind: "available",
1196
+ state: state.state
1197
+ };
1198
+ return p.getDocFragments(parserState, void 0).fragments;
1199
+ });
1086
1200
  const entries = fragments.filter((f) => f.type === "entry");
1087
1201
  const sections = [];
1088
1202
  for (const fragment of fragments) {
@@ -1183,7 +1297,13 @@ function concat(...parsers) {
1183
1297
  };
1184
1298
  },
1185
1299
  getDocFragments(state, _defaultValue) {
1186
- const fragments = parsers.flatMap((p, index) => p.getDocFragments(state[index], void 0).fragments);
1300
+ const fragments = parsers.flatMap((p, index) => {
1301
+ const indexState = state.kind === "unavailable" ? { kind: "unavailable" } : {
1302
+ kind: "available",
1303
+ state: state.state[index]
1304
+ };
1305
+ return p.getDocFragments(indexState, void 0).fragments;
1306
+ });
1187
1307
  const entries = fragments.filter((f) => f.type === "entry");
1188
1308
  const sections = [];
1189
1309
  for (const fragment of fragments) {
@@ -1290,7 +1410,7 @@ function command(name, parser, options = {}) {
1290
1410
  };
1291
1411
  },
1292
1412
  getDocFragments(state, defaultValue) {
1293
- if (typeof state === "undefined") return {
1413
+ if (state.kind === "unavailable" || typeof state.state === "undefined") return {
1294
1414
  description: options.description,
1295
1415
  fragments: [{
1296
1416
  type: "entry",
@@ -1301,7 +1421,11 @@ function command(name, parser, options = {}) {
1301
1421
  description: options.description
1302
1422
  }]
1303
1423
  };
1304
- const innerFragments = parser.getDocFragments(state[0] === "parsing" ? state[1] : parser.initialState, defaultValue);
1424
+ const innerState = state.state[0] === "parsing" ? {
1425
+ kind: "available",
1426
+ state: state.state[1]
1427
+ } : { kind: "unavailable" };
1428
+ const innerFragments = parser.getDocFragments(innerState, defaultValue);
1305
1429
  return {
1306
1430
  ...innerFragments,
1307
1431
  description: innerFragments.description ?? options.description
@@ -1398,7 +1522,10 @@ function getDocPage(parser, args = []) {
1398
1522
  if (!result.success) break;
1399
1523
  context = result.next;
1400
1524
  } while (context.buffer.length > 0);
1401
- const { description, fragments } = parser.getDocFragments(context.state, void 0);
1525
+ const { description, fragments } = parser.getDocFragments({
1526
+ kind: "available",
1527
+ state: context.state
1528
+ }, void 0);
1402
1529
  const entries = fragments.filter((f) => f.type === "entry");
1403
1530
  const sections = [];
1404
1531
  for (const fragment of fragments) {
@@ -1430,4 +1557,4 @@ function getDocPage(parser, args = []) {
1430
1557
  }
1431
1558
 
1432
1559
  //#endregion
1433
- export { argument, command, concat, constant, flag, getDocPage, map, merge, multiple, object, option, optional, or, parse, tuple, withDefault };
1560
+ export { argument, command, concat, constant, flag, getDocPage, longestMatch, map, merge, multiple, object, option, optional, or, parse, tuple, withDefault };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/core",
3
- "version": "0.3.0-dev.39+80d894ad",
3
+ "version": "0.3.0-dev.41+76c1e568",
4
4
  "description": "Type-safe combinatorial command-line interface parser",
5
5
  "keywords": [
6
6
  "CLI",