@optique/core 1.1.13-dev.2823 → 1.1.13

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.
@@ -513,12 +513,16 @@ function analyzeNoMatchContext(parsers) {
513
513
  * construction. This is a programmer error, not a user error.
514
514
  */
515
515
  var DuplicateOptionError = class extends Error {
516
+ /** The option name shared by multiple fields. */
517
+ optionName;
518
+ /** The fields declaring the option. */
519
+ sources;
516
520
  constructor(optionName$1, sources) {
517
521
  const sourceNames = sources.map((s) => typeof s === "symbol" ? s.description ?? s.toString() : s);
518
522
  super(`Duplicate option name "${optionName$1}" found in fields: ${sourceNames.join(", ")}. Each option name must be unique within a parser combinator.`);
523
+ this.name = "DuplicateOptionError";
519
524
  this.optionName = optionName$1;
520
525
  this.sources = sources;
521
- this.name = "DuplicateOptionError";
522
526
  }
523
527
  };
524
528
  /**
@@ -526,6 +530,8 @@ var DuplicateOptionError = class extends Error {
526
530
  * during parser construction. This is a programmer error, not a user error.
527
531
  */
528
532
  var DuplicateCommandNameError = class extends TypeError {
533
+ commandName;
534
+ sources;
529
535
  constructor(commandName, sources) {
530
536
  const sourceNames = sources.map((s) => typeof s === "symbol" ? s.description ?? s.toString() : s);
531
537
  super(`Duplicate command name "${commandName}" found in parsers: ${sourceNames.join(", ")}. Each command name or alias must be unique within active parser alternatives.`);
@@ -102,7 +102,9 @@ interface OrErrorOptions {
102
102
  * construction. This is a programmer error, not a user error.
103
103
  */
104
104
  declare class DuplicateOptionError extends Error {
105
+ /** The option name shared by multiple fields. */
105
106
  readonly optionName: string;
107
+ /** The fields declaring the option. */
106
108
  readonly sources: readonly (string | symbol)[];
107
109
  constructor(optionName: string, sources: readonly (string | symbol)[]);
108
110
  }
@@ -102,7 +102,9 @@ interface OrErrorOptions {
102
102
  * construction. This is a programmer error, not a user error.
103
103
  */
104
104
  declare class DuplicateOptionError extends Error {
105
+ /** The option name shared by multiple fields. */
105
106
  readonly optionName: string;
107
+ /** The fields declaring the option. */
106
108
  readonly sources: readonly (string | symbol)[];
107
109
  constructor(optionName: string, sources: readonly (string | symbol)[]);
108
110
  }
@@ -513,12 +513,16 @@ function analyzeNoMatchContext(parsers) {
513
513
  * construction. This is a programmer error, not a user error.
514
514
  */
515
515
  var DuplicateOptionError = class extends Error {
516
+ /** The option name shared by multiple fields. */
517
+ optionName;
518
+ /** The fields declaring the option. */
519
+ sources;
516
520
  constructor(optionName$1, sources) {
517
521
  const sourceNames = sources.map((s) => typeof s === "symbol" ? s.description ?? s.toString() : s);
518
522
  super(`Duplicate option name "${optionName$1}" found in fields: ${sourceNames.join(", ")}. Each option name must be unique within a parser combinator.`);
523
+ this.name = "DuplicateOptionError";
519
524
  this.optionName = optionName$1;
520
525
  this.sources = sources;
521
- this.name = "DuplicateOptionError";
522
526
  }
523
527
  };
524
528
  /**
@@ -526,6 +530,8 @@ var DuplicateOptionError = class extends Error {
526
530
  * during parser construction. This is a programmer error, not a user error.
527
531
  */
528
532
  var DuplicateCommandNameError = class extends TypeError {
533
+ commandName;
534
+ sources;
529
535
  constructor(commandName, sources) {
530
536
  const sourceNames = sources.map((s) => typeof s === "symbol" ? s.description ?? s.toString() : s);
531
537
  super(`Duplicate command name "${commandName}" found in parsers: ${sourceNames.join(", ")}. Each command name or alias must be unique within active parser alternatives.`);
@@ -0,0 +1,73 @@
1
+
2
+ //#region src/internal/joined-file-suggestion.ts
3
+ const joinedFiles = /* @__PURE__ */ new WeakMap();
4
+ /**
5
+ * Keeps a joined file fallback's origin available to the Bash encoder.
6
+ * Other shells retain the existing literal representation.
7
+ * @param prefix The option name and delimiter.
8
+ * @param file The original file suggestion.
9
+ * @param value The value prefix being completed.
10
+ * @returns The literal fallback with private file metadata.
11
+ * @internal
12
+ */
13
+ function joinedFileSuggestion(prefix, file, value) {
14
+ const literal = {
15
+ kind: "literal",
16
+ text: prefix + (file.pattern ?? ""),
17
+ description: file.description
18
+ };
19
+ joinedFiles.set(literal, {
20
+ files: [{
21
+ prefix,
22
+ file: file.pattern === value && !/[?*]/.test(value) && !(file.prefix != null && file.prefix !== "" && value.startsWith(file.prefix)) ? {
23
+ ...file,
24
+ pattern: void 0
25
+ } : file
26
+ }],
27
+ literal: false
28
+ });
29
+ return literal;
30
+ }
31
+ /**
32
+ * Retrieves the originating file suggestion for a joined literal fallback.
33
+ * @param suggestion The suggestion to inspect.
34
+ * @returns Its private file metadata, if present.
35
+ * @internal
36
+ */
37
+ function getJoinedFileSuggestion(suggestion) {
38
+ return joinedFiles.get(suggestion);
39
+ }
40
+ /**
41
+ * Preserves file origins when equal literal fallbacks are deduplicated.
42
+ * @param target The retained suggestion.
43
+ * @param source The duplicate suggestion.
44
+ * @internal
45
+ */
46
+ function mergeJoinedFileSuggestions(target, source) {
47
+ const a = joinedFiles.get(target);
48
+ const b = joinedFiles.get(source);
49
+ if (a === void 0 && b === void 0) return;
50
+ const files = [...a?.files ?? [], ...b?.files ?? []];
51
+ const seen = /* @__PURE__ */ new Set();
52
+ joinedFiles.set(target, {
53
+ literal: (a?.literal ?? true) || (b?.literal ?? true),
54
+ files: files.filter(({ prefix, file }) => {
55
+ const key = JSON.stringify([
56
+ prefix,
57
+ file.type,
58
+ file.pattern,
59
+ file.prefix,
60
+ file.includeHidden === true,
61
+ file.extensions?.toSorted()
62
+ ]);
63
+ if (seen.has(key)) return false;
64
+ seen.add(key);
65
+ return true;
66
+ })
67
+ });
68
+ }
69
+
70
+ //#endregion
71
+ exports.getJoinedFileSuggestion = getJoinedFileSuggestion;
72
+ exports.joinedFileSuggestion = joinedFileSuggestion;
73
+ exports.mergeJoinedFileSuggestions = mergeJoinedFileSuggestions;
@@ -0,0 +1,70 @@
1
+ //#region src/internal/joined-file-suggestion.ts
2
+ const joinedFiles = /* @__PURE__ */ new WeakMap();
3
+ /**
4
+ * Keeps a joined file fallback's origin available to the Bash encoder.
5
+ * Other shells retain the existing literal representation.
6
+ * @param prefix The option name and delimiter.
7
+ * @param file The original file suggestion.
8
+ * @param value The value prefix being completed.
9
+ * @returns The literal fallback with private file metadata.
10
+ * @internal
11
+ */
12
+ function joinedFileSuggestion(prefix, file, value) {
13
+ const literal = {
14
+ kind: "literal",
15
+ text: prefix + (file.pattern ?? ""),
16
+ description: file.description
17
+ };
18
+ joinedFiles.set(literal, {
19
+ files: [{
20
+ prefix,
21
+ file: file.pattern === value && !/[?*]/.test(value) && !(file.prefix != null && file.prefix !== "" && value.startsWith(file.prefix)) ? {
22
+ ...file,
23
+ pattern: void 0
24
+ } : file
25
+ }],
26
+ literal: false
27
+ });
28
+ return literal;
29
+ }
30
+ /**
31
+ * Retrieves the originating file suggestion for a joined literal fallback.
32
+ * @param suggestion The suggestion to inspect.
33
+ * @returns Its private file metadata, if present.
34
+ * @internal
35
+ */
36
+ function getJoinedFileSuggestion(suggestion) {
37
+ return joinedFiles.get(suggestion);
38
+ }
39
+ /**
40
+ * Preserves file origins when equal literal fallbacks are deduplicated.
41
+ * @param target The retained suggestion.
42
+ * @param source The duplicate suggestion.
43
+ * @internal
44
+ */
45
+ function mergeJoinedFileSuggestions(target, source) {
46
+ const a = joinedFiles.get(target);
47
+ const b = joinedFiles.get(source);
48
+ if (a === void 0 && b === void 0) return;
49
+ const files = [...a?.files ?? [], ...b?.files ?? []];
50
+ const seen = /* @__PURE__ */ new Set();
51
+ joinedFiles.set(target, {
52
+ literal: (a?.literal ?? true) || (b?.literal ?? true),
53
+ files: files.filter(({ prefix, file }) => {
54
+ const key = JSON.stringify([
55
+ prefix,
56
+ file.type,
57
+ file.pattern,
58
+ file.prefix,
59
+ file.includeHidden === true,
60
+ file.extensions?.toSorted()
61
+ ]);
62
+ if (seen.has(key)) return false;
63
+ seen.add(key);
64
+ return true;
65
+ })
66
+ });
67
+ }
68
+
69
+ //#endregion
70
+ export { getJoinedFileSuggestion, joinedFileSuggestion, mergeJoinedFileSuggestions };
@@ -3,6 +3,7 @@ const require_message = require('./message.cjs');
3
3
  const require_mode_dispatch = require('./internal/mode-dispatch.cjs');
4
4
  const require_internal_parser = require('./internal/parser.cjs');
5
5
  const require_annotation_state = require('./annotation-state.cjs');
6
+ const require_joined_file_suggestion = require('./internal/joined-file-suggestion.cjs');
6
7
  const require_command_doc_metadata = require('./internal/command-doc-metadata.cjs');
7
8
  const require_passthrough = require('./internal/passthrough.cjs');
8
9
  const require_execution_context = require('./execution-context.cjs');
@@ -1319,14 +1320,16 @@ function multiple(parser, options = {}) {
1319
1320
  };
1320
1321
  return require_mode_dispatch.dispatchIterableByMode(parser.mode, function* () {
1321
1322
  const selectedValues = collectSelectedValuesSync();
1322
- const emitted = /* @__PURE__ */ new Set();
1323
+ const emitted = /* @__PURE__ */ new Map();
1323
1324
  const yieldUnique = function* (suggestions) {
1324
1325
  for (const s of suggestions) {
1325
1326
  const key = suggestionKey(s);
1326
- if (shouldInclude(selectedValues, s) && !emitted.has(key)) {
1327
- emitted.add(key);
1327
+ if (!shouldInclude(selectedValues, s)) continue;
1328
+ const existing = emitted.get(key);
1329
+ if (existing === void 0) {
1330
+ emitted.set(key, s);
1328
1331
  yield s;
1329
- }
1332
+ } else require_joined_file_suggestion.mergeJoinedFileSuggestions(existing, s);
1330
1333
  }
1331
1334
  };
1332
1335
  let shouldTryFallback = false;
@@ -1339,14 +1342,16 @@ function multiple(parser, options = {}) {
1339
1342
  if (shouldTryFallback) yield* yieldUnique(syncParser.suggest(require_execution_context.withChildContext(context, itemIndex, suggestFallbackState), prefix));
1340
1343
  }, async function* () {
1341
1344
  const selectedValues = await collectSelectedValuesAsync();
1342
- const emitted = /* @__PURE__ */ new Set();
1345
+ const emitted = /* @__PURE__ */ new Map();
1343
1346
  const yieldUnique = async function* (suggestions) {
1344
1347
  for await (const s of suggestions) {
1345
1348
  const key = suggestionKey(s);
1346
- if (shouldInclude(selectedValues, s) && !emitted.has(key)) {
1347
- emitted.add(key);
1349
+ if (!shouldInclude(selectedValues, s)) continue;
1350
+ const existing = emitted.get(key);
1351
+ if (existing === void 0) {
1352
+ emitted.set(key, s);
1348
1353
  yield s;
1349
- }
1354
+ } else require_joined_file_suggestion.mergeJoinedFileSuggestions(existing, s);
1350
1355
  }
1351
1356
  };
1352
1357
  let shouldTryFallback = false;
package/dist/modifiers.js CHANGED
@@ -3,6 +3,7 @@ import { formatMessage, message, text } from "./message.js";
3
3
  import { dispatchByMode, dispatchIterableByMode, mapModeValue, wrapForMode } from "./internal/mode-dispatch.js";
4
4
  import { defineInheritedAnnotationParser, defineParseLanes, defineSourceBindingOnlyAnnotationCompletionParser, getOwnParseLanes, unmatchedNonCliDependencySourceStateMarker } from "./internal/parser.js";
5
5
  import { getDelegatedAnnotationState, hasDelegatedAnnotationCarrier, isAnnotationWrappedInitialState, normalizeDelegatedAnnotationState, normalizeNestedDelegatedAnnotationState } from "./annotation-state.js";
6
+ import { mergeJoinedFileSuggestions } from "./internal/joined-file-suggestion.js";
6
7
  import { getCommandDocMetadata, withCommandDocMetadata } from "./internal/command-doc-metadata.js";
7
8
  import { defineKnownCompletionLookup, defineOptionMatch, definePassThroughPriority, getKnownCompletion, getOptionMatch, getPassThroughPriority } from "./internal/passthrough.js";
8
9
  import { mergeChildExec, withChildContext, withChildExecPath } from "./execution-context.js";
@@ -1319,14 +1320,16 @@ function multiple(parser, options = {}) {
1319
1320
  };
1320
1321
  return dispatchIterableByMode(parser.mode, function* () {
1321
1322
  const selectedValues = collectSelectedValuesSync();
1322
- const emitted = /* @__PURE__ */ new Set();
1323
+ const emitted = /* @__PURE__ */ new Map();
1323
1324
  const yieldUnique = function* (suggestions) {
1324
1325
  for (const s of suggestions) {
1325
1326
  const key = suggestionKey(s);
1326
- if (shouldInclude(selectedValues, s) && !emitted.has(key)) {
1327
- emitted.add(key);
1327
+ if (!shouldInclude(selectedValues, s)) continue;
1328
+ const existing = emitted.get(key);
1329
+ if (existing === void 0) {
1330
+ emitted.set(key, s);
1328
1331
  yield s;
1329
- }
1332
+ } else mergeJoinedFileSuggestions(existing, s);
1330
1333
  }
1331
1334
  };
1332
1335
  let shouldTryFallback = false;
@@ -1339,14 +1342,16 @@ function multiple(parser, options = {}) {
1339
1342
  if (shouldTryFallback) yield* yieldUnique(syncParser.suggest(withChildContext(context, itemIndex, suggestFallbackState), prefix));
1340
1343
  }, async function* () {
1341
1344
  const selectedValues = await collectSelectedValuesAsync();
1342
- const emitted = /* @__PURE__ */ new Set();
1345
+ const emitted = /* @__PURE__ */ new Map();
1343
1346
  const yieldUnique = async function* (suggestions) {
1344
1347
  for await (const s of suggestions) {
1345
1348
  const key = suggestionKey(s);
1346
- if (shouldInclude(selectedValues, s) && !emitted.has(key)) {
1347
- emitted.add(key);
1349
+ if (!shouldInclude(selectedValues, s)) continue;
1350
+ const existing = emitted.get(key);
1351
+ if (existing === void 0) {
1352
+ emitted.set(key, s);
1348
1353
  yield s;
1349
- }
1354
+ } else mergeJoinedFileSuggestions(existing, s);
1350
1355
  }
1351
1356
  };
1352
1357
  let shouldTryFallback = false;
@@ -6,6 +6,7 @@ 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_joined_file_suggestion = require('./internal/joined-file-suggestion.cjs');
9
10
  const require_command_doc_metadata = require('./internal/command-doc-metadata.cjs');
10
11
  const require_passthrough = require('./internal/passthrough.cjs');
11
12
  const require_command_alias = require('./internal/command-alias.cjs');
@@ -246,33 +247,33 @@ function* getSuggestionsWithDependency(valueParser, prefix, dependencyRegistry,
246
247
  }
247
248
  yield* valueParser.suggest(prefix);
248
249
  }
250
+ /** Returns the joined prefix accepted by option parsing. */
251
+ function joinedOptionPrefix(names, input) {
252
+ return names.filter((name) => name.startsWith("--") || name.startsWith("/") || name.startsWith("-") && name.length > 2).map((name) => name + (name.startsWith("/") ? ":" : "=")).find((prefix) => input.startsWith(prefix));
253
+ }
249
254
  /**
250
255
  * Internal sync helper for option suggest functionality.
251
256
  * @internal
252
257
  */
253
258
  function* suggestOptionSync(optionNames$1, valueParser, hidden, description, context, prefix) {
254
259
  if (hidden || context.optionsTerminated) return;
255
- const equalsIndex = prefix.indexOf("=");
256
- if (equalsIndex >= 0) {
257
- const optionPart = prefix.slice(0, equalsIndex);
258
- const valuePart = prefix.slice(equalsIndex + 1);
260
+ const expectingValue = context.buffer.length > 0 && optionNames$1.includes(context.buffer[context.buffer.length - 1]);
261
+ const joinedPrefix = joinedOptionPrefix(optionNames$1, prefix);
262
+ if (joinedPrefix !== void 0 && !expectingValue) {
263
+ const optionPart = joinedPrefix.slice(0, -1);
264
+ const valuePart = prefix.slice(joinedPrefix.length);
259
265
  if (optionNames$1.includes(optionPart)) {
260
266
  if (valueParser && valueParser.suggest) {
261
267
  const valueSuggestions = getSuggestionsWithDependency(valueParser, valuePart, context.dependencyRegistry, context.exec);
262
268
  for (const suggestion of valueSuggestions) if (suggestion.kind === "literal") yield {
263
269
  kind: "literal",
264
- text: `${optionPart}=${suggestion.text}`,
265
- description: suggestion.description
266
- };
267
- else yield {
268
- kind: "literal",
269
- text: `${optionPart}=${suggestion.pattern || ""}`,
270
+ text: `${joinedPrefix}${suggestion.text}`,
270
271
  description: suggestion.description
271
272
  };
273
+ else yield require_joined_file_suggestion.joinedFileSuggestion(joinedPrefix, suggestion, valuePart);
272
274
  }
273
275
  }
274
276
  } else {
275
- const expectingValue = context.buffer.length > 0 && optionNames$1.includes(context.buffer[context.buffer.length - 1]);
276
277
  if (!expectingValue && (prefix.startsWith("--") || prefix.startsWith("-") || prefix.startsWith("/") || prefix.startsWith("+"))) {
277
278
  for (const optionName$1 of optionNames$1) if (optionName$1.startsWith(prefix)) {
278
279
  if (prefix === "-" && optionName$1.length !== 2) continue;
@@ -337,27 +338,23 @@ async function* getSuggestionsWithDependencyAsync(valueParser, prefix, dependenc
337
338
  */
338
339
  async function* suggestOptionAsync(optionNames$1, valueParser, hidden, description, context, prefix) {
339
340
  if (hidden || context.optionsTerminated) return;
340
- const equalsIndex = prefix.indexOf("=");
341
- if (equalsIndex >= 0) {
342
- const optionPart = prefix.slice(0, equalsIndex);
343
- const valuePart = prefix.slice(equalsIndex + 1);
341
+ const expectingValue = context.buffer.length > 0 && optionNames$1.includes(context.buffer[context.buffer.length - 1]);
342
+ const joinedPrefix = joinedOptionPrefix(optionNames$1, prefix);
343
+ if (joinedPrefix !== void 0 && !expectingValue) {
344
+ const optionPart = joinedPrefix.slice(0, -1);
345
+ const valuePart = prefix.slice(joinedPrefix.length);
344
346
  if (optionNames$1.includes(optionPart)) {
345
347
  if (valueParser && valueParser.suggest) {
346
348
  const valueSuggestions = getSuggestionsWithDependencyAsync(valueParser, valuePart, context.dependencyRegistry, context.exec);
347
349
  for await (const suggestion of valueSuggestions) if (suggestion.kind === "literal") yield {
348
350
  kind: "literal",
349
- text: `${optionPart}=${suggestion.text}`,
350
- description: suggestion.description
351
- };
352
- else yield {
353
- kind: "literal",
354
- text: `${optionPart}=${suggestion.pattern || ""}`,
351
+ text: `${joinedPrefix}${suggestion.text}`,
355
352
  description: suggestion.description
356
353
  };
354
+ else yield require_joined_file_suggestion.joinedFileSuggestion(joinedPrefix, suggestion, valuePart);
357
355
  }
358
356
  }
359
357
  } else {
360
- const expectingValue = context.buffer.length > 0 && optionNames$1.includes(context.buffer[context.buffer.length - 1]);
361
358
  if (!expectingValue && (prefix.startsWith("--") || prefix.startsWith("-") || prefix.startsWith("/") || prefix.startsWith("+"))) {
362
359
  for (const optionName$1 of optionNames$1) if (optionName$1.startsWith(prefix)) {
363
360
  if (prefix === "-" && optionName$1.length !== 2) continue;
@@ -6,6 +6,7 @@ import { dispatchByMode, dispatchIterableByMode, wrapForMode } from "./internal/
6
6
  import { getDefaultValuesFunction, getDependencyIds, getSnapshottedDefaultDependencyValues, isDerivedValueParser, suggestWithDependency } from "./internal/dependency.js";
7
7
  import { replayDerivedParser, replayDerivedParserAsync } from "./dependency-runtime.js";
8
8
  import { getWrappedChildParseState, getWrappedChildState, isAnnotationWrappedInitialState, normalizeInjectedAnnotationState } from "./annotation-state.js";
9
+ import { joinedFileSuggestion } from "./internal/joined-file-suggestion.js";
9
10
  import { getCommandDocMetadata, withCommandDocMetadata } from "./internal/command-doc-metadata.js";
10
11
  import { defineKnownCompletion, defineOptionMatch, definePassThroughPriority, getOptionMatch, getPassThroughFailure, getPassThroughPriority } from "./internal/passthrough.js";
11
12
  import { hiddenCommandAliasesKey } from "./internal/command-alias.js";
@@ -246,33 +247,33 @@ function* getSuggestionsWithDependency(valueParser, prefix, dependencyRegistry,
246
247
  }
247
248
  yield* valueParser.suggest(prefix);
248
249
  }
250
+ /** Returns the joined prefix accepted by option parsing. */
251
+ function joinedOptionPrefix(names, input) {
252
+ return names.filter((name) => name.startsWith("--") || name.startsWith("/") || name.startsWith("-") && name.length > 2).map((name) => name + (name.startsWith("/") ? ":" : "=")).find((prefix) => input.startsWith(prefix));
253
+ }
249
254
  /**
250
255
  * Internal sync helper for option suggest functionality.
251
256
  * @internal
252
257
  */
253
258
  function* suggestOptionSync(optionNames$1, valueParser, hidden, description, context, prefix) {
254
259
  if (hidden || context.optionsTerminated) return;
255
- const equalsIndex = prefix.indexOf("=");
256
- if (equalsIndex >= 0) {
257
- const optionPart = prefix.slice(0, equalsIndex);
258
- const valuePart = prefix.slice(equalsIndex + 1);
260
+ const expectingValue = context.buffer.length > 0 && optionNames$1.includes(context.buffer[context.buffer.length - 1]);
261
+ const joinedPrefix = joinedOptionPrefix(optionNames$1, prefix);
262
+ if (joinedPrefix !== void 0 && !expectingValue) {
263
+ const optionPart = joinedPrefix.slice(0, -1);
264
+ const valuePart = prefix.slice(joinedPrefix.length);
259
265
  if (optionNames$1.includes(optionPart)) {
260
266
  if (valueParser && valueParser.suggest) {
261
267
  const valueSuggestions = getSuggestionsWithDependency(valueParser, valuePart, context.dependencyRegistry, context.exec);
262
268
  for (const suggestion of valueSuggestions) if (suggestion.kind === "literal") yield {
263
269
  kind: "literal",
264
- text: `${optionPart}=${suggestion.text}`,
265
- description: suggestion.description
266
- };
267
- else yield {
268
- kind: "literal",
269
- text: `${optionPart}=${suggestion.pattern || ""}`,
270
+ text: `${joinedPrefix}${suggestion.text}`,
270
271
  description: suggestion.description
271
272
  };
273
+ else yield joinedFileSuggestion(joinedPrefix, suggestion, valuePart);
272
274
  }
273
275
  }
274
276
  } else {
275
- const expectingValue = context.buffer.length > 0 && optionNames$1.includes(context.buffer[context.buffer.length - 1]);
276
277
  if (!expectingValue && (prefix.startsWith("--") || prefix.startsWith("-") || prefix.startsWith("/") || prefix.startsWith("+"))) {
277
278
  for (const optionName$1 of optionNames$1) if (optionName$1.startsWith(prefix)) {
278
279
  if (prefix === "-" && optionName$1.length !== 2) continue;
@@ -337,27 +338,23 @@ async function* getSuggestionsWithDependencyAsync(valueParser, prefix, dependenc
337
338
  */
338
339
  async function* suggestOptionAsync(optionNames$1, valueParser, hidden, description, context, prefix) {
339
340
  if (hidden || context.optionsTerminated) return;
340
- const equalsIndex = prefix.indexOf("=");
341
- if (equalsIndex >= 0) {
342
- const optionPart = prefix.slice(0, equalsIndex);
343
- const valuePart = prefix.slice(equalsIndex + 1);
341
+ const expectingValue = context.buffer.length > 0 && optionNames$1.includes(context.buffer[context.buffer.length - 1]);
342
+ const joinedPrefix = joinedOptionPrefix(optionNames$1, prefix);
343
+ if (joinedPrefix !== void 0 && !expectingValue) {
344
+ const optionPart = joinedPrefix.slice(0, -1);
345
+ const valuePart = prefix.slice(joinedPrefix.length);
344
346
  if (optionNames$1.includes(optionPart)) {
345
347
  if (valueParser && valueParser.suggest) {
346
348
  const valueSuggestions = getSuggestionsWithDependencyAsync(valueParser, valuePart, context.dependencyRegistry, context.exec);
347
349
  for await (const suggestion of valueSuggestions) if (suggestion.kind === "literal") yield {
348
350
  kind: "literal",
349
- text: `${optionPart}=${suggestion.text}`,
350
- description: suggestion.description
351
- };
352
- else yield {
353
- kind: "literal",
354
- text: `${optionPart}=${suggestion.pattern || ""}`,
351
+ text: `${joinedPrefix}${suggestion.text}`,
355
352
  description: suggestion.description
356
353
  };
354
+ else yield joinedFileSuggestion(joinedPrefix, suggestion, valuePart);
357
355
  }
358
356
  }
359
357
  } else {
360
- const expectingValue = context.buffer.length > 0 && optionNames$1.includes(context.buffer[context.buffer.length - 1]);
361
358
  if (!expectingValue && (prefix.startsWith("--") || prefix.startsWith("-") || prefix.startsWith("/") || prefix.startsWith("+"))) {
362
359
  for (const optionName$1 of optionNames$1) if (optionName$1.startsWith(prefix)) {
363
360
  if (prefix === "-" && optionName$1.length !== 2) continue;
@@ -1,5 +1,6 @@
1
1
  const require_message = require('./message.cjs');
2
2
  const require_usage = require('./usage.cjs');
3
+ const require_joined_file_suggestion = require('./internal/joined-file-suggestion.cjs');
3
4
 
4
5
  //#region src/suggestion.ts
5
6
  /**
@@ -310,7 +311,8 @@ function deduplicateSuggestions(suggestions) {
310
311
  if (existing == null) {
311
312
  entries.set(key, suggestion);
312
313
  order.push(key);
313
- } else if (suggestion.kind === "file" && existing.kind === "file" && suggestion.includeHidden === true && existing.includeHidden !== true) entries.set(key, {
314
+ } else if (suggestion.kind === "literal" && existing.kind === "literal") require_joined_file_suggestion.mergeJoinedFileSuggestions(existing, suggestion);
315
+ else if (suggestion.kind === "file" && existing.kind === "file" && suggestion.includeHidden === true && existing.includeHidden !== true) entries.set(key, {
314
316
  ...existing,
315
317
  includeHidden: true
316
318
  });
@@ -1,5 +1,6 @@
1
1
  import { message, optionName, text } from "./message.js";
2
2
  import { extractCommandNames, extractOptionNames, isSuggestionHidden } from "./usage.js";
3
+ import { mergeJoinedFileSuggestions } from "./internal/joined-file-suggestion.js";
3
4
 
4
5
  //#region src/suggestion.ts
5
6
  /**
@@ -310,7 +311,8 @@ function deduplicateSuggestions(suggestions) {
310
311
  if (existing == null) {
311
312
  entries.set(key, suggestion);
312
313
  order.push(key);
313
- } else if (suggestion.kind === "file" && existing.kind === "file" && suggestion.includeHidden === true && existing.includeHidden !== true) entries.set(key, {
314
+ } else if (suggestion.kind === "literal" && existing.kind === "literal") mergeJoinedFileSuggestions(existing, suggestion);
315
+ else if (suggestion.kind === "file" && existing.kind === "file" && suggestion.includeHidden === true && existing.includeHidden !== true) entries.set(key, {
314
316
  ...existing,
315
317
  includeHidden: true
316
318
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/core",
3
- "version": "1.1.13-dev.2823",
3
+ "version": "1.1.13",
4
4
  "description": "Type-safe combinatorial command-line interface parser",
5
5
  "keywords": [
6
6
  "CLI",
@@ -196,7 +196,7 @@
196
196
  },
197
197
  "sideEffects": false,
198
198
  "devDependencies": {
199
- "@optique/env": "1.1.13-dev.2823+d4c34d2f",
199
+ "@optique/env": "1.1.13",
200
200
  "@types/node": "^24.0.0",
201
201
  "fast-check": "^4.7.0",
202
202
  "tsdown": "^0.13.0",