@optique/core 1.4.0-dev.2604 → 1.4.0-dev.2607

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/facade.js CHANGED
@@ -12,6 +12,7 @@ import { collectExplicitSourceValues, collectExplicitSourceValuesAsync, createDe
12
12
  import { createInputTrace } from "./input-trace.js";
13
13
  import { attemptParseAsync, attemptParseSync, createEffectfulCompletionSession, createParserContext, getDocPage } from "./internal/parser.js";
14
14
  import { bash, fish, nu, pwsh, zsh } from "./completion.js";
15
+ import { collectActiveOptionNames, collectRootOptionNames } from "./usage-internals.js";
15
16
  import { allowDuplicateLeadingCommandNamesKey, hiddenCommandAliasesKey } from "./internal/command-alias.js";
16
17
  import { completeOrExtractPhase2Seed } from "./phase2-seed.js";
17
18
  import { multiple, optional, withDefault } from "./modifiers.js";
@@ -756,76 +757,9 @@ function collectActiveValueOptionNames(usage, commandPath, includeDirectAfterCom
756
757
  for (const name of optionNames.flag) names.delete(name);
757
758
  return names;
758
759
  }
759
- function collectRootOptionNames(usage, names, rootLeadingNames) {
760
- for (const term of usage) collectRootOptionNamesFromTerm(term, names, rootLeadingNames);
761
- }
762
- function collectRootOptionNamesFromTerm(term, names, rootLeadingNames) {
763
- switch (term.type) {
764
- case "option":
765
- for (const name of term.names) {
766
- if (!rootLeadingNames.has(name)) continue;
767
- if (term.metavar != null) names.value.add(name);
768
- else names.flag.add(name);
769
- }
770
- return;
771
- case "optional":
772
- case "multiple":
773
- case "sequence":
774
- collectRootOptionNames(term.terms, names, rootLeadingNames);
775
- return;
776
- case "exclusive":
777
- for (const branch of term.terms) collectRootOptionNames(branch, names, rootLeadingNames);
778
- return;
779
- case "argument":
780
- case "command":
781
- case "literal":
782
- case "passthrough":
783
- case "ellipsis": return;
784
- }
785
- }
786
- function collectActiveOptionNames(usage, commandPath, names, fromExclusive, includeDirectAfterCommandOptions) {
787
- if (commandPath.length === 0) {
788
- collectOptionNamesAtCurrentCommandDepth(usage, names, false);
789
- return;
790
- }
791
- const [commandName, ...rest] = commandPath;
792
- for (let i = 0; i < usage.length; i++) {
793
- const term = usage[i];
794
- if (term.type === "command" && term.name === commandName) {
795
- const remainingUsage = usage.slice(i + 1);
796
- if (rest.length === 0) collectOptionNamesAtCurrentCommandDepth(remainingUsage, names, !fromExclusive && includeDirectAfterCommandOptions);
797
- else collectActiveOptionNames(remainingUsage, rest, names, fromExclusive, includeDirectAfterCommandOptions);
798
- } else if (term.type === "exclusive") for (const branch of term.terms) collectActiveOptionNames(branch, commandPath, names, true, includeDirectAfterCommandOptions);
799
- else if (term.type === "optional" || term.type === "multiple" || term.type === "sequence") collectActiveOptionNames(term.terms, commandPath, names, fromExclusive, includeDirectAfterCommandOptions);
800
- }
801
- }
802
760
  function isRootOptionToken(token, rootOptionSuggestions) {
803
761
  return token != null && rootOptionSuggestions.some((suggestion) => token === suggestion.text || token.startsWith(`${suggestion.text}=`));
804
762
  }
805
- function collectOptionNamesAtCurrentCommandDepth(usage, names, afterMatchedCommand) {
806
- for (const term of usage) if (collectOptionNamesAtCurrentCommandDepthFromTerm(term, names, afterMatchedCommand)) return;
807
- }
808
- function collectOptionNamesAtCurrentCommandDepthFromTerm(term, names, afterMatchedCommand) {
809
- switch (term.type) {
810
- case "command": return !afterMatchedCommand;
811
- case "option":
812
- if (term.metavar != null) for (const name of term.names) names.value.add(name);
813
- else for (const name of term.names) names.flag.add(name);
814
- return false;
815
- case "optional":
816
- case "multiple":
817
- case "sequence":
818
- collectOptionNamesAtCurrentCommandDepth(term.terms, names, afterMatchedCommand);
819
- return false;
820
- case "exclusive":
821
- for (const branch of term.terms) collectOptionNamesAtCurrentCommandDepth(branch, names, false);
822
- return false;
823
- case "argument":
824
- case "literal":
825
- case "passthrough":
826
- case "ellipsis": return false;
827
- }
828
- }
829
763
  function getRootMetaOptionSuggestions(helpOptionConfig, helpOptionNames, versionOptionConfig, versionOptionNames) {
830
764
  const suggestions = [];
831
765
  if (helpOptionConfig != null && !isSuggestionHidden(helpOptionConfig.hidden)) suggestions.push(...helpOptionNames.map((name) => ({
@@ -4,6 +4,7 @@ const require_mode_dispatch = require('./internal/mode-dispatch.cjs');
4
4
  const require_dependency_runtime = require('./dependency-runtime.cjs');
5
5
  const require_internal_parser = require('./internal/parser.cjs');
6
6
  const require_annotation_state = require('./annotation-state.cjs');
7
+ const require_short_option = require('./short-option.cjs');
7
8
  const require_execution_context = require('./execution-context.cjs');
8
9
  const require_phase2_seed = require('./phase2-seed.cjs');
9
10
  const require_dependency_metadata = require('./dependency-metadata.cjs');
@@ -478,7 +479,7 @@ function optional(parser) {
478
479
  require_internal_parser.defineParseLanes(optionalParser, adaptOptionalStyleParseLanes(parser));
479
480
  require_internal_parser.defineInheritedAnnotationParser(optionalParser);
480
481
  require_internal_parser.defineSourceBindingOnlyAnnotationCompletionParser(optionalParser);
481
- return fluent(optionalParser);
482
+ return fluent(require_short_option.scopeParser(optionalParser, require_short_option.combinedOptionScope([parser])));
482
483
  }
483
484
  /**
484
485
  * Error type for structured error messages in {@link withDefault} default value callbacks.
@@ -729,7 +730,7 @@ function withDefault(parser, defaultValue, options) {
729
730
  require_internal_parser.defineParseLanes(withDefaultParser, adaptOptionalStyleParseLanes(parser));
730
731
  require_internal_parser.defineInheritedAnnotationParser(withDefaultParser);
731
732
  require_internal_parser.defineSourceBindingOnlyAnnotationCompletionParser(withDefaultParser);
732
- return fluent(withDefaultParser);
733
+ return fluent(require_short_option.scopeParser(withDefaultParser, require_short_option.combinedOptionScope([parser])));
733
734
  }
734
735
  /**
735
736
  * Creates a parser that transforms the result value of another parser using
@@ -918,7 +919,7 @@ function map(parser, transform) {
918
919
  if (composed != null) mappedParser.dependencyMetadata = composed;
919
920
  }
920
921
  require_dependency_runtime.defineForwardedEffectfulSchedulingNodes(mappedParser, parser);
921
- return fluent(mappedParser);
922
+ return fluent(require_short_option.scopeParser(mappedParser, require_short_option.combinedOptionScope([parser])));
922
923
  }
923
924
  const deferredValueBrand = Symbol.for("@optique/core/deferredValue");
924
925
  /**
@@ -1714,7 +1715,7 @@ function multiple(parser, options = {}) {
1714
1715
  enumerable: false
1715
1716
  });
1716
1717
  }
1717
- return fluent(resultParser);
1718
+ return fluent(require_short_option.scopeParser(resultParser, require_short_option.combinedOptionScope([parser])));
1718
1719
  }
1719
1720
  /**
1720
1721
  * Creates a parser that requires the wrapped parser to consume at least one
@@ -1850,7 +1851,7 @@ function nonEmpty(parser) {
1850
1851
  enumerable: false
1851
1852
  });
1852
1853
  require_dependency_runtime.defineForwardedEffectfulSchedulingNodes(nonEmptyParser, parser);
1853
- return fluent(nonEmptyParser);
1854
+ return fluent(require_short_option.scopeParser(nonEmptyParser, require_short_option.combinedOptionScope([parser])));
1854
1855
  }
1855
1856
  const fluentParserMarker = Symbol.for("@optique/core/fluent");
1856
1857
  /**
package/dist/modifiers.js CHANGED
@@ -4,6 +4,7 @@ import { dispatchByMode, dispatchIterableByMode, mapModeValue, wrapForMode } fro
4
4
  import { defineForwardedEffectfulSchedulingNodes } from "./dependency-runtime.js";
5
5
  import { defineInheritedAnnotationParser, defineParseLanes, defineSourceBindingOnlyAnnotationCompletionParser, getOwnParseLanes, unmatchedNonCliDependencySourceStateMarker } from "./internal/parser.js";
6
6
  import { getDelegatedAnnotationState, hasDelegatedAnnotationCarrier, isAnnotationWrappedInitialState, normalizeDelegatedAnnotationState, normalizeNestedDelegatedAnnotationState } from "./annotation-state.js";
7
+ import { combinedOptionScope, scopeParser } from "./short-option.js";
7
8
  import { mergeChildExec, withChildContext, withChildExecPath } from "./execution-context.js";
8
9
  import { completeOrExtractPhase2Seed, extractPhase2Seed, extractPhase2SeedKey, phase2SeedFromValueResult } from "./phase2-seed.js";
9
10
  import { composeDependencyMetadata } from "./dependency-metadata.js";
@@ -478,7 +479,7 @@ function optional(parser) {
478
479
  defineParseLanes(optionalParser, adaptOptionalStyleParseLanes(parser));
479
480
  defineInheritedAnnotationParser(optionalParser);
480
481
  defineSourceBindingOnlyAnnotationCompletionParser(optionalParser);
481
- return fluent(optionalParser);
482
+ return fluent(scopeParser(optionalParser, combinedOptionScope([parser])));
482
483
  }
483
484
  /**
484
485
  * Error type for structured error messages in {@link withDefault} default value callbacks.
@@ -729,7 +730,7 @@ function withDefault(parser, defaultValue, options) {
729
730
  defineParseLanes(withDefaultParser, adaptOptionalStyleParseLanes(parser));
730
731
  defineInheritedAnnotationParser(withDefaultParser);
731
732
  defineSourceBindingOnlyAnnotationCompletionParser(withDefaultParser);
732
- return fluent(withDefaultParser);
733
+ return fluent(scopeParser(withDefaultParser, combinedOptionScope([parser])));
733
734
  }
734
735
  /**
735
736
  * Creates a parser that transforms the result value of another parser using
@@ -918,7 +919,7 @@ function map(parser, transform) {
918
919
  if (composed != null) mappedParser.dependencyMetadata = composed;
919
920
  }
920
921
  defineForwardedEffectfulSchedulingNodes(mappedParser, parser);
921
- return fluent(mappedParser);
922
+ return fluent(scopeParser(mappedParser, combinedOptionScope([parser])));
922
923
  }
923
924
  const deferredValueBrand = Symbol.for("@optique/core/deferredValue");
924
925
  /**
@@ -1714,7 +1715,7 @@ function multiple(parser, options = {}) {
1714
1715
  enumerable: false
1715
1716
  });
1716
1717
  }
1717
- return fluent(resultParser);
1718
+ return fluent(scopeParser(resultParser, combinedOptionScope([parser])));
1718
1719
  }
1719
1720
  /**
1720
1721
  * Creates a parser that requires the wrapped parser to consume at least one
@@ -1850,7 +1851,7 @@ function nonEmpty(parser) {
1850
1851
  enumerable: false
1851
1852
  });
1852
1853
  defineForwardedEffectfulSchedulingNodes(nonEmptyParser, parser);
1853
- return fluent(nonEmptyParser);
1854
+ return fluent(scopeParser(nonEmptyParser, combinedOptionScope([parser])));
1854
1855
  }
1855
1856
  const fluentParserMarker = Symbol.for("@optique/core/fluent");
1856
1857
  /**
@@ -6,13 +6,14 @@ const require_mode_dispatch = require('./internal/mode-dispatch.cjs');
6
6
  const require_internal_dependency = require('./internal/dependency.cjs');
7
7
  const require_dependency_runtime = require('./dependency-runtime.cjs');
8
8
  const require_annotation_state = require('./annotation-state.cjs');
9
+ const require_usage_internals = require('./usage-internals.cjs');
10
+ const require_short_option = require('./short-option.cjs');
9
11
  const require_command_alias = require('./internal/command-alias.cjs');
10
12
  const require_execution_context = require('./execution-context.cjs');
11
13
  const require_phase2_seed = require('./phase2-seed.cjs');
12
14
  const require_dependency_metadata = require('./dependency-metadata.cjs');
13
15
  const require_modifiers = require('./modifiers.cjs');
14
16
  const require_suggestion = require('./suggestion.cjs');
15
- const require_usage_internals = require('./usage-internals.cjs');
16
17
  const require_valueparser = require('./valueparser.cjs');
17
18
 
18
19
  //#region src/primitives.ts
@@ -256,11 +257,25 @@ function* getSuggestionsWithDependency(valueParser, prefix, dependencyRegistry,
256
257
  */
257
258
  function* suggestOptionSync(optionNames$1, valueParser, hidden, description, context, prefix) {
258
259
  if (hidden) return;
260
+ if (context.optionsTerminated) return;
261
+ const waitingForValue = context.buffer.length > 0 && optionNames$1.includes(context.buffer[context.buffer.length - 1]);
262
+ if (valueParser != null && !waitingForValue && !isTerminalValueState(context.state)) {
263
+ const attached = require_short_option.attachedValuePrefix(context, prefix, optionNames$1);
264
+ if (attached != null) {
265
+ if (attached.value.length === 0 && optionNames$1.includes(prefix)) yield {
266
+ kind: "literal",
267
+ text: prefix,
268
+ ...description != null ? { description } : {}
269
+ };
270
+ if (valueParser.suggest != null) for (const suggestion of getSuggestionsWithDependency(valueParser, attached.value, context.dependencyRegistry, context.exec)) yield require_short_option.prefixSuggestion(attached.head, suggestion);
271
+ return;
272
+ }
273
+ }
259
274
  const equalsIndex = prefix.indexOf("=");
260
- if (equalsIndex >= 0) {
275
+ if (equalsIndex >= 0 && !waitingForValue) {
261
276
  const optionPart = prefix.slice(0, equalsIndex);
262
277
  const valuePart = prefix.slice(equalsIndex + 1);
263
- if (optionNames$1.includes(optionPart)) {
278
+ if (optionNames$1.includes(optionPart) && !/^-[^-]$/.test(optionPart)) {
264
279
  if (valueParser && valueParser.suggest) {
265
280
  const valueSuggestions = getSuggestionsWithDependency(valueParser, valuePart, context.dependencyRegistry, context.exec);
266
281
  for (const suggestion of valueSuggestions) if (suggestion.kind === "literal") yield {
@@ -341,11 +356,25 @@ async function* getSuggestionsWithDependencyAsync(valueParser, prefix, dependenc
341
356
  */
342
357
  async function* suggestOptionAsync(optionNames$1, valueParser, hidden, description, context, prefix) {
343
358
  if (hidden) return;
359
+ if (context.optionsTerminated) return;
360
+ const waitingForValue = context.buffer.length > 0 && optionNames$1.includes(context.buffer[context.buffer.length - 1]);
361
+ if (valueParser != null && !waitingForValue && !isTerminalValueState(context.state)) {
362
+ const attached = require_short_option.attachedValuePrefix(context, prefix, optionNames$1);
363
+ if (attached != null) {
364
+ if (attached.value.length === 0 && optionNames$1.includes(prefix)) yield {
365
+ kind: "literal",
366
+ text: prefix,
367
+ ...description != null ? { description } : {}
368
+ };
369
+ if (valueParser.suggest != null) for await (const suggestion of getSuggestionsWithDependencyAsync(valueParser, attached.value, context.dependencyRegistry, context.exec)) yield require_short_option.prefixSuggestion(attached.head, suggestion);
370
+ return;
371
+ }
372
+ }
344
373
  const equalsIndex = prefix.indexOf("=");
345
- if (equalsIndex >= 0) {
374
+ if (equalsIndex >= 0 && !waitingForValue) {
346
375
  const optionPart = prefix.slice(0, equalsIndex);
347
376
  const valuePart = prefix.slice(equalsIndex + 1);
348
- if (optionNames$1.includes(optionPart)) {
377
+ if (optionNames$1.includes(optionPart) && !/^-[^-]$/.test(optionPart)) {
349
378
  if (valueParser && valueParser.suggest) {
350
379
  const valueSuggestions = getSuggestionsWithDependencyAsync(valueParser, valuePart, context.dependencyRegistry, context.exec);
351
380
  for await (const suggestion of valueSuggestions) if (suggestion.kind === "literal") yield {
@@ -524,10 +553,18 @@ function option(...args) {
524
553
  };
525
554
  });
526
555
  }
527
- const prefixes = optionNames$1.filter((name) => name.startsWith("--") || name.startsWith("/") || name.startsWith("-") && name.length > 2).map((name) => name.startsWith("/") ? `${name}:` : `${name}=`);
528
- for (const prefix of prefixes) {
556
+ const prefixes = optionNames$1.filter((name) => name.startsWith("--") || name.startsWith("/") || name.startsWith("-") && name.length > 2).map((name) => ({
557
+ optionName: name,
558
+ prefix: name.startsWith("/") ? `${name}:` : `${name}=`
559
+ }));
560
+ if (valueParser != null && optionNames$1.some((name) => /^-[^-]$/.test(name) && context.buffer[0].startsWith(name)) && !require_short_option.fullNameOwnsToken(context, context.buffer[0])) {
561
+ for (const name of optionNames$1) if (/^-[^-]$/.test(name)) prefixes.push({
562
+ optionName: name,
563
+ prefix: name
564
+ });
565
+ }
566
+ for (const { prefix, optionName: optionName$1 } of prefixes) {
529
567
  if (!context.buffer[0].startsWith(prefix)) continue;
530
- const optionName$1 = prefix.slice(0, -1);
531
568
  if (hasParsedOptionValue(context.state, valueParser)) return {
532
569
  success: false,
533
570
  consumed: 1,
@@ -568,7 +605,7 @@ function option(...args) {
568
605
  if (valueParser == null) {
569
606
  const shortOptions = optionNames$1.filter((name) => name.match(/^-[^-]$/));
570
607
  for (const shortOption of shortOptions) {
571
- if (!context.buffer[0].startsWith(shortOption)) continue;
608
+ if (!context.buffer[0].startsWith(shortOption) || require_short_option.fullNameOwnsToken(context, context.buffer[0])) continue;
572
609
  if (hasParsedOptionValue(context.state, valueParser)) return {
573
610
  success: false,
574
611
  consumed: 1,
@@ -857,7 +894,7 @@ function flag(...args) {
857
894
  }
858
895
  const shortOptions = optionNames$1.filter((name) => name.match(/^-[^-]$/));
859
896
  for (const shortOption of shortOptions) {
860
- if (!context.buffer[0].startsWith(shortOption)) continue;
897
+ if (!context.buffer[0].startsWith(shortOption) || require_short_option.fullNameOwnsToken(context, context.buffer[0])) continue;
861
898
  if (context.state?.success) return {
862
899
  success: false,
863
900
  consumed: 1,
@@ -1078,7 +1115,7 @@ function negatableFlag(names, options = {}) {
1078
1115
  }
1079
1116
  for (const shortOption of optionNames$1) {
1080
1117
  if (!shortOption.match(/^-[^-]$/)) continue;
1081
- if (!context.buffer[0].startsWith(shortOption)) continue;
1118
+ if (!context.buffer[0].startsWith(shortOption) || require_short_option.fullNameOwnsToken(context, context.buffer[0])) continue;
1082
1119
  return parseMatchedNegatableFlag(context, shortOption, valueByName.get(shortOption), [context.buffer[0].slice(0, 2)], 1, [`-${context.buffer[0].slice(2)}`, ...context.buffer.slice(1)], options);
1083
1120
  }
1084
1121
  const invalidOption = context.buffer[0];
@@ -1434,6 +1471,7 @@ function validateUniqueCommandNames(names) {
1434
1471
  function* suggestCommandSync(context, prefix, name, aliases, parser, options) {
1435
1472
  if (require_usage.isSuggestionHidden(options.hidden)) return;
1436
1473
  const state = normalizeCommandState(context.state);
1474
+ if (state != null) context = require_short_option.withParserOptionScope(context, parser);
1437
1475
  if (state === void 0) {
1438
1476
  const description = options.brief ?? options.description;
1439
1477
  for (const commandName of [name, ...aliases]) if (commandName.startsWith(prefix)) yield {
@@ -1447,6 +1485,7 @@ function* suggestCommandSync(context, prefix, name, aliases, parser, options) {
1447
1485
  async function* suggestCommandAsync(context, prefix, name, aliases, parser, options) {
1448
1486
  if (require_usage.isSuggestionHidden(options.hidden)) return;
1449
1487
  const state = normalizeCommandState(context.state);
1488
+ if (state != null) context = require_short_option.withParserOptionScope(context, parser);
1450
1489
  if (state === void 0) {
1451
1490
  const description = options.brief ?? options.description;
1452
1491
  for (const commandName of [name, ...aliases]) if (commandName.startsWith(prefix)) yield {
@@ -1487,6 +1526,8 @@ function command(name, parser, options = {}) {
1487
1526
  const isAsync = parser.mode === "async";
1488
1527
  const syncInnerParser = parser;
1489
1528
  const asyncInnerParser = parser;
1529
+ const commandScope = require_short_option.selectableOptionScope(() => new Set(commandNames));
1530
+ const enteredScope = require_short_option.combinedOptionScope([parser]);
1490
1531
  const result = {
1491
1532
  [Symbol.for("@optique/core/commandParser")]: true,
1492
1533
  mode: parser.mode,
@@ -1554,6 +1595,7 @@ function command(name, parser, options = {}) {
1554
1595
  ] : baseError
1555
1596
  };
1556
1597
  }
1598
+ commandScope.select(context, enteredScope);
1557
1599
  return {
1558
1600
  success: true,
1559
1601
  next: {
@@ -1584,7 +1626,7 @@ function command(name, parser, options = {}) {
1584
1626
  }
1585
1627
  return parseResult;
1586
1628
  };
1587
- return require_mode_dispatch.dispatchByMode(parser.mode, () => wrapState(syncInnerParser.parse(require_execution_context.withChildContext(context, name, innerState, parser.usage))), async () => wrapState(await parser.parse(require_execution_context.withChildContext(context, name, innerState, parser.usage))));
1629
+ return require_mode_dispatch.dispatchByMode(parser.mode, () => wrapState(syncInnerParser.parse(require_execution_context.withChildContext(require_short_option.withParserOptionScope(context, parser), name, innerState, parser.usage))), async () => wrapState(await parser.parse(require_execution_context.withChildContext(require_short_option.withParserOptionScope(context, parser), name, innerState, parser.usage))));
1588
1630
  }
1589
1631
  return {
1590
1632
  success: false,
@@ -1734,7 +1776,9 @@ function command(name, parser, options = {}) {
1734
1776
  configurable: true,
1735
1777
  enumerable: false
1736
1778
  });
1737
- return require_modifiers.fluent(result);
1779
+ return require_modifiers.fluent(require_short_option.scopeParser(result, commandScope.source, (context) => {
1780
+ if (normalizeCommandState(context.state) != null) commandScope.select(context, enteredScope);
1781
+ }));
1738
1782
  }
1739
1783
  /**
1740
1784
  * Creates a parser that collects unrecognized options and passes them through.
@@ -104,7 +104,7 @@ interface OptionErrorOptions {
104
104
  /**
105
105
  * Creates a parser for various styles of command-line options that take an
106
106
  * argument value, such as `--option=value`, `-option=value`, `-o value`,
107
- * or `/option:value`.
107
+ * `-ovalue`, or `/option:value`.
108
108
  * @template M The execution mode of the parser.
109
109
  * @template T The type of value this parser produces.
110
110
  * @param args The {@link OptionName}s to parse, followed by
@@ -118,7 +118,7 @@ declare function option<M extends Mode, T>(...args: readonly [OptionName, ...rea
118
118
  /**
119
119
  * Creates a parser for various styles of command-line options that take an
120
120
  * argument value, such as `--option=value`, `-option=value`, `-o value`,
121
- * or `/option:value`.
121
+ * `-ovalue`, or `/option:value`.
122
122
  * @template M The execution mode of the parser.
123
123
  * @template T The type of value this parser produces.
124
124
  * @param args The {@link OptionName}s to parse, followed by
@@ -140,7 +140,7 @@ declare function option(...optionNames: readonly [OptionName, ...readonly Option
140
140
  /**
141
141
  * Creates a parser for various styles of command-line options that take an
142
142
  * argument value, such as `--option=value`, `-option=value`, `-o value`,
143
- * or `/option:value`.
143
+ * `-ovalue`, or `/option:value`.
144
144
  * @param args The {@link OptionName}s to parse, followed by
145
145
  * an optional {@link OptionOptions} object that allows you to
146
146
  * specify a description or other metadata.
@@ -104,7 +104,7 @@ interface OptionErrorOptions {
104
104
  /**
105
105
  * Creates a parser for various styles of command-line options that take an
106
106
  * argument value, such as `--option=value`, `-option=value`, `-o value`,
107
- * or `/option:value`.
107
+ * `-ovalue`, or `/option:value`.
108
108
  * @template M The execution mode of the parser.
109
109
  * @template T The type of value this parser produces.
110
110
  * @param args The {@link OptionName}s to parse, followed by
@@ -118,7 +118,7 @@ declare function option<M extends Mode, T>(...args: readonly [OptionName, ...rea
118
118
  /**
119
119
  * Creates a parser for various styles of command-line options that take an
120
120
  * argument value, such as `--option=value`, `-option=value`, `-o value`,
121
- * or `/option:value`.
121
+ * `-ovalue`, or `/option:value`.
122
122
  * @template M The execution mode of the parser.
123
123
  * @template T The type of value this parser produces.
124
124
  * @param args The {@link OptionName}s to parse, followed by
@@ -140,7 +140,7 @@ declare function option(...optionNames: readonly [OptionName, ...readonly Option
140
140
  /**
141
141
  * Creates a parser for various styles of command-line options that take an
142
142
  * argument value, such as `--option=value`, `-option=value`, `-o value`,
143
- * or `/option:value`.
143
+ * `-ovalue`, or `/option:value`.
144
144
  * @param args The {@link OptionName}s to parse, followed by
145
145
  * an optional {@link OptionOptions} object that allows you to
146
146
  * specify a description or other metadata.
@@ -6,13 +6,14 @@ import { dispatchByMode, dispatchIterableByMode, wrapForMode } from "./internal/
6
6
  import { getDefaultValuesFunction, getDependencyIds, getSnapshottedDefaultDependencyValues, isDerivedValueParser, suggestWithDependency } from "./internal/dependency.js";
7
7
  import { effectfulSchedulingNodesKey, exclusiveSourceScopeKey, includeSourceFailureChain, recordDerivedRawInput, replayDerivedParser, replayDerivedParserAsync, sourceCollectionExpansionKey } from "./dependency-runtime.js";
8
8
  import { getWrappedChildParseState, getWrappedChildState, isAnnotationWrappedInitialState, normalizeInjectedAnnotationState } from "./annotation-state.js";
9
+ import { extractLeadingCommandNames } from "./usage-internals.js";
10
+ import { attachedValuePrefix, combinedOptionScope, fullNameOwnsToken, prefixSuggestion, scopeParser, selectableOptionScope, withParserOptionScope } from "./short-option.js";
9
11
  import { hiddenCommandAliasesKey } from "./internal/command-alias.js";
10
12
  import { mergeChildExec, withChildContext, withChildExecPath } from "./execution-context.js";
11
13
  import { completeOrExtractPhase2Seed, extractPhase2SeedKey } from "./phase2-seed.js";
12
14
  import { extractDependencyMetadata } from "./dependency-metadata.js";
13
15
  import { fluent } from "./modifiers.js";
14
16
  import { DEFAULT_FIND_SIMILAR_OPTIONS, createErrorWithSuggestions, createSuggestionMessage, expandCommandAliasSuggestions, findSimilar } from "./suggestion.js";
15
- import { extractLeadingCommandNames } from "./usage-internals.js";
16
17
  import { isValueParser } from "./valueparser.js";
17
18
 
18
19
  //#region src/primitives.ts
@@ -256,11 +257,25 @@ function* getSuggestionsWithDependency(valueParser, prefix, dependencyRegistry,
256
257
  */
257
258
  function* suggestOptionSync(optionNames$1, valueParser, hidden, description, context, prefix) {
258
259
  if (hidden) return;
260
+ if (context.optionsTerminated) return;
261
+ const waitingForValue = context.buffer.length > 0 && optionNames$1.includes(context.buffer[context.buffer.length - 1]);
262
+ if (valueParser != null && !waitingForValue && !isTerminalValueState(context.state)) {
263
+ const attached = attachedValuePrefix(context, prefix, optionNames$1);
264
+ if (attached != null) {
265
+ if (attached.value.length === 0 && optionNames$1.includes(prefix)) yield {
266
+ kind: "literal",
267
+ text: prefix,
268
+ ...description != null ? { description } : {}
269
+ };
270
+ if (valueParser.suggest != null) for (const suggestion of getSuggestionsWithDependency(valueParser, attached.value, context.dependencyRegistry, context.exec)) yield prefixSuggestion(attached.head, suggestion);
271
+ return;
272
+ }
273
+ }
259
274
  const equalsIndex = prefix.indexOf("=");
260
- if (equalsIndex >= 0) {
275
+ if (equalsIndex >= 0 && !waitingForValue) {
261
276
  const optionPart = prefix.slice(0, equalsIndex);
262
277
  const valuePart = prefix.slice(equalsIndex + 1);
263
- if (optionNames$1.includes(optionPart)) {
278
+ if (optionNames$1.includes(optionPart) && !/^-[^-]$/.test(optionPart)) {
264
279
  if (valueParser && valueParser.suggest) {
265
280
  const valueSuggestions = getSuggestionsWithDependency(valueParser, valuePart, context.dependencyRegistry, context.exec);
266
281
  for (const suggestion of valueSuggestions) if (suggestion.kind === "literal") yield {
@@ -341,11 +356,25 @@ async function* getSuggestionsWithDependencyAsync(valueParser, prefix, dependenc
341
356
  */
342
357
  async function* suggestOptionAsync(optionNames$1, valueParser, hidden, description, context, prefix) {
343
358
  if (hidden) return;
359
+ if (context.optionsTerminated) return;
360
+ const waitingForValue = context.buffer.length > 0 && optionNames$1.includes(context.buffer[context.buffer.length - 1]);
361
+ if (valueParser != null && !waitingForValue && !isTerminalValueState(context.state)) {
362
+ const attached = attachedValuePrefix(context, prefix, optionNames$1);
363
+ if (attached != null) {
364
+ if (attached.value.length === 0 && optionNames$1.includes(prefix)) yield {
365
+ kind: "literal",
366
+ text: prefix,
367
+ ...description != null ? { description } : {}
368
+ };
369
+ if (valueParser.suggest != null) for await (const suggestion of getSuggestionsWithDependencyAsync(valueParser, attached.value, context.dependencyRegistry, context.exec)) yield prefixSuggestion(attached.head, suggestion);
370
+ return;
371
+ }
372
+ }
344
373
  const equalsIndex = prefix.indexOf("=");
345
- if (equalsIndex >= 0) {
374
+ if (equalsIndex >= 0 && !waitingForValue) {
346
375
  const optionPart = prefix.slice(0, equalsIndex);
347
376
  const valuePart = prefix.slice(equalsIndex + 1);
348
- if (optionNames$1.includes(optionPart)) {
377
+ if (optionNames$1.includes(optionPart) && !/^-[^-]$/.test(optionPart)) {
349
378
  if (valueParser && valueParser.suggest) {
350
379
  const valueSuggestions = getSuggestionsWithDependencyAsync(valueParser, valuePart, context.dependencyRegistry, context.exec);
351
380
  for await (const suggestion of valueSuggestions) if (suggestion.kind === "literal") yield {
@@ -524,10 +553,18 @@ function option(...args) {
524
553
  };
525
554
  });
526
555
  }
527
- const prefixes = optionNames$1.filter((name) => name.startsWith("--") || name.startsWith("/") || name.startsWith("-") && name.length > 2).map((name) => name.startsWith("/") ? `${name}:` : `${name}=`);
528
- for (const prefix of prefixes) {
556
+ const prefixes = optionNames$1.filter((name) => name.startsWith("--") || name.startsWith("/") || name.startsWith("-") && name.length > 2).map((name) => ({
557
+ optionName: name,
558
+ prefix: name.startsWith("/") ? `${name}:` : `${name}=`
559
+ }));
560
+ if (valueParser != null && optionNames$1.some((name) => /^-[^-]$/.test(name) && context.buffer[0].startsWith(name)) && !fullNameOwnsToken(context, context.buffer[0])) {
561
+ for (const name of optionNames$1) if (/^-[^-]$/.test(name)) prefixes.push({
562
+ optionName: name,
563
+ prefix: name
564
+ });
565
+ }
566
+ for (const { prefix, optionName: optionName$1 } of prefixes) {
529
567
  if (!context.buffer[0].startsWith(prefix)) continue;
530
- const optionName$1 = prefix.slice(0, -1);
531
568
  if (hasParsedOptionValue(context.state, valueParser)) return {
532
569
  success: false,
533
570
  consumed: 1,
@@ -568,7 +605,7 @@ function option(...args) {
568
605
  if (valueParser == null) {
569
606
  const shortOptions = optionNames$1.filter((name) => name.match(/^-[^-]$/));
570
607
  for (const shortOption of shortOptions) {
571
- if (!context.buffer[0].startsWith(shortOption)) continue;
608
+ if (!context.buffer[0].startsWith(shortOption) || fullNameOwnsToken(context, context.buffer[0])) continue;
572
609
  if (hasParsedOptionValue(context.state, valueParser)) return {
573
610
  success: false,
574
611
  consumed: 1,
@@ -857,7 +894,7 @@ function flag(...args) {
857
894
  }
858
895
  const shortOptions = optionNames$1.filter((name) => name.match(/^-[^-]$/));
859
896
  for (const shortOption of shortOptions) {
860
- if (!context.buffer[0].startsWith(shortOption)) continue;
897
+ if (!context.buffer[0].startsWith(shortOption) || fullNameOwnsToken(context, context.buffer[0])) continue;
861
898
  if (context.state?.success) return {
862
899
  success: false,
863
900
  consumed: 1,
@@ -1078,7 +1115,7 @@ function negatableFlag(names, options = {}) {
1078
1115
  }
1079
1116
  for (const shortOption of optionNames$1) {
1080
1117
  if (!shortOption.match(/^-[^-]$/)) continue;
1081
- if (!context.buffer[0].startsWith(shortOption)) continue;
1118
+ if (!context.buffer[0].startsWith(shortOption) || fullNameOwnsToken(context, context.buffer[0])) continue;
1082
1119
  return parseMatchedNegatableFlag(context, shortOption, valueByName.get(shortOption), [context.buffer[0].slice(0, 2)], 1, [`-${context.buffer[0].slice(2)}`, ...context.buffer.slice(1)], options);
1083
1120
  }
1084
1121
  const invalidOption = context.buffer[0];
@@ -1434,6 +1471,7 @@ function validateUniqueCommandNames(names) {
1434
1471
  function* suggestCommandSync(context, prefix, name, aliases, parser, options) {
1435
1472
  if (isSuggestionHidden(options.hidden)) return;
1436
1473
  const state = normalizeCommandState(context.state);
1474
+ if (state != null) context = withParserOptionScope(context, parser);
1437
1475
  if (state === void 0) {
1438
1476
  const description = options.brief ?? options.description;
1439
1477
  for (const commandName of [name, ...aliases]) if (commandName.startsWith(prefix)) yield {
@@ -1447,6 +1485,7 @@ function* suggestCommandSync(context, prefix, name, aliases, parser, options) {
1447
1485
  async function* suggestCommandAsync(context, prefix, name, aliases, parser, options) {
1448
1486
  if (isSuggestionHidden(options.hidden)) return;
1449
1487
  const state = normalizeCommandState(context.state);
1488
+ if (state != null) context = withParserOptionScope(context, parser);
1450
1489
  if (state === void 0) {
1451
1490
  const description = options.brief ?? options.description;
1452
1491
  for (const commandName of [name, ...aliases]) if (commandName.startsWith(prefix)) yield {
@@ -1487,6 +1526,8 @@ function command(name, parser, options = {}) {
1487
1526
  const isAsync = parser.mode === "async";
1488
1527
  const syncInnerParser = parser;
1489
1528
  const asyncInnerParser = parser;
1529
+ const commandScope = selectableOptionScope(() => new Set(commandNames));
1530
+ const enteredScope = combinedOptionScope([parser]);
1490
1531
  const result = {
1491
1532
  [Symbol.for("@optique/core/commandParser")]: true,
1492
1533
  mode: parser.mode,
@@ -1554,6 +1595,7 @@ function command(name, parser, options = {}) {
1554
1595
  ] : baseError
1555
1596
  };
1556
1597
  }
1598
+ commandScope.select(context, enteredScope);
1557
1599
  return {
1558
1600
  success: true,
1559
1601
  next: {
@@ -1584,7 +1626,7 @@ function command(name, parser, options = {}) {
1584
1626
  }
1585
1627
  return parseResult;
1586
1628
  };
1587
- return dispatchByMode(parser.mode, () => wrapState(syncInnerParser.parse(withChildContext(context, name, innerState, parser.usage))), async () => wrapState(await parser.parse(withChildContext(context, name, innerState, parser.usage))));
1629
+ return dispatchByMode(parser.mode, () => wrapState(syncInnerParser.parse(withChildContext(withParserOptionScope(context, parser), name, innerState, parser.usage))), async () => wrapState(await parser.parse(withChildContext(withParserOptionScope(context, parser), name, innerState, parser.usage))));
1588
1630
  }
1589
1631
  return {
1590
1632
  success: false,
@@ -1734,7 +1776,9 @@ function command(name, parser, options = {}) {
1734
1776
  configurable: true,
1735
1777
  enumerable: false
1736
1778
  });
1737
- return fluent(result);
1779
+ return fluent(scopeParser(result, commandScope.source, (context) => {
1780
+ if (normalizeCommandState(context.state) != null) commandScope.select(context, enteredScope);
1781
+ }));
1738
1782
  }
1739
1783
  /**
1740
1784
  * Creates a parser that collects unrecognized options and passes them through.