@optique/core 1.1.1 → 1.1.3

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.
@@ -581,6 +581,35 @@ function createExclusiveState(parentState, index, parser, result) {
581
581
  return require_internal_annotations.annotateFreshArray(parentState, [index, annotatedResult]);
582
582
  }
583
583
  /**
584
+ * Detects a branch result that handled only the global options terminator.
585
+ * Such a result advances the shared parser context, but does not identify
586
+ * which exclusive branch matched.
587
+ * @internal
588
+ */
589
+ function isOptionsTerminatorOnlyResult(context, result) {
590
+ return !context.optionsTerminated && context.buffer[0] === "--" && result.success && result.next.optionsTerminated && result.consumed.length === 1 && result.consumed[0] === "--";
591
+ }
592
+ function isUnselectedBooleanOptionState(state) {
593
+ return state != null && typeof state === "object" && "success" in state && state.success === true && "value" in state && state.value === false;
594
+ }
595
+ function preserveExclusiveStateAfterOptionsTerminator(context, result) {
596
+ const mergedExec = require_execution_context.mergeChildExec(context.exec, result.next.exec);
597
+ return {
598
+ success: true,
599
+ next: {
600
+ ...context,
601
+ buffer: result.next.buffer,
602
+ optionsTerminated: true,
603
+ state: context.state,
604
+ ...mergedExec != null ? {
605
+ exec: mergedExec,
606
+ dependencyRegistry: mergedExec.dependencyRegistry
607
+ } : {}
608
+ },
609
+ consumed: result.consumed
610
+ };
611
+ }
612
+ /**
584
613
  * Creates a complete() method shared by or() and longestMatch().
585
614
  * @internal
586
615
  */
@@ -818,9 +847,15 @@ function or(...args) {
818
847
  let zeroConsumedBranch = null;
819
848
  let zeroConsumedCount = 0;
820
849
  let provisionalConsuming = null;
850
+ let optionsTerminatorResult = null;
821
851
  let provisionalAmbiguous = false;
822
852
  for (const [parser, i] of orderedParsers) {
823
853
  const result = parser.parse(withChildContext$1(context, i, activeState == null || activeState[0] !== i || !activeState[1].success ? parser.initialState : activeState[1].next.state, parser));
854
+ if (isOptionsTerminatorOnlyResult(context, result)) {
855
+ if (activeState?.[0] === i && activeState[1].success && activeState[1].consumed.length > 0 && result.next.buffer.length < 1) return preserveExclusiveStateAfterOptionsTerminator(context, result);
856
+ optionsTerminatorResult ??= result;
857
+ continue;
858
+ }
824
859
  if (result.success && result.consumed.length > 0) {
825
860
  if (result.provisional) {
826
861
  const activeBranchLocked = activeState != null && activeState[1].success && activeState[1].consumed.length > 0;
@@ -1001,6 +1036,7 @@ function or(...args) {
1001
1036
  }
1002
1037
  }
1003
1038
  }
1039
+ if (optionsTerminatorResult != null) return preserveExclusiveStateAfterOptionsTerminator(context, optionsTerminatorResult);
1004
1040
  return {
1005
1041
  ...error,
1006
1042
  success: false
@@ -1014,10 +1050,16 @@ function or(...args) {
1014
1050
  let zeroConsumedBranch = null;
1015
1051
  let zeroConsumedCount = 0;
1016
1052
  let provisionalConsuming = null;
1053
+ let optionsTerminatorResult = null;
1017
1054
  let provisionalAmbiguous = false;
1018
1055
  for (const [parser, i] of orderedParsers) {
1019
1056
  const resultOrPromise = parser.parse(withChildContext$1(context, i, activeState == null || activeState[0] !== i || !activeState[1].success ? parser.initialState : activeState[1].next.state, parser));
1020
1057
  const result = await resultOrPromise;
1058
+ if (isOptionsTerminatorOnlyResult(context, result)) {
1059
+ if (activeState?.[0] === i && activeState[1].success && activeState[1].consumed.length > 0 && result.next.buffer.length < 1) return preserveExclusiveStateAfterOptionsTerminator(context, result);
1060
+ optionsTerminatorResult ??= result;
1061
+ continue;
1062
+ }
1021
1063
  if (result.success && result.consumed.length > 0) {
1022
1064
  if (result.provisional) {
1023
1065
  const activeBranchLocked = activeState != null && activeState[1].success && activeState[1].consumed.length > 0;
@@ -1200,6 +1242,7 @@ function or(...args) {
1200
1242
  }
1201
1243
  }
1202
1244
  }
1245
+ if (optionsTerminatorResult != null) return preserveExclusiveStateAfterOptionsTerminator(context, optionsTerminatorResult);
1203
1246
  return {
1204
1247
  ...error,
1205
1248
  success: false
@@ -1301,82 +1344,122 @@ function createLongestMatch(...args) {
1301
1344
  return createUnexpectedInputErrorWithScopedSuggestions(defaultMsg, token, parsers, options?.errors?.suggestions);
1302
1345
  })()
1303
1346
  });
1304
- const parseSync = (context) => {
1347
+ const commitLongestMatch = (context, match, optionsTerminatorResult) => {
1348
+ const selectedResult = optionsTerminatorResult == null ? match.result : {
1349
+ ...match.result,
1350
+ next: {
1351
+ ...match.result.next,
1352
+ buffer: optionsTerminatorResult.next.buffer,
1353
+ optionsTerminated: true
1354
+ },
1355
+ consumed: [...optionsTerminatorResult.consumed, ...match.result.consumed]
1356
+ };
1357
+ const parser = parsers[match.index];
1358
+ const mergedExec = require_execution_context.mergeChildExec(context.exec, selectedResult.next.exec);
1359
+ return {
1360
+ success: true,
1361
+ next: {
1362
+ ...context,
1363
+ buffer: selectedResult.next.buffer,
1364
+ optionsTerminated: selectedResult.next.optionsTerminated,
1365
+ state: createExclusiveState(context.state, match.index, parser, selectedResult),
1366
+ ...mergedExec != null ? {
1367
+ exec: mergedExec,
1368
+ dependencyRegistry: mergedExec.dependencyRegistry
1369
+ } : {}
1370
+ },
1371
+ consumed: selectedResult.consumed
1372
+ };
1373
+ };
1374
+ const resolveLongestMatchOptionsTerminator = (context, bestMatch, optionsTerminatorMatches) => {
1375
+ if (optionsTerminatorMatches.length < 1) return null;
1376
+ if (bestMatch != null && bestMatch.consumed > 0) return null;
1377
+ const activeState = normalizeExclusiveState(context.state);
1378
+ if (activeState != null && activeState[1].success && activeState[1].consumed.length > 0) return preserveExclusiveStateAfterOptionsTerminator(context, optionsTerminatorMatches[0].result);
1379
+ let selected = bestMatch == null ? null : {
1380
+ match: bestMatch,
1381
+ fromTerminator: false
1382
+ };
1383
+ for (const match of optionsTerminatorMatches) {
1384
+ if (!match.selectedAfterTerminator && (extractRequiredUsage(parsers[match.index].usage).length > 0 || isUnselectedBooleanOptionState(match.result.next.state))) continue;
1385
+ const selectedIsProvisional = selected != null && !!selected.match.result.provisional;
1386
+ const matchIsProvisional = !!match.result.provisional;
1387
+ if (selected == null || selectedIsProvisional && !matchIsProvisional || selectedIsProvisional === matchIsProvisional && match.index < selected.match.index) selected = {
1388
+ match,
1389
+ fromTerminator: true
1390
+ };
1391
+ }
1392
+ if (selected != null) return commitLongestMatch(context, selected.match, selected.fromTerminator ? void 0 : optionsTerminatorMatches[0].result);
1393
+ return preserveExclusiveStateAfterOptionsTerminator(context, optionsTerminatorMatches[0].result);
1394
+ };
1395
+ const parseSyncBranches = (context) => {
1305
1396
  let bestMatch = null;
1397
+ const optionsTerminatorMatches = [];
1306
1398
  let error = getInitialError(context);
1307
1399
  const activeState = normalizeExclusiveState(context.state);
1308
1400
  for (let i = 0; i < syncParsers.length; i++) {
1309
1401
  const parser = syncParsers[i];
1310
- const result = parser.parse(withChildContext$1(context, i, activeState == null || activeState[0] !== i || !activeState[1].success ? parser.initialState : activeState[1].next.state, parser));
1402
+ const childContext = withChildContext$1(context, i, activeState == null || activeState[0] !== i || !activeState[1].success ? parser.initialState : activeState[1].next.state, parser);
1403
+ const result = parser.parse(childContext);
1404
+ if (isOptionsTerminatorOnlyResult(context, result)) {
1405
+ optionsTerminatorMatches.push({
1406
+ index: i,
1407
+ result,
1408
+ consumed: 1,
1409
+ ...normalizeExclusiveState(result.next.state) != null ? { selectedAfterTerminator: true } : {}
1410
+ });
1411
+ continue;
1412
+ }
1311
1413
  if (result.success) {
1312
1414
  const consumed = context.buffer.length - result.next.buffer.length;
1313
1415
  const bestIsProvisional = bestMatch != null && bestMatch.result.success && !!bestMatch.result.provisional;
1314
1416
  if (bestMatch === null || consumed > bestMatch.consumed || consumed === bestMatch.consumed && bestIsProvisional && !result.provisional) bestMatch = {
1315
1417
  index: i,
1316
- parser,
1317
1418
  result,
1318
1419
  consumed
1319
1420
  };
1320
1421
  } else if (error.consumed < result.consumed) error = result;
1321
1422
  }
1322
- if (bestMatch && bestMatch.result.success) {
1323
- const mergedExec = require_execution_context.mergeChildExec(context.exec, bestMatch.result.next.exec);
1324
- return {
1325
- success: true,
1326
- next: {
1327
- ...context,
1328
- buffer: bestMatch.result.next.buffer,
1329
- optionsTerminated: bestMatch.result.next.optionsTerminated,
1330
- state: createExclusiveState(context.state, bestMatch.index, bestMatch.parser, bestMatch.result),
1331
- ...mergedExec != null ? {
1332
- exec: mergedExec,
1333
- dependencyRegistry: mergedExec.dependencyRegistry
1334
- } : {}
1335
- },
1336
- consumed: bestMatch.result.consumed
1337
- };
1338
- }
1423
+ const optionsTerminatorResolution = resolveLongestMatchOptionsTerminator(context, bestMatch, optionsTerminatorMatches);
1424
+ if (optionsTerminatorResolution != null) return optionsTerminatorResolution;
1425
+ if (bestMatch != null) return commitLongestMatch(context, bestMatch);
1339
1426
  return {
1340
1427
  ...error,
1341
1428
  success: false
1342
1429
  };
1343
1430
  };
1344
- const parseAsync = async (context) => {
1431
+ const parseAsyncBranches = async (context) => {
1345
1432
  let bestMatch = null;
1433
+ const optionsTerminatorMatches = [];
1346
1434
  let error = getInitialError(context);
1347
1435
  const activeState = normalizeExclusiveState(context.state);
1348
1436
  for (let i = 0; i < parsers.length; i++) {
1349
1437
  const parser = parsers[i];
1350
- const resultOrPromise = parser.parse(withChildContext$1(context, i, activeState == null || activeState[0] !== i || !activeState[1].success ? parser.initialState : activeState[1].next.state, parser));
1438
+ const childContext = withChildContext$1(context, i, activeState == null || activeState[0] !== i || !activeState[1].success ? parser.initialState : activeState[1].next.state, parser);
1439
+ const resultOrPromise = parser.parse(childContext);
1351
1440
  const result = await resultOrPromise;
1441
+ if (isOptionsTerminatorOnlyResult(context, result)) {
1442
+ optionsTerminatorMatches.push({
1443
+ index: i,
1444
+ result,
1445
+ consumed: 1,
1446
+ ...normalizeExclusiveState(result.next.state) != null ? { selectedAfterTerminator: true } : {}
1447
+ });
1448
+ continue;
1449
+ }
1352
1450
  if (result.success) {
1353
1451
  const consumed = context.buffer.length - result.next.buffer.length;
1354
1452
  const bestIsProvisional = bestMatch != null && bestMatch.result.success && !!bestMatch.result.provisional;
1355
1453
  if (bestMatch === null || consumed > bestMatch.consumed || consumed === bestMatch.consumed && bestIsProvisional && !result.provisional) bestMatch = {
1356
1454
  index: i,
1357
- parser,
1358
1455
  result,
1359
1456
  consumed
1360
1457
  };
1361
1458
  } else if (error.consumed < result.consumed) error = result;
1362
1459
  }
1363
- if (bestMatch && bestMatch.result.success) {
1364
- const mergedExec = require_execution_context.mergeChildExec(context.exec, bestMatch.result.next.exec);
1365
- return {
1366
- success: true,
1367
- next: {
1368
- ...context,
1369
- buffer: bestMatch.result.next.buffer,
1370
- optionsTerminated: bestMatch.result.next.optionsTerminated,
1371
- state: createExclusiveState(context.state, bestMatch.index, bestMatch.parser, bestMatch.result),
1372
- ...mergedExec != null ? {
1373
- exec: mergedExec,
1374
- dependencyRegistry: mergedExec.dependencyRegistry
1375
- } : {}
1376
- },
1377
- consumed: bestMatch.result.consumed
1378
- };
1379
- }
1460
+ const optionsTerminatorResolution = resolveLongestMatchOptionsTerminator(context, bestMatch, optionsTerminatorMatches);
1461
+ if (optionsTerminatorResolution != null) return optionsTerminatorResolution;
1462
+ if (bestMatch != null) return commitLongestMatch(context, bestMatch);
1380
1463
  return {
1381
1464
  ...error,
1382
1465
  success: false
@@ -1399,7 +1482,7 @@ function createLongestMatch(...args) {
1399
1482
  return extractExclusivePhase2Seed(parsers, state, exec, combinedMode);
1400
1483
  },
1401
1484
  parse(context) {
1402
- return require_mode_dispatch.dispatchByMode(combinedMode, () => parseSync(context), () => parseAsync(context));
1485
+ return require_mode_dispatch.dispatchByMode(combinedMode, () => parseSyncBranches(context), () => parseAsyncBranches(context));
1403
1486
  },
1404
1487
  getSuggestRuntimeNodes(state, path) {
1405
1488
  return getExclusiveSuggestRuntimeNodes(parsers, state, path);
@@ -581,6 +581,35 @@ function createExclusiveState(parentState, index, parser, result) {
581
581
  return annotateFreshArray(parentState, [index, annotatedResult]);
582
582
  }
583
583
  /**
584
+ * Detects a branch result that handled only the global options terminator.
585
+ * Such a result advances the shared parser context, but does not identify
586
+ * which exclusive branch matched.
587
+ * @internal
588
+ */
589
+ function isOptionsTerminatorOnlyResult(context, result) {
590
+ return !context.optionsTerminated && context.buffer[0] === "--" && result.success && result.next.optionsTerminated && result.consumed.length === 1 && result.consumed[0] === "--";
591
+ }
592
+ function isUnselectedBooleanOptionState(state) {
593
+ return state != null && typeof state === "object" && "success" in state && state.success === true && "value" in state && state.value === false;
594
+ }
595
+ function preserveExclusiveStateAfterOptionsTerminator(context, result) {
596
+ const mergedExec = mergeChildExec(context.exec, result.next.exec);
597
+ return {
598
+ success: true,
599
+ next: {
600
+ ...context,
601
+ buffer: result.next.buffer,
602
+ optionsTerminated: true,
603
+ state: context.state,
604
+ ...mergedExec != null ? {
605
+ exec: mergedExec,
606
+ dependencyRegistry: mergedExec.dependencyRegistry
607
+ } : {}
608
+ },
609
+ consumed: result.consumed
610
+ };
611
+ }
612
+ /**
584
613
  * Creates a complete() method shared by or() and longestMatch().
585
614
  * @internal
586
615
  */
@@ -818,9 +847,15 @@ function or(...args) {
818
847
  let zeroConsumedBranch = null;
819
848
  let zeroConsumedCount = 0;
820
849
  let provisionalConsuming = null;
850
+ let optionsTerminatorResult = null;
821
851
  let provisionalAmbiguous = false;
822
852
  for (const [parser, i] of orderedParsers) {
823
853
  const result = parser.parse(withChildContext$1(context, i, activeState == null || activeState[0] !== i || !activeState[1].success ? parser.initialState : activeState[1].next.state, parser));
854
+ if (isOptionsTerminatorOnlyResult(context, result)) {
855
+ if (activeState?.[0] === i && activeState[1].success && activeState[1].consumed.length > 0 && result.next.buffer.length < 1) return preserveExclusiveStateAfterOptionsTerminator(context, result);
856
+ optionsTerminatorResult ??= result;
857
+ continue;
858
+ }
824
859
  if (result.success && result.consumed.length > 0) {
825
860
  if (result.provisional) {
826
861
  const activeBranchLocked = activeState != null && activeState[1].success && activeState[1].consumed.length > 0;
@@ -1001,6 +1036,7 @@ function or(...args) {
1001
1036
  }
1002
1037
  }
1003
1038
  }
1039
+ if (optionsTerminatorResult != null) return preserveExclusiveStateAfterOptionsTerminator(context, optionsTerminatorResult);
1004
1040
  return {
1005
1041
  ...error,
1006
1042
  success: false
@@ -1014,10 +1050,16 @@ function or(...args) {
1014
1050
  let zeroConsumedBranch = null;
1015
1051
  let zeroConsumedCount = 0;
1016
1052
  let provisionalConsuming = null;
1053
+ let optionsTerminatorResult = null;
1017
1054
  let provisionalAmbiguous = false;
1018
1055
  for (const [parser, i] of orderedParsers) {
1019
1056
  const resultOrPromise = parser.parse(withChildContext$1(context, i, activeState == null || activeState[0] !== i || !activeState[1].success ? parser.initialState : activeState[1].next.state, parser));
1020
1057
  const result = await resultOrPromise;
1058
+ if (isOptionsTerminatorOnlyResult(context, result)) {
1059
+ if (activeState?.[0] === i && activeState[1].success && activeState[1].consumed.length > 0 && result.next.buffer.length < 1) return preserveExclusiveStateAfterOptionsTerminator(context, result);
1060
+ optionsTerminatorResult ??= result;
1061
+ continue;
1062
+ }
1021
1063
  if (result.success && result.consumed.length > 0) {
1022
1064
  if (result.provisional) {
1023
1065
  const activeBranchLocked = activeState != null && activeState[1].success && activeState[1].consumed.length > 0;
@@ -1200,6 +1242,7 @@ function or(...args) {
1200
1242
  }
1201
1243
  }
1202
1244
  }
1245
+ if (optionsTerminatorResult != null) return preserveExclusiveStateAfterOptionsTerminator(context, optionsTerminatorResult);
1203
1246
  return {
1204
1247
  ...error,
1205
1248
  success: false
@@ -1301,82 +1344,122 @@ function createLongestMatch(...args) {
1301
1344
  return createUnexpectedInputErrorWithScopedSuggestions(defaultMsg, token, parsers, options?.errors?.suggestions);
1302
1345
  })()
1303
1346
  });
1304
- const parseSync = (context) => {
1347
+ const commitLongestMatch = (context, match, optionsTerminatorResult) => {
1348
+ const selectedResult = optionsTerminatorResult == null ? match.result : {
1349
+ ...match.result,
1350
+ next: {
1351
+ ...match.result.next,
1352
+ buffer: optionsTerminatorResult.next.buffer,
1353
+ optionsTerminated: true
1354
+ },
1355
+ consumed: [...optionsTerminatorResult.consumed, ...match.result.consumed]
1356
+ };
1357
+ const parser = parsers[match.index];
1358
+ const mergedExec = mergeChildExec(context.exec, selectedResult.next.exec);
1359
+ return {
1360
+ success: true,
1361
+ next: {
1362
+ ...context,
1363
+ buffer: selectedResult.next.buffer,
1364
+ optionsTerminated: selectedResult.next.optionsTerminated,
1365
+ state: createExclusiveState(context.state, match.index, parser, selectedResult),
1366
+ ...mergedExec != null ? {
1367
+ exec: mergedExec,
1368
+ dependencyRegistry: mergedExec.dependencyRegistry
1369
+ } : {}
1370
+ },
1371
+ consumed: selectedResult.consumed
1372
+ };
1373
+ };
1374
+ const resolveLongestMatchOptionsTerminator = (context, bestMatch, optionsTerminatorMatches) => {
1375
+ if (optionsTerminatorMatches.length < 1) return null;
1376
+ if (bestMatch != null && bestMatch.consumed > 0) return null;
1377
+ const activeState = normalizeExclusiveState(context.state);
1378
+ if (activeState != null && activeState[1].success && activeState[1].consumed.length > 0) return preserveExclusiveStateAfterOptionsTerminator(context, optionsTerminatorMatches[0].result);
1379
+ let selected = bestMatch == null ? null : {
1380
+ match: bestMatch,
1381
+ fromTerminator: false
1382
+ };
1383
+ for (const match of optionsTerminatorMatches) {
1384
+ if (!match.selectedAfterTerminator && (extractRequiredUsage(parsers[match.index].usage).length > 0 || isUnselectedBooleanOptionState(match.result.next.state))) continue;
1385
+ const selectedIsProvisional = selected != null && !!selected.match.result.provisional;
1386
+ const matchIsProvisional = !!match.result.provisional;
1387
+ if (selected == null || selectedIsProvisional && !matchIsProvisional || selectedIsProvisional === matchIsProvisional && match.index < selected.match.index) selected = {
1388
+ match,
1389
+ fromTerminator: true
1390
+ };
1391
+ }
1392
+ if (selected != null) return commitLongestMatch(context, selected.match, selected.fromTerminator ? void 0 : optionsTerminatorMatches[0].result);
1393
+ return preserveExclusiveStateAfterOptionsTerminator(context, optionsTerminatorMatches[0].result);
1394
+ };
1395
+ const parseSyncBranches = (context) => {
1305
1396
  let bestMatch = null;
1397
+ const optionsTerminatorMatches = [];
1306
1398
  let error = getInitialError(context);
1307
1399
  const activeState = normalizeExclusiveState(context.state);
1308
1400
  for (let i = 0; i < syncParsers.length; i++) {
1309
1401
  const parser = syncParsers[i];
1310
- const result = parser.parse(withChildContext$1(context, i, activeState == null || activeState[0] !== i || !activeState[1].success ? parser.initialState : activeState[1].next.state, parser));
1402
+ const childContext = withChildContext$1(context, i, activeState == null || activeState[0] !== i || !activeState[1].success ? parser.initialState : activeState[1].next.state, parser);
1403
+ const result = parser.parse(childContext);
1404
+ if (isOptionsTerminatorOnlyResult(context, result)) {
1405
+ optionsTerminatorMatches.push({
1406
+ index: i,
1407
+ result,
1408
+ consumed: 1,
1409
+ ...normalizeExclusiveState(result.next.state) != null ? { selectedAfterTerminator: true } : {}
1410
+ });
1411
+ continue;
1412
+ }
1311
1413
  if (result.success) {
1312
1414
  const consumed = context.buffer.length - result.next.buffer.length;
1313
1415
  const bestIsProvisional = bestMatch != null && bestMatch.result.success && !!bestMatch.result.provisional;
1314
1416
  if (bestMatch === null || consumed > bestMatch.consumed || consumed === bestMatch.consumed && bestIsProvisional && !result.provisional) bestMatch = {
1315
1417
  index: i,
1316
- parser,
1317
1418
  result,
1318
1419
  consumed
1319
1420
  };
1320
1421
  } else if (error.consumed < result.consumed) error = result;
1321
1422
  }
1322
- if (bestMatch && bestMatch.result.success) {
1323
- const mergedExec = mergeChildExec(context.exec, bestMatch.result.next.exec);
1324
- return {
1325
- success: true,
1326
- next: {
1327
- ...context,
1328
- buffer: bestMatch.result.next.buffer,
1329
- optionsTerminated: bestMatch.result.next.optionsTerminated,
1330
- state: createExclusiveState(context.state, bestMatch.index, bestMatch.parser, bestMatch.result),
1331
- ...mergedExec != null ? {
1332
- exec: mergedExec,
1333
- dependencyRegistry: mergedExec.dependencyRegistry
1334
- } : {}
1335
- },
1336
- consumed: bestMatch.result.consumed
1337
- };
1338
- }
1423
+ const optionsTerminatorResolution = resolveLongestMatchOptionsTerminator(context, bestMatch, optionsTerminatorMatches);
1424
+ if (optionsTerminatorResolution != null) return optionsTerminatorResolution;
1425
+ if (bestMatch != null) return commitLongestMatch(context, bestMatch);
1339
1426
  return {
1340
1427
  ...error,
1341
1428
  success: false
1342
1429
  };
1343
1430
  };
1344
- const parseAsync = async (context) => {
1431
+ const parseAsyncBranches = async (context) => {
1345
1432
  let bestMatch = null;
1433
+ const optionsTerminatorMatches = [];
1346
1434
  let error = getInitialError(context);
1347
1435
  const activeState = normalizeExclusiveState(context.state);
1348
1436
  for (let i = 0; i < parsers.length; i++) {
1349
1437
  const parser = parsers[i];
1350
- const resultOrPromise = parser.parse(withChildContext$1(context, i, activeState == null || activeState[0] !== i || !activeState[1].success ? parser.initialState : activeState[1].next.state, parser));
1438
+ const childContext = withChildContext$1(context, i, activeState == null || activeState[0] !== i || !activeState[1].success ? parser.initialState : activeState[1].next.state, parser);
1439
+ const resultOrPromise = parser.parse(childContext);
1351
1440
  const result = await resultOrPromise;
1441
+ if (isOptionsTerminatorOnlyResult(context, result)) {
1442
+ optionsTerminatorMatches.push({
1443
+ index: i,
1444
+ result,
1445
+ consumed: 1,
1446
+ ...normalizeExclusiveState(result.next.state) != null ? { selectedAfterTerminator: true } : {}
1447
+ });
1448
+ continue;
1449
+ }
1352
1450
  if (result.success) {
1353
1451
  const consumed = context.buffer.length - result.next.buffer.length;
1354
1452
  const bestIsProvisional = bestMatch != null && bestMatch.result.success && !!bestMatch.result.provisional;
1355
1453
  if (bestMatch === null || consumed > bestMatch.consumed || consumed === bestMatch.consumed && bestIsProvisional && !result.provisional) bestMatch = {
1356
1454
  index: i,
1357
- parser,
1358
1455
  result,
1359
1456
  consumed
1360
1457
  };
1361
1458
  } else if (error.consumed < result.consumed) error = result;
1362
1459
  }
1363
- if (bestMatch && bestMatch.result.success) {
1364
- const mergedExec = mergeChildExec(context.exec, bestMatch.result.next.exec);
1365
- return {
1366
- success: true,
1367
- next: {
1368
- ...context,
1369
- buffer: bestMatch.result.next.buffer,
1370
- optionsTerminated: bestMatch.result.next.optionsTerminated,
1371
- state: createExclusiveState(context.state, bestMatch.index, bestMatch.parser, bestMatch.result),
1372
- ...mergedExec != null ? {
1373
- exec: mergedExec,
1374
- dependencyRegistry: mergedExec.dependencyRegistry
1375
- } : {}
1376
- },
1377
- consumed: bestMatch.result.consumed
1378
- };
1379
- }
1460
+ const optionsTerminatorResolution = resolveLongestMatchOptionsTerminator(context, bestMatch, optionsTerminatorMatches);
1461
+ if (optionsTerminatorResolution != null) return optionsTerminatorResolution;
1462
+ if (bestMatch != null) return commitLongestMatch(context, bestMatch);
1380
1463
  return {
1381
1464
  ...error,
1382
1465
  success: false
@@ -1399,7 +1482,7 @@ function createLongestMatch(...args) {
1399
1482
  return extractExclusivePhase2Seed(parsers, state, exec, combinedMode);
1400
1483
  },
1401
1484
  parse(context) {
1402
- return dispatchByMode(combinedMode, () => parseSync(context), () => parseAsync(context));
1485
+ return dispatchByMode(combinedMode, () => parseSyncBranches(context), () => parseAsyncBranches(context));
1403
1486
  },
1404
1487
  getSuggestRuntimeNodes(state, path) {
1405
1488
  return getExclusiveSuggestRuntimeNodes(parsers, state, path);
@@ -247,7 +247,7 @@ function* getSuggestionsWithDependency(valueParser, prefix, dependencyRegistry,
247
247
  * Internal sync helper for option suggest functionality.
248
248
  * @internal
249
249
  */
250
- function* suggestOptionSync(optionNames$1, valueParser, hidden, context, prefix) {
250
+ function* suggestOptionSync(optionNames$1, valueParser, hidden, description, context, prefix) {
251
251
  if (hidden) return;
252
252
  const equalsIndex = prefix.indexOf("=");
253
253
  if (equalsIndex >= 0) {
@@ -275,7 +275,8 @@ function* suggestOptionSync(optionNames$1, valueParser, hidden, context, prefix)
275
275
  if (prefix === "-" && optionName$1.length !== 2) continue;
276
276
  yield {
277
277
  kind: "literal",
278
- text: optionName$1
278
+ text: optionName$1,
279
+ ...description && { description }
279
280
  };
280
281
  }
281
282
  }
@@ -331,7 +332,7 @@ async function* getSuggestionsWithDependencyAsync(valueParser, prefix, dependenc
331
332
  * Internal async helper for option suggest functionality.
332
333
  * @internal
333
334
  */
334
- async function* suggestOptionAsync(optionNames$1, valueParser, hidden, context, prefix) {
335
+ async function* suggestOptionAsync(optionNames$1, valueParser, hidden, description, context, prefix) {
335
336
  if (hidden) return;
336
337
  const equalsIndex = prefix.indexOf("=");
337
338
  if (equalsIndex >= 0) {
@@ -359,7 +360,8 @@ async function* suggestOptionAsync(optionNames$1, valueParser, hidden, context,
359
360
  if (prefix === "-" && optionName$1.length !== 2) continue;
360
361
  yield {
361
362
  kind: "literal",
362
- text: optionName$1
363
+ text: optionName$1,
364
+ ...description && { description }
363
365
  };
364
366
  }
365
367
  }
@@ -628,8 +630,8 @@ function option(...args) {
628
630
  return require_mode_dispatch.dispatchByMode(mode, completeSync, completeAsync);
629
631
  },
630
632
  suggest(context, prefix) {
631
- if (isAsync) return suggestOptionAsync(optionNames$1, valueParser, require_usage.isSuggestionHidden(options.hidden), context, prefix);
632
- return suggestOptionSync(optionNames$1, valueParser, require_usage.isSuggestionHidden(options.hidden), context, prefix);
633
+ if (isAsync) return suggestOptionAsync(optionNames$1, valueParser, require_usage.isSuggestionHidden(options.hidden), options.description, context, prefix);
634
+ return suggestOptionSync(optionNames$1, valueParser, require_usage.isSuggestionHidden(options.hidden), options.description, context, prefix);
633
635
  },
634
636
  getDocFragments(_state, defaultValue) {
635
637
  if (require_usage.isDocHidden(options.hidden)) return {
@@ -910,7 +912,8 @@ function flag(...args) {
910
912
  if (prefix === "-" && optionName$1.length !== 2) continue;
911
913
  suggestions.push({
912
914
  kind: "literal",
913
- text: optionName$1
915
+ text: optionName$1,
916
+ ...options.description && { description: options.description }
914
917
  });
915
918
  }
916
919
  }
@@ -1427,10 +1430,11 @@ function* suggestCommandSync(context, prefix, name, aliases, parser, options) {
1427
1430
  if (require_usage.isSuggestionHidden(options.hidden)) return;
1428
1431
  const state = normalizeCommandState(context.state);
1429
1432
  if (state === void 0) {
1433
+ const description = options.brief ?? options.description;
1430
1434
  for (const commandName of [name, ...aliases]) if (commandName.startsWith(prefix)) yield {
1431
1435
  kind: "literal",
1432
1436
  text: commandName,
1433
- ...options.description && { description: options.description }
1437
+ ...description && { description }
1434
1438
  };
1435
1439
  } else if (state[0] === "matched") yield* parser.suggest(require_execution_context.withChildContext(context, name, getCommandChildState(context.state, parser.initialState, parser), parser.usage), prefix);
1436
1440
  else if (state[0] === "parsing") yield* parser.suggest(require_execution_context.withChildContext(context, name, getCommandChildState(context.state, state[1], parser), parser.usage), prefix);
@@ -1439,10 +1443,11 @@ async function* suggestCommandAsync(context, prefix, name, aliases, parser, opti
1439
1443
  if (require_usage.isSuggestionHidden(options.hidden)) return;
1440
1444
  const state = normalizeCommandState(context.state);
1441
1445
  if (state === void 0) {
1446
+ const description = options.brief ?? options.description;
1442
1447
  for (const commandName of [name, ...aliases]) if (commandName.startsWith(prefix)) yield {
1443
1448
  kind: "literal",
1444
1449
  text: commandName,
1445
- ...options.description && { description: options.description }
1450
+ ...description && { description }
1446
1451
  };
1447
1452
  } else if (state[0] === "matched") {
1448
1453
  const suggestions = parser.suggest(require_execution_context.withChildContext(context, name, getCommandChildState(context.state, parser.initialState, parser), parser.usage), prefix);
@@ -247,7 +247,7 @@ function* getSuggestionsWithDependency(valueParser, prefix, dependencyRegistry,
247
247
  * Internal sync helper for option suggest functionality.
248
248
  * @internal
249
249
  */
250
- function* suggestOptionSync(optionNames$1, valueParser, hidden, context, prefix) {
250
+ function* suggestOptionSync(optionNames$1, valueParser, hidden, description, context, prefix) {
251
251
  if (hidden) return;
252
252
  const equalsIndex = prefix.indexOf("=");
253
253
  if (equalsIndex >= 0) {
@@ -275,7 +275,8 @@ function* suggestOptionSync(optionNames$1, valueParser, hidden, context, prefix)
275
275
  if (prefix === "-" && optionName$1.length !== 2) continue;
276
276
  yield {
277
277
  kind: "literal",
278
- text: optionName$1
278
+ text: optionName$1,
279
+ ...description && { description }
279
280
  };
280
281
  }
281
282
  }
@@ -331,7 +332,7 @@ async function* getSuggestionsWithDependencyAsync(valueParser, prefix, dependenc
331
332
  * Internal async helper for option suggest functionality.
332
333
  * @internal
333
334
  */
334
- async function* suggestOptionAsync(optionNames$1, valueParser, hidden, context, prefix) {
335
+ async function* suggestOptionAsync(optionNames$1, valueParser, hidden, description, context, prefix) {
335
336
  if (hidden) return;
336
337
  const equalsIndex = prefix.indexOf("=");
337
338
  if (equalsIndex >= 0) {
@@ -359,7 +360,8 @@ async function* suggestOptionAsync(optionNames$1, valueParser, hidden, context,
359
360
  if (prefix === "-" && optionName$1.length !== 2) continue;
360
361
  yield {
361
362
  kind: "literal",
362
- text: optionName$1
363
+ text: optionName$1,
364
+ ...description && { description }
363
365
  };
364
366
  }
365
367
  }
@@ -628,8 +630,8 @@ function option(...args) {
628
630
  return dispatchByMode(mode, completeSync, completeAsync);
629
631
  },
630
632
  suggest(context, prefix) {
631
- if (isAsync) return suggestOptionAsync(optionNames$1, valueParser, isSuggestionHidden(options.hidden), context, prefix);
632
- return suggestOptionSync(optionNames$1, valueParser, isSuggestionHidden(options.hidden), context, prefix);
633
+ if (isAsync) return suggestOptionAsync(optionNames$1, valueParser, isSuggestionHidden(options.hidden), options.description, context, prefix);
634
+ return suggestOptionSync(optionNames$1, valueParser, isSuggestionHidden(options.hidden), options.description, context, prefix);
633
635
  },
634
636
  getDocFragments(_state, defaultValue) {
635
637
  if (isDocHidden(options.hidden)) return {
@@ -910,7 +912,8 @@ function flag(...args) {
910
912
  if (prefix === "-" && optionName$1.length !== 2) continue;
911
913
  suggestions.push({
912
914
  kind: "literal",
913
- text: optionName$1
915
+ text: optionName$1,
916
+ ...options.description && { description: options.description }
914
917
  });
915
918
  }
916
919
  }
@@ -1427,10 +1430,11 @@ function* suggestCommandSync(context, prefix, name, aliases, parser, options) {
1427
1430
  if (isSuggestionHidden(options.hidden)) return;
1428
1431
  const state = normalizeCommandState(context.state);
1429
1432
  if (state === void 0) {
1433
+ const description = options.brief ?? options.description;
1430
1434
  for (const commandName of [name, ...aliases]) if (commandName.startsWith(prefix)) yield {
1431
1435
  kind: "literal",
1432
1436
  text: commandName,
1433
- ...options.description && { description: options.description }
1437
+ ...description && { description }
1434
1438
  };
1435
1439
  } else if (state[0] === "matched") yield* parser.suggest(withChildContext(context, name, getCommandChildState(context.state, parser.initialState, parser), parser.usage), prefix);
1436
1440
  else if (state[0] === "parsing") yield* parser.suggest(withChildContext(context, name, getCommandChildState(context.state, state[1], parser), parser.usage), prefix);
@@ -1439,10 +1443,11 @@ async function* suggestCommandAsync(context, prefix, name, aliases, parser, opti
1439
1443
  if (isSuggestionHidden(options.hidden)) return;
1440
1444
  const state = normalizeCommandState(context.state);
1441
1445
  if (state === void 0) {
1446
+ const description = options.brief ?? options.description;
1442
1447
  for (const commandName of [name, ...aliases]) if (commandName.startsWith(prefix)) yield {
1443
1448
  kind: "literal",
1444
1449
  text: commandName,
1445
- ...options.description && { description: options.description }
1450
+ ...description && { description }
1446
1451
  };
1447
1452
  } else if (state[0] === "matched") {
1448
1453
  const suggestions = parser.suggest(withChildContext(context, name, getCommandChildState(context.state, parser.initialState, parser), parser.usage), prefix);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/core",
3
- "version": "1.1.1",
3
+ "version": "1.1.3",
4
4
  "description": "Type-safe combinatorial command-line interface parser",
5
5
  "keywords": [
6
6
  "CLI",
@@ -200,7 +200,7 @@
200
200
  "fast-check": "^4.7.0",
201
201
  "tsdown": "^0.13.0",
202
202
  "typescript": "^5.8.3",
203
- "@optique/env": "1.1.1"
203
+ "@optique/env": "1.1.3"
204
204
  },
205
205
  "scripts": {
206
206
  "build": "tsdown",