@optique/core 1.0.16-dev.2947 → 1.0.16-dev.2975

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.
@@ -0,0 +1,47 @@
1
+ //#region src/internal/command-usage.ts
2
+ const commandUsageIds = /* @__PURE__ */ new WeakMap();
3
+ /** Associates a command usage leaf with its parser occurrence. @internal */
4
+ function identifyCommandUsage(term, id) {
5
+ commandUsageIds.set(term, id);
6
+ return term;
7
+ }
8
+ /** Reads a command usage leaf's private identity. @internal */
9
+ function getCommandUsageId(term) {
10
+ return commandUsageIds.get(term);
11
+ }
12
+ /** Preserves private identity when cloning a usage leaf. @internal */
13
+ function copyCommandUsageId(source, target) {
14
+ const id = commandUsageIds.get(source);
15
+ if (id != null) commandUsageIds.set(target, id);
16
+ return target;
17
+ }
18
+ /** Distinguishes repeated uses of one parser in different children. @internal */
19
+ function scopeCommandUsageId(scope, id) {
20
+ let scoped = scope.get(id);
21
+ if (scoped == null) {
22
+ scoped = {};
23
+ scope.set(id, scoped);
24
+ }
25
+ return scoped;
26
+ }
27
+ /** Copies usage with identities scoped to this child occurrence. @internal */
28
+ function scopeCommandUsage(usage, scope) {
29
+ return usage.map((term) => {
30
+ if (term.type === "command") {
31
+ const id = getCommandUsageId(term);
32
+ return id == null ? term : identifyCommandUsage({ ...term }, scopeCommandUsageId(scope, id));
33
+ }
34
+ if (term.type === "optional" || term.type === "multiple") return {
35
+ ...term,
36
+ terms: scopeCommandUsage(term.terms, scope)
37
+ };
38
+ if (term.type === "exclusive") return {
39
+ ...term,
40
+ terms: term.terms.map((branch) => scopeCommandUsage(branch, scope))
41
+ };
42
+ return term;
43
+ });
44
+ }
45
+
46
+ //#endregion
47
+ export { copyCommandUsageId, getCommandUsageId, identifyCommandUsage, scopeCommandUsage, scopeCommandUsageId };
@@ -1,5 +1,7 @@
1
1
  const require_annotations = require('./annotations.cjs');
2
2
  const require_message = require('../message.cjs');
3
+ const require_command_usage = require('./command-usage.cjs');
4
+ const require_command_doc_metadata = require('./command-doc-metadata.cjs');
3
5
  const require_usage = require('../usage.cjs');
4
6
  const require_doc = require('../doc.cjs');
5
7
  const require_mode_dispatch = require('./mode-dispatch.cjs');
@@ -527,20 +529,66 @@ function suggest(parser, args, options) {
527
529
  *
528
530
  * @param term The usage term to search in
529
531
  * @param commandName The command name to find
532
+ * @param selectedCommandUsage The command occurrences selected by parser state
530
533
  * @returns The expanded usage terms if found, null otherwise
531
534
  */
532
- function findCommandInExclusive(term, commandName) {
535
+ function findCommandInExclusive(term, commandName, selectedCommandUsage) {
536
+ return findCommandInExclusiveMatching(term, commandName, selectedCommandUsage) ?? findCommandInExclusiveMatching(term, commandName, selectedCommandUsage, false);
537
+ }
538
+ function findCommandInExclusiveMatching(term, commandName, selectedCommandUsage, requireSelectedIdentity = true) {
533
539
  if (term.type !== "exclusive") return null;
534
540
  for (const termGroup of term.terms) {
535
541
  const firstTerm = termGroup[0];
536
- if (firstTerm?.type === "command" && firstTerm.name === commandName) return termGroup;
542
+ if (firstTerm?.type === "command" && firstTerm.name === commandName) {
543
+ const id = require_command_usage.getCommandUsageId(firstTerm);
544
+ if (id == null ? !requireSelectedIdentity : selectedCommandUsage.has(id)) return termGroup;
545
+ }
546
+ if (requireSelectedIdentity && (firstTerm?.type === "optional" || firstTerm?.type === "multiple") && hasSelectedLeadingCommand(firstTerm.terms, commandName, selectedCommandUsage)) return termGroup;
537
547
  if (firstTerm?.type === "exclusive") {
538
- const found = findCommandInExclusive(firstTerm, commandName);
548
+ const found = findCommandInExclusiveMatching(firstTerm, commandName, selectedCommandUsage, requireSelectedIdentity);
539
549
  if (found) return [...found, ...termGroup.slice(1)];
540
550
  }
541
551
  }
542
552
  return null;
543
553
  }
554
+ function hasSelectedLeadingCommand(usage, commandName, selectedCommandUsage) {
555
+ const first = usage[0];
556
+ if (first?.type === "command") {
557
+ const id = require_command_usage.getCommandUsageId(first);
558
+ return first.name === commandName && id != null && selectedCommandUsage.has(id);
559
+ }
560
+ if (first?.type === "optional" || first?.type === "multiple") return hasSelectedLeadingCommand(first.terms, commandName, selectedCommandUsage);
561
+ return first?.type === "exclusive" && first.terms.some((branch) => hasSelectedLeadingCommand(branch, commandName, selectedCommandUsage));
562
+ }
563
+ function applyWrappedCommandUsageLine(term, commandName, selectedCommandUsage) {
564
+ const candidates = [];
565
+ const visit = (terms, rebuild) => {
566
+ const first = terms[0];
567
+ if (first?.type === "command") {
568
+ const id = require_command_usage.getCommandUsageId(first);
569
+ if (first.name === commandName && id != null && selectedCommandUsage.has(id)) candidates.push({
570
+ command: first,
571
+ tail: terms.slice(1),
572
+ replace: (tail$1) => rebuild([first, ...tail$1])
573
+ });
574
+ } else if (first?.type === "optional" || first?.type === "multiple") visit(first.terms, (children) => rebuild([{
575
+ ...first,
576
+ terms: children
577
+ }, ...terms.slice(1)]));
578
+ else if (first?.type === "exclusive") first.terms.forEach((branch, index) => {
579
+ visit(branch, (children) => rebuild([{
580
+ ...first,
581
+ terms: first.terms.map((other, otherIndex) => otherIndex === index ? children : other)
582
+ }, ...terms.slice(1)]));
583
+ });
584
+ };
585
+ visit([term], (terms) => terms[0]);
586
+ if (candidates.length !== 1) return term;
587
+ const { command, tail, replace } = candidates[0];
588
+ if (command.usageLine == null) return term;
589
+ const customUsageLine = typeof command.usageLine === "function" ? command.usageLine(require_usage.cloneUsage(tail)) : command.usageLine;
590
+ return replace(require_usage.normalizeUsage(customUsageLine));
591
+ }
544
592
  /**
545
593
  * Generates a documentation page for a synchronous parser.
546
594
  *
@@ -651,10 +699,13 @@ async function getDocPageAsyncImpl(parser, args, options) {
651
699
  */
652
700
  function buildDocPage(parser, context, args) {
653
701
  let effectiveArgs = args;
654
- let { brief, description, fragments, footer } = parser.getDocFragments({
702
+ let matchedCommandPath = context.exec?.commandPath ?? [];
703
+ const docs = parser.getDocFragments({
655
704
  kind: "available",
656
705
  state: context.state
657
706
  }, void 0);
707
+ let selectedCommandUsage = require_command_doc_metadata.getSelectedCommandUsage(require_command_doc_metadata.getCommandDocMetadata(docs));
708
+ let { brief, description, fragments, footer } = docs;
658
709
  if (args.length === 0 && Reflect.get(parser, Symbol.for("@optique/core/commandParser")) === true && fragments.length === 1 && fragments[0].type === "entry" && fragments[0].term.type === "command") {
659
710
  const cmdName = fragments[0].term.name;
660
711
  const matchedState = require_annotations.inheritAnnotations(context.state, ["matched", cmdName]);
@@ -663,7 +714,9 @@ function buildDocPage(parser, context, args) {
663
714
  state: matchedState
664
715
  }, void 0);
665
716
  ({brief, description, fragments, footer} = matched);
717
+ selectedCommandUsage = require_command_doc_metadata.getSelectedCommandUsage(require_command_doc_metadata.getCommandDocMetadata(matched));
666
718
  effectiveArgs = [cmdName];
719
+ matchedCommandPath = [cmdName];
667
720
  }
668
721
  const buildingSections = [];
669
722
  let untitledSection = null;
@@ -698,9 +751,15 @@ function buildDocPage(parser, context, args) {
698
751
  }
699
752
  }
700
753
  const sections = buildingSections;
754
+ const selectedCommandUsageIds = new Set(selectedCommandUsage);
701
755
  const usage = [...require_usage.normalizeUsage(parser.usage)];
702
756
  const maybeApplyCommandUsageLine = (term, arg, isLastArg, usageIndex) => {
703
- if (term?.type !== "command" || term.name !== arg || !isLastArg || term.usageLine == null) return;
757
+ if (!isLastArg) return;
758
+ if (term?.type === "optional" || term?.type === "multiple" || term?.type === "exclusive") {
759
+ usage[usageIndex] = applyWrappedCommandUsageLine(term, arg, selectedCommandUsageIds);
760
+ return;
761
+ }
762
+ if (term?.type !== "command" || term.name !== arg || term.usageLine == null) return;
704
763
  const defaultUsageLine = require_usage.cloneUsage(usage.slice(usageIndex + 1));
705
764
  const customUsageLine = typeof term.usageLine === "function" ? term.usageLine(defaultUsageLine) : term.usageLine;
706
765
  const normalizedCustomUsageLine = require_usage.normalizeUsage(customUsageLine);
@@ -712,7 +771,7 @@ function buildDocPage(parser, context, args) {
712
771
  if (i >= usage.length) break;
713
772
  let term = usage[i];
714
773
  if (term.type === "exclusive") {
715
- const found = findCommandInExclusive(term, arg);
774
+ const found = findCommandInExclusive(term, arg, selectedCommandUsageIds);
716
775
  if (found) {
717
776
  usage.splice(i, 1, ...found);
718
777
  term = usage[i];
@@ -731,13 +790,58 @@ function buildDocPage(parser, context, args) {
731
790
  }
732
791
  }
733
792
  return {
734
- usage,
793
+ usage: revealMatchedCommandUsage(usage, matchedCommandPath, selectedCommandUsageIds),
735
794
  sections,
736
795
  ...brief != null && { brief: require_message.cloneMessage(brief) },
737
796
  ...description != null && { description: require_message.cloneMessage(description) },
738
797
  ...footer != null && { footer: require_message.cloneMessage(footer) }
739
798
  };
740
799
  }
800
+ function revealMatchedCommandUsage(usage, matchedCommandPath, selectedCommandUsage) {
801
+ return revealMatchedCommandUsageTerms(usage, matchedCommandPath, 0, selectedCommandUsage)[0];
802
+ }
803
+ function revealMatchedCommandUsageTerms(usage, matchedCommandPath, pathIndex, selectedCommandUsage, allowNameFallback = true) {
804
+ const terms = [];
805
+ let nextPathIndex = pathIndex;
806
+ let nameFallbackEnabled = allowNameFallback;
807
+ for (let index = 0; index < usage.length; index++) {
808
+ const term = usage[index];
809
+ if (term.type === "command") {
810
+ const id = require_command_usage.getCommandUsageId(term);
811
+ if (id != null) {
812
+ if (selectedCommandUsage.has(id)) {
813
+ const { hidden: _hidden$1,...revealed$1 } = term;
814
+ terms.push(revealed$1);
815
+ if (term.name === matchedCommandPath[nextPathIndex]) nextPathIndex++;
816
+ } else terms.push(term);
817
+ continue;
818
+ }
819
+ if (!nameFallbackEnabled || term.name !== matchedCommandPath[nextPathIndex]) {
820
+ nameFallbackEnabled = false;
821
+ terms.push(term);
822
+ continue;
823
+ }
824
+ const { hidden: _hidden,...revealed } = term;
825
+ terms.push(revealed);
826
+ nextPathIndex++;
827
+ } else if (term.type === "optional" || term.type === "multiple") {
828
+ const [nested, afterTerm] = revealMatchedCommandUsageTerms(term.terms, matchedCommandPath, nextPathIndex, selectedCommandUsage, nameFallbackEnabled);
829
+ terms.push({
830
+ ...term,
831
+ terms: nested
832
+ });
833
+ nextPathIndex = afterTerm;
834
+ } else if (term.type === "exclusive") {
835
+ const branches = term.terms.map((branch) => revealMatchedCommandUsageTerms(branch, matchedCommandPath, nextPathIndex, selectedCommandUsage, nameFallbackEnabled));
836
+ terms.push({
837
+ ...term,
838
+ terms: branches.map(([branch]) => branch)
839
+ });
840
+ nextPathIndex = Math.max(nextPathIndex, ...branches.map(([, afterBranch]) => afterBranch));
841
+ } else terms.push(term);
842
+ }
843
+ return [terms, nextPathIndex];
844
+ }
741
845
 
742
846
  //#endregion
743
847
  exports.annotationWrapperRequiresSourceBindingKey = annotationWrapperRequiresSourceBindingKey;
@@ -1,5 +1,7 @@
1
1
  import { hasMeaningfulAnnotations, inheritAnnotations, injectAnnotations, isInjectedAnnotationWrapper, unwrapInjectedAnnotationWrapper } from "./annotations.js";
2
2
  import { cloneMessage, message } from "../message.js";
3
+ import { getCommandUsageId } from "./command-usage.js";
4
+ import { getCommandDocMetadata, getSelectedCommandUsage } from "./command-doc-metadata.js";
3
5
  import { cloneUsage, normalizeUsage } from "../usage.js";
4
6
  import { cloneDocEntry, isDocEntryHidden } from "../doc.js";
5
7
  import { dispatchByMode } from "./mode-dispatch.js";
@@ -527,20 +529,66 @@ function suggest(parser, args, options) {
527
529
  *
528
530
  * @param term The usage term to search in
529
531
  * @param commandName The command name to find
532
+ * @param selectedCommandUsage The command occurrences selected by parser state
530
533
  * @returns The expanded usage terms if found, null otherwise
531
534
  */
532
- function findCommandInExclusive(term, commandName) {
535
+ function findCommandInExclusive(term, commandName, selectedCommandUsage) {
536
+ return findCommandInExclusiveMatching(term, commandName, selectedCommandUsage) ?? findCommandInExclusiveMatching(term, commandName, selectedCommandUsage, false);
537
+ }
538
+ function findCommandInExclusiveMatching(term, commandName, selectedCommandUsage, requireSelectedIdentity = true) {
533
539
  if (term.type !== "exclusive") return null;
534
540
  for (const termGroup of term.terms) {
535
541
  const firstTerm = termGroup[0];
536
- if (firstTerm?.type === "command" && firstTerm.name === commandName) return termGroup;
542
+ if (firstTerm?.type === "command" && firstTerm.name === commandName) {
543
+ const id = getCommandUsageId(firstTerm);
544
+ if (id == null ? !requireSelectedIdentity : selectedCommandUsage.has(id)) return termGroup;
545
+ }
546
+ if (requireSelectedIdentity && (firstTerm?.type === "optional" || firstTerm?.type === "multiple") && hasSelectedLeadingCommand(firstTerm.terms, commandName, selectedCommandUsage)) return termGroup;
537
547
  if (firstTerm?.type === "exclusive") {
538
- const found = findCommandInExclusive(firstTerm, commandName);
548
+ const found = findCommandInExclusiveMatching(firstTerm, commandName, selectedCommandUsage, requireSelectedIdentity);
539
549
  if (found) return [...found, ...termGroup.slice(1)];
540
550
  }
541
551
  }
542
552
  return null;
543
553
  }
554
+ function hasSelectedLeadingCommand(usage, commandName, selectedCommandUsage) {
555
+ const first = usage[0];
556
+ if (first?.type === "command") {
557
+ const id = getCommandUsageId(first);
558
+ return first.name === commandName && id != null && selectedCommandUsage.has(id);
559
+ }
560
+ if (first?.type === "optional" || first?.type === "multiple") return hasSelectedLeadingCommand(first.terms, commandName, selectedCommandUsage);
561
+ return first?.type === "exclusive" && first.terms.some((branch) => hasSelectedLeadingCommand(branch, commandName, selectedCommandUsage));
562
+ }
563
+ function applyWrappedCommandUsageLine(term, commandName, selectedCommandUsage) {
564
+ const candidates = [];
565
+ const visit = (terms, rebuild) => {
566
+ const first = terms[0];
567
+ if (first?.type === "command") {
568
+ const id = getCommandUsageId(first);
569
+ if (first.name === commandName && id != null && selectedCommandUsage.has(id)) candidates.push({
570
+ command: first,
571
+ tail: terms.slice(1),
572
+ replace: (tail$1) => rebuild([first, ...tail$1])
573
+ });
574
+ } else if (first?.type === "optional" || first?.type === "multiple") visit(first.terms, (children) => rebuild([{
575
+ ...first,
576
+ terms: children
577
+ }, ...terms.slice(1)]));
578
+ else if (first?.type === "exclusive") first.terms.forEach((branch, index) => {
579
+ visit(branch, (children) => rebuild([{
580
+ ...first,
581
+ terms: first.terms.map((other, otherIndex) => otherIndex === index ? children : other)
582
+ }, ...terms.slice(1)]));
583
+ });
584
+ };
585
+ visit([term], (terms) => terms[0]);
586
+ if (candidates.length !== 1) return term;
587
+ const { command, tail, replace } = candidates[0];
588
+ if (command.usageLine == null) return term;
589
+ const customUsageLine = typeof command.usageLine === "function" ? command.usageLine(cloneUsage(tail)) : command.usageLine;
590
+ return replace(normalizeUsage(customUsageLine));
591
+ }
544
592
  /**
545
593
  * Generates a documentation page for a synchronous parser.
546
594
  *
@@ -651,10 +699,13 @@ async function getDocPageAsyncImpl(parser, args, options) {
651
699
  */
652
700
  function buildDocPage(parser, context, args) {
653
701
  let effectiveArgs = args;
654
- let { brief, description, fragments, footer } = parser.getDocFragments({
702
+ let matchedCommandPath = context.exec?.commandPath ?? [];
703
+ const docs = parser.getDocFragments({
655
704
  kind: "available",
656
705
  state: context.state
657
706
  }, void 0);
707
+ let selectedCommandUsage = getSelectedCommandUsage(getCommandDocMetadata(docs));
708
+ let { brief, description, fragments, footer } = docs;
658
709
  if (args.length === 0 && Reflect.get(parser, Symbol.for("@optique/core/commandParser")) === true && fragments.length === 1 && fragments[0].type === "entry" && fragments[0].term.type === "command") {
659
710
  const cmdName = fragments[0].term.name;
660
711
  const matchedState = inheritAnnotations(context.state, ["matched", cmdName]);
@@ -663,7 +714,9 @@ function buildDocPage(parser, context, args) {
663
714
  state: matchedState
664
715
  }, void 0);
665
716
  ({brief, description, fragments, footer} = matched);
717
+ selectedCommandUsage = getSelectedCommandUsage(getCommandDocMetadata(matched));
666
718
  effectiveArgs = [cmdName];
719
+ matchedCommandPath = [cmdName];
667
720
  }
668
721
  const buildingSections = [];
669
722
  let untitledSection = null;
@@ -698,9 +751,15 @@ function buildDocPage(parser, context, args) {
698
751
  }
699
752
  }
700
753
  const sections = buildingSections;
754
+ const selectedCommandUsageIds = new Set(selectedCommandUsage);
701
755
  const usage = [...normalizeUsage(parser.usage)];
702
756
  const maybeApplyCommandUsageLine = (term, arg, isLastArg, usageIndex) => {
703
- if (term?.type !== "command" || term.name !== arg || !isLastArg || term.usageLine == null) return;
757
+ if (!isLastArg) return;
758
+ if (term?.type === "optional" || term?.type === "multiple" || term?.type === "exclusive") {
759
+ usage[usageIndex] = applyWrappedCommandUsageLine(term, arg, selectedCommandUsageIds);
760
+ return;
761
+ }
762
+ if (term?.type !== "command" || term.name !== arg || term.usageLine == null) return;
704
763
  const defaultUsageLine = cloneUsage(usage.slice(usageIndex + 1));
705
764
  const customUsageLine = typeof term.usageLine === "function" ? term.usageLine(defaultUsageLine) : term.usageLine;
706
765
  const normalizedCustomUsageLine = normalizeUsage(customUsageLine);
@@ -712,7 +771,7 @@ function buildDocPage(parser, context, args) {
712
771
  if (i >= usage.length) break;
713
772
  let term = usage[i];
714
773
  if (term.type === "exclusive") {
715
- const found = findCommandInExclusive(term, arg);
774
+ const found = findCommandInExclusive(term, arg, selectedCommandUsageIds);
716
775
  if (found) {
717
776
  usage.splice(i, 1, ...found);
718
777
  term = usage[i];
@@ -731,13 +790,58 @@ function buildDocPage(parser, context, args) {
731
790
  }
732
791
  }
733
792
  return {
734
- usage,
793
+ usage: revealMatchedCommandUsage(usage, matchedCommandPath, selectedCommandUsageIds),
735
794
  sections,
736
795
  ...brief != null && { brief: cloneMessage(brief) },
737
796
  ...description != null && { description: cloneMessage(description) },
738
797
  ...footer != null && { footer: cloneMessage(footer) }
739
798
  };
740
799
  }
800
+ function revealMatchedCommandUsage(usage, matchedCommandPath, selectedCommandUsage) {
801
+ return revealMatchedCommandUsageTerms(usage, matchedCommandPath, 0, selectedCommandUsage)[0];
802
+ }
803
+ function revealMatchedCommandUsageTerms(usage, matchedCommandPath, pathIndex, selectedCommandUsage, allowNameFallback = true) {
804
+ const terms = [];
805
+ let nextPathIndex = pathIndex;
806
+ let nameFallbackEnabled = allowNameFallback;
807
+ for (let index = 0; index < usage.length; index++) {
808
+ const term = usage[index];
809
+ if (term.type === "command") {
810
+ const id = getCommandUsageId(term);
811
+ if (id != null) {
812
+ if (selectedCommandUsage.has(id)) {
813
+ const { hidden: _hidden$1,...revealed$1 } = term;
814
+ terms.push(revealed$1);
815
+ if (term.name === matchedCommandPath[nextPathIndex]) nextPathIndex++;
816
+ } else terms.push(term);
817
+ continue;
818
+ }
819
+ if (!nameFallbackEnabled || term.name !== matchedCommandPath[nextPathIndex]) {
820
+ nameFallbackEnabled = false;
821
+ terms.push(term);
822
+ continue;
823
+ }
824
+ const { hidden: _hidden,...revealed } = term;
825
+ terms.push(revealed);
826
+ nextPathIndex++;
827
+ } else if (term.type === "optional" || term.type === "multiple") {
828
+ const [nested, afterTerm] = revealMatchedCommandUsageTerms(term.terms, matchedCommandPath, nextPathIndex, selectedCommandUsage, nameFallbackEnabled);
829
+ terms.push({
830
+ ...term,
831
+ terms: nested
832
+ });
833
+ nextPathIndex = afterTerm;
834
+ } else if (term.type === "exclusive") {
835
+ const branches = term.terms.map((branch) => revealMatchedCommandUsageTerms(branch, matchedCommandPath, nextPathIndex, selectedCommandUsage, nameFallbackEnabled));
836
+ terms.push({
837
+ ...term,
838
+ terms: branches.map(([branch]) => branch)
839
+ });
840
+ nextPathIndex = Math.max(nextPathIndex, ...branches.map(([, afterBranch]) => afterBranch));
841
+ } else terms.push(term);
842
+ }
843
+ return [terms, nextPathIndex];
844
+ }
741
845
 
742
846
  //#endregion
743
847
  export { annotationWrapperRequiresSourceBindingKey, composeWrappedSourceMetadata, createParserContext, defineInheritedAnnotationParser, defineParseLanes, defineSourceBindingOnlyAnnotationCompletionParser, getDelegatingSuggestRuntimeNodes, getDocPage, getDocPageAsync, getDocPageSync, getOwnParseLanes, getParserSuggestRuntimeNodes, inheritParentAnnotationsKey, parse, parseAsync, parseSync, suggest, suggestAsync, suggestSync, unmatchedNonCliDependencySourceStateMarker };
package/dist/message.cjs CHANGED
@@ -306,7 +306,8 @@ function formatMessage(msg, options = {}) {
306
306
  const isAfterLineBreak = prevWasLineBreak;
307
307
  prevWasLineBreak = false;
308
308
  if (term.type === "text") {
309
- const rawText = isAfterLineBreak ? term.text.replace(/^\n(?!\n)/, "") : term.text;
309
+ const textWithLf = term.text.replace(/\r\n?/g, "\n");
310
+ const rawText = isAfterLineBreak ? textWithLf.replace(/^\n(?!\n)/, "") : textWithLf;
310
311
  if (rawText.includes("\n\n")) {
311
312
  const paragraphs = rawText.split(/\n\n+/);
312
313
  for (let paragraphIndex = 0; paragraphIndex < paragraphs.length; paragraphIndex++) {
@@ -332,8 +333,8 @@ function formatMessage(msg, options = {}) {
332
333
  } else {
333
334
  const normalizedText = rawText.replace(/\n/g, " ");
334
335
  if (normalizedText.trim() === "" && normalizedText.length > 0) yield {
335
- text: " ",
336
- width: 1,
336
+ text: normalizedText,
337
+ width: require_displaywidth.getDisplayWidth(normalizedText),
337
338
  trimOnWrap: true
338
339
  };
339
340
  else {
package/dist/message.js CHANGED
@@ -306,7 +306,8 @@ function formatMessage(msg, options = {}) {
306
306
  const isAfterLineBreak = prevWasLineBreak;
307
307
  prevWasLineBreak = false;
308
308
  if (term.type === "text") {
309
- const rawText = isAfterLineBreak ? term.text.replace(/^\n(?!\n)/, "") : term.text;
309
+ const textWithLf = term.text.replace(/\r\n?/g, "\n");
310
+ const rawText = isAfterLineBreak ? textWithLf.replace(/^\n(?!\n)/, "") : textWithLf;
310
311
  if (rawText.includes("\n\n")) {
311
312
  const paragraphs = rawText.split(/\n\n+/);
312
313
  for (let paragraphIndex = 0; paragraphIndex < paragraphs.length; paragraphIndex++) {
@@ -332,8 +333,8 @@ function formatMessage(msg, options = {}) {
332
333
  } else {
333
334
  const normalizedText = rawText.replace(/\n/g, " ");
334
335
  if (normalizedText.trim() === "" && normalizedText.length > 0) yield {
335
- text: " ",
336
- width: 1,
336
+ text: normalizedText,
337
+ width: getDisplayWidth(normalizedText),
337
338
  trimOnWrap: true
338
339
  };
339
340
  else {
@@ -8,6 +8,7 @@ const require_parser = require('./internal/parser.cjs');
8
8
  const require_annotation_state = require('./annotation-state.cjs');
9
9
  const require_execution_context = require('./execution-context.cjs');
10
10
  const require_phase2_seed = require('./phase2-seed.cjs');
11
+ const require_extension = require('./extension.cjs');
11
12
  const require_dependency_metadata = require('./dependency-metadata.cjs');
12
13
 
13
14
  //#region src/modifiers.ts
@@ -828,6 +829,7 @@ function map(parser, transform) {
828
829
  }
829
830
  };
830
831
  require_parser.defineParseLanes(mappedParser, require_parser.getOwnParseLanes(parser));
832
+ require_extension.defineTraits(mappedParser, require_extension.getTraits(parser));
831
833
  delete mappedParser.normalizeValue;
832
834
  delete mappedParser.validateValue;
833
835
  if ("placeholder" in parser) Object.defineProperty(mappedParser, "placeholder", {
@@ -1346,7 +1348,19 @@ function multiple(parser, options = {}) {
1346
1348
  kind: "available",
1347
1349
  state: latestInnerState
1348
1350
  } : { kind: "unavailable" };
1349
- return syncParser.getDocFragments(innerState, defaultValue != null && defaultValue.length > 0 ? defaultValue[0] : void 0);
1351
+ const docs = syncParser.getDocFragments(innerState, defaultValue != null && defaultValue.length > 0 ? defaultValue[0] : void 0);
1352
+ if (state.kind === "unavailable" || state.state.length < 2) return docs;
1353
+ const metadata = require_command_doc_metadata.getCommandDocMetadata(docs);
1354
+ const selected = [...require_command_doc_metadata.getSelectedCommandUsage(metadata)];
1355
+ for (const itemState of state.state.slice(0, -1)) {
1356
+ const itemInnerState = itemState != null && require_annotations.isInjectedAnnotationWrapper(itemState) ? unwrapInjectedWrapper(itemState) : itemState;
1357
+ const itemDocs = syncParser.getDocFragments(itemInnerState === void 0 ? { kind: "unavailable" } : {
1358
+ kind: "available",
1359
+ state: itemInnerState
1360
+ }, defaultValue != null && defaultValue.length > 0 ? defaultValue[0] : void 0);
1361
+ selected.push(...require_command_doc_metadata.getSelectedCommandUsage(require_command_doc_metadata.getCommandDocMetadata(itemDocs)));
1362
+ }
1363
+ return require_command_doc_metadata.withCommandDocMetadata(docs, require_command_doc_metadata.withSelectedCommandUsage(metadata, selected));
1350
1364
  }
1351
1365
  };
1352
1366
  function validateMultipleResult(result, deferredIndices, hasDeferred = false) {
package/dist/modifiers.js CHANGED
@@ -1,13 +1,14 @@
1
1
  import { annotateFreshArray, annotationKey, getAnnotations, inheritAnnotations, isInjectedAnnotationWrapper, unwrapInjectedAnnotationWrapper } from "./internal/annotations.js";
2
2
  import { mergeJoinedFileSuggestions } from "./internal/joined-file-suggestion.js";
3
3
  import { formatMessage, message, text } from "./message.js";
4
- import { getCommandDocMetadata, withCommandDocMetadata } from "./internal/command-doc-metadata.js";
4
+ import { getCommandDocMetadata, getSelectedCommandUsage, withCommandDocMetadata, withSelectedCommandUsage } from "./internal/command-doc-metadata.js";
5
5
  import { defineKnownCompletionLookup, defineOptionMatch, definePassThroughPriority, getKnownCompletion, getOptionMatch, getPassThroughPriority } from "./internal/passthrough.js";
6
6
  import { dispatchByMode, dispatchIterableByMode, mapModeValue, wrapForMode } from "./internal/mode-dispatch.js";
7
7
  import { defineInheritedAnnotationParser, defineParseLanes, defineSourceBindingOnlyAnnotationCompletionParser, getOwnParseLanes, unmatchedNonCliDependencySourceStateMarker } from "./internal/parser.js";
8
8
  import { getDelegatedAnnotationState, hasDelegatedAnnotationCarrier, isAnnotationWrappedInitialState, normalizeDelegatedAnnotationState, normalizeNestedDelegatedAnnotationState } from "./annotation-state.js";
9
9
  import { mergeChildExec, withChildContext, withChildExecPath } from "./execution-context.js";
10
10
  import { completeOrExtractPhase2Seed, extractPhase2Seed, extractPhase2SeedKey, phase2SeedFromValueResult } from "./phase2-seed.js";
11
+ import { defineTraits, getTraits } from "./extension.js";
11
12
  import { composeDependencyMetadata } from "./dependency-metadata.js";
12
13
 
13
14
  //#region src/modifiers.ts
@@ -828,6 +829,7 @@ function map(parser, transform) {
828
829
  }
829
830
  };
830
831
  defineParseLanes(mappedParser, getOwnParseLanes(parser));
832
+ defineTraits(mappedParser, getTraits(parser));
831
833
  delete mappedParser.normalizeValue;
832
834
  delete mappedParser.validateValue;
833
835
  if ("placeholder" in parser) Object.defineProperty(mappedParser, "placeholder", {
@@ -1346,7 +1348,19 @@ function multiple(parser, options = {}) {
1346
1348
  kind: "available",
1347
1349
  state: latestInnerState
1348
1350
  } : { kind: "unavailable" };
1349
- return syncParser.getDocFragments(innerState, defaultValue != null && defaultValue.length > 0 ? defaultValue[0] : void 0);
1351
+ const docs = syncParser.getDocFragments(innerState, defaultValue != null && defaultValue.length > 0 ? defaultValue[0] : void 0);
1352
+ if (state.kind === "unavailable" || state.state.length < 2) return docs;
1353
+ const metadata = getCommandDocMetadata(docs);
1354
+ const selected = [...getSelectedCommandUsage(metadata)];
1355
+ for (const itemState of state.state.slice(0, -1)) {
1356
+ const itemInnerState = itemState != null && isInjectedAnnotationWrapper(itemState) ? unwrapInjectedWrapper(itemState) : itemState;
1357
+ const itemDocs = syncParser.getDocFragments(itemInnerState === void 0 ? { kind: "unavailable" } : {
1358
+ kind: "available",
1359
+ state: itemInnerState
1360
+ }, defaultValue != null && defaultValue.length > 0 ? defaultValue[0] : void 0);
1361
+ selected.push(...getSelectedCommandUsage(getCommandDocMetadata(itemDocs)));
1362
+ }
1363
+ return withCommandDocMetadata(docs, withSelectedCommandUsage(metadata, selected));
1350
1364
  }
1351
1365
  };
1352
1366
  function validateMultipleResult(result, deferredIndices, hasDeferred = false) {
@@ -1,6 +1,7 @@
1
1
  const require_annotations = require('./internal/annotations.cjs');
2
2
  const require_joined_file_suggestion = require('./internal/joined-file-suggestion.cjs');
3
3
  const require_message = require('./message.cjs');
4
+ const require_command_usage = require('./internal/command-usage.cjs');
4
5
  const require_command_doc_metadata = require('./internal/command-doc-metadata.cjs');
5
6
  const require_passthrough = require('./internal/passthrough.cjs');
6
7
  const require_validate = require('./validate.cjs');
@@ -1221,18 +1222,20 @@ function command(name, parser, options = {}) {
1221
1222
  const isAsync = parser.mode === "async";
1222
1223
  const syncInnerParser = parser;
1223
1224
  const asyncInnerParser = parser;
1225
+ const commandUsageId = {};
1226
+ const childUsageScope = /* @__PURE__ */ new WeakMap();
1224
1227
  const result = {
1225
1228
  [Symbol.for("@optique/core/commandParser")]: true,
1226
1229
  mode: parser.mode,
1227
1230
  $valueType: [],
1228
1231
  $stateType: [],
1229
1232
  priority: 15,
1230
- usage: [{
1233
+ usage: [require_command_usage.identifyCommandUsage({
1231
1234
  type: "command",
1232
1235
  name,
1233
1236
  ...options.usageLine != null && { usageLine: options.usageLine },
1234
1237
  ...options.hidden != null && { hidden: options.hidden }
1235
- }, ...parser.usage],
1238
+ }, commandUsageId), ...require_command_usage.scopeCommandUsage(parser.usage, childUsageScope)],
1236
1239
  leadingNames: new Set([name]),
1237
1240
  acceptingAnyToken: false,
1238
1241
  initialState: void 0,
@@ -1420,12 +1423,12 @@ function command(name, parser, options = {}) {
1420
1423
  brief: innerFragments.brief ?? options.brief,
1421
1424
  description: innerFragments.description ?? options.description,
1422
1425
  footer: innerFragments.footer ?? options.footer
1423
- }, {
1426
+ }, require_command_doc_metadata.withSelectedCommandUsage({
1424
1427
  origin: "command",
1425
1428
  brief: innerMetadata?.brief ?? options.brief,
1426
1429
  description: innerMetadata?.description ?? options.description,
1427
1430
  footer: innerMetadata?.footer ?? options.footer
1428
- });
1431
+ }, [commandUsageId, ...require_command_doc_metadata.getSelectedCommandUsage(require_command_doc_metadata.scopeCommandDocMetadata(require_command_doc_metadata.getCommandDocMetadata(innerFragments), childUsageScope))]));
1429
1432
  },
1430
1433
  [Symbol.for("Deno.customInspect")]() {
1431
1434
  return `command(${JSON.stringify(name)})`;
@@ -1,7 +1,8 @@
1
1
  import { annotateFreshArray, getAnnotations } from "./internal/annotations.js";
2
2
  import { joinedFileSuggestion } from "./internal/joined-file-suggestion.js";
3
3
  import { message, metavar, optionName, optionNames, text, valueSet } from "./message.js";
4
- import { getCommandDocMetadata, withCommandDocMetadata } from "./internal/command-doc-metadata.js";
4
+ import { identifyCommandUsage, scopeCommandUsage } from "./internal/command-usage.js";
5
+ import { getCommandDocMetadata, getSelectedCommandUsage, scopeCommandDocMetadata, withCommandDocMetadata, withSelectedCommandUsage } from "./internal/command-doc-metadata.js";
5
6
  import { defineKnownCompletion, defineOptionMatch, definePassThroughPriority, getOptionMatch, getPassThroughFailure, getPassThroughPriority } from "./internal/passthrough.js";
6
7
  import { validateCommandNames, validateOptionNames } from "./validate.js";
7
8
  import { extractOptionNames, isDocHidden, isSuggestionHidden } from "./usage.js";
@@ -1221,18 +1222,20 @@ function command(name, parser, options = {}) {
1221
1222
  const isAsync = parser.mode === "async";
1222
1223
  const syncInnerParser = parser;
1223
1224
  const asyncInnerParser = parser;
1225
+ const commandUsageId = {};
1226
+ const childUsageScope = /* @__PURE__ */ new WeakMap();
1224
1227
  const result = {
1225
1228
  [Symbol.for("@optique/core/commandParser")]: true,
1226
1229
  mode: parser.mode,
1227
1230
  $valueType: [],
1228
1231
  $stateType: [],
1229
1232
  priority: 15,
1230
- usage: [{
1233
+ usage: [identifyCommandUsage({
1231
1234
  type: "command",
1232
1235
  name,
1233
1236
  ...options.usageLine != null && { usageLine: options.usageLine },
1234
1237
  ...options.hidden != null && { hidden: options.hidden }
1235
- }, ...parser.usage],
1238
+ }, commandUsageId), ...scopeCommandUsage(parser.usage, childUsageScope)],
1236
1239
  leadingNames: new Set([name]),
1237
1240
  acceptingAnyToken: false,
1238
1241
  initialState: void 0,
@@ -1420,12 +1423,12 @@ function command(name, parser, options = {}) {
1420
1423
  brief: innerFragments.brief ?? options.brief,
1421
1424
  description: innerFragments.description ?? options.description,
1422
1425
  footer: innerFragments.footer ?? options.footer
1423
- }, {
1426
+ }, withSelectedCommandUsage({
1424
1427
  origin: "command",
1425
1428
  brief: innerMetadata?.brief ?? options.brief,
1426
1429
  description: innerMetadata?.description ?? options.description,
1427
1430
  footer: innerMetadata?.footer ?? options.footer
1428
- });
1431
+ }, [commandUsageId, ...getSelectedCommandUsage(scopeCommandDocMetadata(getCommandDocMetadata(innerFragments), childUsageScope))]));
1429
1432
  },
1430
1433
  [Symbol.for("Deno.customInspect")]() {
1431
1434
  return `command(${JSON.stringify(name)})`;