@optique/core 0.6.0-dev.102 → 0.6.0-dev.104

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.
@@ -348,6 +348,235 @@ complete -c ${programName} -f -a '(${functionName})'
348
348
  }
349
349
  };
350
350
  /**
351
+ * The Nushell completion generator.
352
+ * @since 0.6.0
353
+ */
354
+ const nu = {
355
+ name: "nu",
356
+ generateScript(programName, args = []) {
357
+ const escapedArgs = args.map((arg) => `'${arg.replace(/'/g, "''")}'`).join(" ");
358
+ const safeName = programName.replace(/[^a-zA-Z0-9]+/g, "-");
359
+ const functionName = `nu-complete-${safeName}`;
360
+ return `
361
+ # Helper to split args respecting quotes and whitespace
362
+ def args-split []: string -> list<string> {
363
+ let STATE_NORMAL = 0
364
+ let STATE_IN_SINGLE_QUOTE = 1
365
+ let STATE_IN_DOUBLE_QUOTE = 2
366
+ let STATE_ESCAPE = 3
367
+ let WHITESPACES = [" " "\\t" "\\n" "\\r"]
368
+
369
+ mut state = $STATE_NORMAL
370
+ mut current_token = ""
371
+ mut result: list<string> = []
372
+ mut prev_state = $STATE_NORMAL
373
+
374
+ for char in ($in | split chars) {
375
+ if $state == $STATE_ESCAPE {
376
+ $current_token = $current_token + $char
377
+ $state = $prev_state
378
+ } else if $char == '\\\\' {
379
+ $prev_state = $state
380
+ $state = $STATE_ESCAPE
381
+ } else if $state == $STATE_NORMAL {
382
+ if $char == "'" {
383
+ $state = $STATE_IN_SINGLE_QUOTE
384
+ } else if $char == '"' {
385
+ $state = $STATE_IN_DOUBLE_QUOTE
386
+ } else if ($char in $WHITESPACES) {
387
+ if ($current_token | is-not-empty) {
388
+ $result = $result | append $current_token
389
+ $current_token = ""
390
+ }
391
+ } else {
392
+ $current_token = $current_token + $char
393
+ }
394
+ } else if $state == $STATE_IN_SINGLE_QUOTE {
395
+ if $char == "'" {
396
+ $state = $STATE_NORMAL
397
+ } else {
398
+ $current_token = $current_token + $char
399
+ }
400
+ } else if $state == $STATE_IN_DOUBLE_QUOTE {
401
+ if $char == '"' {
402
+ $state = $STATE_NORMAL
403
+ } else {
404
+ $current_token = $current_token + $char
405
+ }
406
+ }
407
+ }
408
+ if ($current_token | is-not-empty) {
409
+ $result = $result | append $current_token
410
+ }
411
+ $result
412
+ }
413
+
414
+ # Completion command that calls back to the CLI
415
+ export def "${functionName}" [context: string] {
416
+ # Split context into tokens, handling quotes properly
417
+ let tokens = $context | args-split
418
+
419
+ # Remove the command name (first token) to get arguments
420
+ let args = if ($tokens | length) > 1 {
421
+ $tokens | skip 1
422
+ } else {
423
+ []
424
+ }
425
+
426
+ # If context ends with whitespace, add empty string to represent the new argument
427
+ let args_with_empty = if ($context | str ends-with ' ') {
428
+ $args | append ''
429
+ } else {
430
+ $args
431
+ }
432
+
433
+ # Ensure at least one argument (empty string) to get completions not script
434
+ let final_args = if ($args_with_empty | is-empty) {
435
+ ['']
436
+ } else {
437
+ $args_with_empty
438
+ }
439
+
440
+ let output = try {
441
+ ${escapedArgs ? ` ^${programName} ${escapedArgs} ...$final_args | complete | get stdout\n` : ` ^${programName} ...$final_args | complete | get stdout\n`} } catch {
442
+ ""
443
+ }
444
+
445
+ # Process each line of output
446
+ $output | lines | each {|line|
447
+ if ($line | str starts-with '__FILE__:') {
448
+ # Parse file completion directive: __FILE__:type:extensions:pattern:hidden
449
+ let parts = ($line | split row ':')
450
+ let type = ($parts | get 1)
451
+ let extensions = ($parts | get 2)
452
+ let pattern = ($parts | get 3)
453
+ let hidden = ($parts | get 4) == '1'
454
+
455
+ # Extract prefix from the last argument if it exists
456
+ let prefix = if ($final_args | length) > 0 {
457
+ $final_args | last
458
+ } else {
459
+ ""
460
+ }
461
+
462
+ # Generate file completions based on type
463
+ # Use current directory if prefix is empty
464
+ let ls_pattern = if ($prefix | is-empty) { "." } else { $prefix + "*" }
465
+
466
+ let items = try {
467
+ match $type {
468
+ "file" => {
469
+ if ($extensions | is-empty) {
470
+ ls $ls_pattern | where type == file
471
+ } else {
472
+ let ext_list = ($extensions | split row ',')
473
+ ls $ls_pattern | where type == file | where {|f|
474
+ let ext = ($f.name | path parse | get extension)
475
+ $ext in $ext_list
476
+ }
477
+ }
478
+ },
479
+ "directory" => {
480
+ ls $ls_pattern | where type == dir
481
+ },
482
+ "any" => {
483
+ if ($extensions | is-empty) {
484
+ ls $ls_pattern
485
+ } else {
486
+ let ext_list = ($extensions | split row ',')
487
+ let dirs = ls $ls_pattern | where type == dir
488
+ let files = ls $ls_pattern | where type == file | where {|f|
489
+ let ext = ($f.name | path parse | get extension)
490
+ $ext in $ext_list
491
+ }
492
+ $dirs | append $files
493
+ }
494
+ }
495
+ }
496
+ } catch {
497
+ []
498
+ }
499
+
500
+ # Filter out hidden files unless requested
501
+ let filtered = if $hidden or ($prefix | str starts-with '.') {
502
+ $items
503
+ } else {
504
+ $items | where {|item|
505
+ let basename = ($item.name | path basename)
506
+ not ($basename | str starts-with '.')
507
+ }
508
+ }
509
+
510
+ # Format file completions
511
+ $filtered | each {|item|
512
+ let name = if $item.type == dir {
513
+ ($item.name | path basename) + "/"
514
+ } else {
515
+ $item.name | path basename
516
+ }
517
+ { value: $name }
518
+ }
519
+ } else {
520
+ # Regular literal completion - split by tab
521
+ let parts = ($line | split row "\t")
522
+ if ($parts | length) >= 2 {
523
+ # value\\tdescription format
524
+ { value: ($parts | get 0), description: ($parts | get 1) }
525
+ } else if ($parts | length) == 1 and ($parts | get 0 | is-not-empty) {
526
+ # Just value
527
+ { value: ($parts | get 0) }
528
+ } else {
529
+ null
530
+ }
531
+ }
532
+ } | flatten | compact
533
+ }
534
+
535
+ # Custom completer for external commands
536
+ # This function will be called by Nushell's completion system
537
+ def --env "${functionName}-external" [] {
538
+ let existing_completer = $env.config.completions.external.completer
539
+
540
+ $env.config.completions.external.completer = {|spans|
541
+ # Check if this is for our program
542
+ if ($spans.0 == "${programName}") {
543
+ # Build context string from spans
544
+ let context = $spans | str join ' '
545
+ ${functionName} $context
546
+ } else if ($existing_completer != null) {
547
+ # Delegate to existing completer
548
+ do $existing_completer $spans
549
+ } else {
550
+ # No completions
551
+ null
552
+ }
553
+ }
554
+
555
+ $env.config.completions.external.enable = true
556
+ }
557
+
558
+ # Auto-setup: register the completer when this module is imported
559
+ ${functionName}-external
560
+ `;
561
+ },
562
+ *encodeSuggestions(suggestions) {
563
+ let i = 0;
564
+ for (const suggestion of suggestions) {
565
+ if (i > 0) yield "\n";
566
+ if (suggestion.kind === "literal") {
567
+ const description = suggestion.description == null ? "" : require_message.formatMessage(suggestion.description, { colors: false });
568
+ yield `${suggestion.text}\t${description}`;
569
+ } else {
570
+ const extensions = suggestion.extensions?.join(",") || "";
571
+ const hidden = suggestion.includeHidden ? "1" : "0";
572
+ const description = suggestion.description == null ? "" : require_message.formatMessage(suggestion.description, { colors: false });
573
+ yield `__FILE__:${suggestion.type}:${extensions}:${suggestion.pattern || ""}:${hidden}\t${description}`;
574
+ }
575
+ i++;
576
+ }
577
+ }
578
+ };
579
+ /**
351
580
  * The PowerShell completion generator.
352
581
  * @since 0.6.0
353
582
  */
@@ -509,5 +738,6 @@ ${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
509
738
  //#endregion
510
739
  exports.bash = bash;
511
740
  exports.fish = fish;
741
+ exports.nu = nu;
512
742
  exports.pwsh = pwsh;
513
743
  exports.zsh = zsh;
@@ -42,10 +42,15 @@ declare const zsh: ShellCompletion;
42
42
  * @since 0.6.0
43
43
  */
44
44
  declare const fish: ShellCompletion;
45
+ /**
46
+ * The Nushell completion generator.
47
+ * @since 0.6.0
48
+ */
49
+ declare const nu: ShellCompletion;
45
50
  /**
46
51
  * The PowerShell completion generator.
47
52
  * @since 0.6.0
48
53
  */
49
54
  declare const pwsh: ShellCompletion;
50
55
  //#endregion
51
- export { ShellCompletion, bash, fish, pwsh, zsh };
56
+ export { ShellCompletion, bash, fish, nu, pwsh, zsh };
@@ -42,10 +42,15 @@ declare const zsh: ShellCompletion;
42
42
  * @since 0.6.0
43
43
  */
44
44
  declare const fish: ShellCompletion;
45
+ /**
46
+ * The Nushell completion generator.
47
+ * @since 0.6.0
48
+ */
49
+ declare const nu: ShellCompletion;
45
50
  /**
46
51
  * The PowerShell completion generator.
47
52
  * @since 0.6.0
48
53
  */
49
54
  declare const pwsh: ShellCompletion;
50
55
  //#endregion
51
- export { ShellCompletion, bash, fish, pwsh, zsh };
56
+ export { ShellCompletion, bash, fish, nu, pwsh, zsh };
@@ -348,6 +348,235 @@ complete -c ${programName} -f -a '(${functionName})'
348
348
  }
349
349
  };
350
350
  /**
351
+ * The Nushell completion generator.
352
+ * @since 0.6.0
353
+ */
354
+ const nu = {
355
+ name: "nu",
356
+ generateScript(programName, args = []) {
357
+ const escapedArgs = args.map((arg) => `'${arg.replace(/'/g, "''")}'`).join(" ");
358
+ const safeName = programName.replace(/[^a-zA-Z0-9]+/g, "-");
359
+ const functionName = `nu-complete-${safeName}`;
360
+ return `
361
+ # Helper to split args respecting quotes and whitespace
362
+ def args-split []: string -> list<string> {
363
+ let STATE_NORMAL = 0
364
+ let STATE_IN_SINGLE_QUOTE = 1
365
+ let STATE_IN_DOUBLE_QUOTE = 2
366
+ let STATE_ESCAPE = 3
367
+ let WHITESPACES = [" " "\\t" "\\n" "\\r"]
368
+
369
+ mut state = $STATE_NORMAL
370
+ mut current_token = ""
371
+ mut result: list<string> = []
372
+ mut prev_state = $STATE_NORMAL
373
+
374
+ for char in ($in | split chars) {
375
+ if $state == $STATE_ESCAPE {
376
+ $current_token = $current_token + $char
377
+ $state = $prev_state
378
+ } else if $char == '\\\\' {
379
+ $prev_state = $state
380
+ $state = $STATE_ESCAPE
381
+ } else if $state == $STATE_NORMAL {
382
+ if $char == "'" {
383
+ $state = $STATE_IN_SINGLE_QUOTE
384
+ } else if $char == '"' {
385
+ $state = $STATE_IN_DOUBLE_QUOTE
386
+ } else if ($char in $WHITESPACES) {
387
+ if ($current_token | is-not-empty) {
388
+ $result = $result | append $current_token
389
+ $current_token = ""
390
+ }
391
+ } else {
392
+ $current_token = $current_token + $char
393
+ }
394
+ } else if $state == $STATE_IN_SINGLE_QUOTE {
395
+ if $char == "'" {
396
+ $state = $STATE_NORMAL
397
+ } else {
398
+ $current_token = $current_token + $char
399
+ }
400
+ } else if $state == $STATE_IN_DOUBLE_QUOTE {
401
+ if $char == '"' {
402
+ $state = $STATE_NORMAL
403
+ } else {
404
+ $current_token = $current_token + $char
405
+ }
406
+ }
407
+ }
408
+ if ($current_token | is-not-empty) {
409
+ $result = $result | append $current_token
410
+ }
411
+ $result
412
+ }
413
+
414
+ # Completion command that calls back to the CLI
415
+ export def "${functionName}" [context: string] {
416
+ # Split context into tokens, handling quotes properly
417
+ let tokens = $context | args-split
418
+
419
+ # Remove the command name (first token) to get arguments
420
+ let args = if ($tokens | length) > 1 {
421
+ $tokens | skip 1
422
+ } else {
423
+ []
424
+ }
425
+
426
+ # If context ends with whitespace, add empty string to represent the new argument
427
+ let args_with_empty = if ($context | str ends-with ' ') {
428
+ $args | append ''
429
+ } else {
430
+ $args
431
+ }
432
+
433
+ # Ensure at least one argument (empty string) to get completions not script
434
+ let final_args = if ($args_with_empty | is-empty) {
435
+ ['']
436
+ } else {
437
+ $args_with_empty
438
+ }
439
+
440
+ let output = try {
441
+ ${escapedArgs ? ` ^${programName} ${escapedArgs} ...$final_args | complete | get stdout\n` : ` ^${programName} ...$final_args | complete | get stdout\n`} } catch {
442
+ ""
443
+ }
444
+
445
+ # Process each line of output
446
+ $output | lines | each {|line|
447
+ if ($line | str starts-with '__FILE__:') {
448
+ # Parse file completion directive: __FILE__:type:extensions:pattern:hidden
449
+ let parts = ($line | split row ':')
450
+ let type = ($parts | get 1)
451
+ let extensions = ($parts | get 2)
452
+ let pattern = ($parts | get 3)
453
+ let hidden = ($parts | get 4) == '1'
454
+
455
+ # Extract prefix from the last argument if it exists
456
+ let prefix = if ($final_args | length) > 0 {
457
+ $final_args | last
458
+ } else {
459
+ ""
460
+ }
461
+
462
+ # Generate file completions based on type
463
+ # Use current directory if prefix is empty
464
+ let ls_pattern = if ($prefix | is-empty) { "." } else { $prefix + "*" }
465
+
466
+ let items = try {
467
+ match $type {
468
+ "file" => {
469
+ if ($extensions | is-empty) {
470
+ ls $ls_pattern | where type == file
471
+ } else {
472
+ let ext_list = ($extensions | split row ',')
473
+ ls $ls_pattern | where type == file | where {|f|
474
+ let ext = ($f.name | path parse | get extension)
475
+ $ext in $ext_list
476
+ }
477
+ }
478
+ },
479
+ "directory" => {
480
+ ls $ls_pattern | where type == dir
481
+ },
482
+ "any" => {
483
+ if ($extensions | is-empty) {
484
+ ls $ls_pattern
485
+ } else {
486
+ let ext_list = ($extensions | split row ',')
487
+ let dirs = ls $ls_pattern | where type == dir
488
+ let files = ls $ls_pattern | where type == file | where {|f|
489
+ let ext = ($f.name | path parse | get extension)
490
+ $ext in $ext_list
491
+ }
492
+ $dirs | append $files
493
+ }
494
+ }
495
+ }
496
+ } catch {
497
+ []
498
+ }
499
+
500
+ # Filter out hidden files unless requested
501
+ let filtered = if $hidden or ($prefix | str starts-with '.') {
502
+ $items
503
+ } else {
504
+ $items | where {|item|
505
+ let basename = ($item.name | path basename)
506
+ not ($basename | str starts-with '.')
507
+ }
508
+ }
509
+
510
+ # Format file completions
511
+ $filtered | each {|item|
512
+ let name = if $item.type == dir {
513
+ ($item.name | path basename) + "/"
514
+ } else {
515
+ $item.name | path basename
516
+ }
517
+ { value: $name }
518
+ }
519
+ } else {
520
+ # Regular literal completion - split by tab
521
+ let parts = ($line | split row "\t")
522
+ if ($parts | length) >= 2 {
523
+ # value\\tdescription format
524
+ { value: ($parts | get 0), description: ($parts | get 1) }
525
+ } else if ($parts | length) == 1 and ($parts | get 0 | is-not-empty) {
526
+ # Just value
527
+ { value: ($parts | get 0) }
528
+ } else {
529
+ null
530
+ }
531
+ }
532
+ } | flatten | compact
533
+ }
534
+
535
+ # Custom completer for external commands
536
+ # This function will be called by Nushell's completion system
537
+ def --env "${functionName}-external" [] {
538
+ let existing_completer = $env.config.completions.external.completer
539
+
540
+ $env.config.completions.external.completer = {|spans|
541
+ # Check if this is for our program
542
+ if ($spans.0 == "${programName}") {
543
+ # Build context string from spans
544
+ let context = $spans | str join ' '
545
+ ${functionName} $context
546
+ } else if ($existing_completer != null) {
547
+ # Delegate to existing completer
548
+ do $existing_completer $spans
549
+ } else {
550
+ # No completions
551
+ null
552
+ }
553
+ }
554
+
555
+ $env.config.completions.external.enable = true
556
+ }
557
+
558
+ # Auto-setup: register the completer when this module is imported
559
+ ${functionName}-external
560
+ `;
561
+ },
562
+ *encodeSuggestions(suggestions) {
563
+ let i = 0;
564
+ for (const suggestion of suggestions) {
565
+ if (i > 0) yield "\n";
566
+ if (suggestion.kind === "literal") {
567
+ const description = suggestion.description == null ? "" : formatMessage(suggestion.description, { colors: false });
568
+ yield `${suggestion.text}\t${description}`;
569
+ } else {
570
+ const extensions = suggestion.extensions?.join(",") || "";
571
+ const hidden = suggestion.includeHidden ? "1" : "0";
572
+ const description = suggestion.description == null ? "" : formatMessage(suggestion.description, { colors: false });
573
+ yield `__FILE__:${suggestion.type}:${extensions}:${suggestion.pattern || ""}:${hidden}\t${description}`;
574
+ }
575
+ i++;
576
+ }
577
+ }
578
+ };
579
+ /**
351
580
  * The PowerShell completion generator.
352
581
  * @since 0.6.0
353
582
  */
@@ -507,4 +736,4 @@ ${escapedArgs ? ` \$completionArgs += @(${escapedArgs})
507
736
  };
508
737
 
509
738
  //#endregion
510
- export { bash, fish, pwsh, zsh };
739
+ export { bash, fish, nu, pwsh, zsh };
package/dist/facade.cjs CHANGED
@@ -264,7 +264,7 @@ function classifyResult(result, args) {
264
264
  * Handles shell completion requests.
265
265
  * @since 0.6.0
266
266
  */
267
- function handleCompletion(completionArgs, programName, parser, stdout, stderr, onCompletion, onError, colors) {
267
+ function handleCompletion(completionArgs, programName, parser, stdout, stderr, onCompletion, onError, availableShells, colors) {
268
268
  if (completionArgs.length === 0) {
269
269
  stderr("Error: Missing shell name for completion.\n");
270
270
  stderr("Usage: " + programName + " completion <shell> [args...]\n");
@@ -272,12 +272,6 @@ function handleCompletion(completionArgs, programName, parser, stdout, stderr, o
272
272
  }
273
273
  const shellName = completionArgs[0];
274
274
  const args = completionArgs.slice(1);
275
- const availableShells = {
276
- bash: require_completion.bash,
277
- fish: require_completion.fish,
278
- pwsh: require_completion.pwsh,
279
- zsh: require_completion.zsh
280
- };
281
275
  const shell = availableShells[shellName];
282
276
  if (!shell) {
283
277
  const available = [];
@@ -340,17 +334,28 @@ function run(parser, programName, args, options = {}) {
340
334
  const completionMode = options.completion?.mode ?? "both";
341
335
  const onCompletion = options.completion?.onShow ?? (() => ({}));
342
336
  if (options.completion) {
343
- if ((completionMode === "command" || completionMode === "both") && args.length >= 1 && args[0] === "completion") return handleCompletion(args.slice(1), programName, parser, stdout, stderr, onCompletion, onError, colors);
337
+ const defaultShells = {
338
+ bash: require_completion.bash,
339
+ fish: require_completion.fish,
340
+ nu: require_completion.nu,
341
+ pwsh: require_completion.pwsh,
342
+ zsh: require_completion.zsh
343
+ };
344
+ const availableShells = options.completion.shells ? {
345
+ ...defaultShells,
346
+ ...options.completion.shells
347
+ } : defaultShells;
348
+ if ((completionMode === "command" || completionMode === "both") && args.length >= 1 && args[0] === "completion") return handleCompletion(args.slice(1), programName, parser, stdout, stderr, onCompletion, onError, availableShells, colors);
344
349
  if (completionMode === "option" || completionMode === "both") for (let i = 0; i < args.length; i++) {
345
350
  const arg = args[i];
346
351
  if (arg.startsWith("--completion=")) {
347
352
  const shell = arg.slice(13);
348
353
  const completionArgs = args.slice(i + 1);
349
- return handleCompletion([shell, ...completionArgs], programName, parser, stdout, stderr, onCompletion, onError, colors);
354
+ return handleCompletion([shell, ...completionArgs], programName, parser, stdout, stderr, onCompletion, onError, availableShells, colors);
350
355
  } else if (arg === "--completion" && i + 1 < args.length) {
351
356
  const shell = args[i + 1];
352
357
  const completionArgs = args.slice(i + 2);
353
- return handleCompletion([shell, ...completionArgs], programName, parser, stdout, stderr, onCompletion, onError, colors);
358
+ return handleCompletion([shell, ...completionArgs], programName, parser, stdout, stderr, onCompletion, onError, availableShells, colors);
354
359
  }
355
360
  }
356
361
  }
package/dist/facade.d.cts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { Message } from "./message.cjs";
2
2
  import { ShowDefaultOptions } from "./doc.cjs";
3
3
  import { InferValue, Parser } from "./parser.cjs";
4
+ import { ShellCompletion } from "./completion.cjs";
4
5
 
5
6
  //#region src/facade.d.ts
6
7
 
@@ -103,6 +104,14 @@ interface RunOptions<THelp, TError> {
103
104
  * @default `"both"`
104
105
  */
105
106
  readonly mode?: "command" | "option" | "both";
107
+ /**
108
+ * Available shell completions. By default, includes `bash`, `fish`, `nu`,
109
+ * `pwsh`, and `zsh`. You can provide additional custom shell completions or
110
+ * override the defaults.
111
+ *
112
+ * @default `{ bash, fish, nu, pwsh, zsh }`
113
+ */
114
+ readonly shells?: Record<string, ShellCompletion>;
106
115
  /**
107
116
  * Callback function invoked when completion is requested. The function can
108
117
  * optionally receive an exit code parameter.
package/dist/facade.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import { Message } from "./message.js";
2
2
  import { ShowDefaultOptions } from "./doc.js";
3
3
  import { InferValue, Parser } from "./parser.js";
4
+ import { ShellCompletion } from "./completion.js";
4
5
 
5
6
  //#region src/facade.d.ts
6
7
 
@@ -103,6 +104,14 @@ interface RunOptions<THelp, TError> {
103
104
  * @default `"both"`
104
105
  */
105
106
  readonly mode?: "command" | "option" | "both";
107
+ /**
108
+ * Available shell completions. By default, includes `bash`, `fish`, `nu`,
109
+ * `pwsh`, and `zsh`. You can provide additional custom shell completions or
110
+ * override the defaults.
111
+ *
112
+ * @default `{ bash, fish, nu, pwsh, zsh }`
113
+ */
114
+ readonly shells?: Record<string, ShellCompletion>;
106
115
  /**
107
116
  * Callback function invoked when completion is requested. The function can
108
117
  * optionally receive an exit code parameter.
package/dist/facade.js CHANGED
@@ -2,7 +2,7 @@ import { formatMessage, message, optionName, text, value } from "./message.js";
2
2
  import { longestMatch, object } from "./constructs.js";
3
3
  import { formatUsage } from "./usage.js";
4
4
  import { formatDocPage } from "./doc.js";
5
- import { bash, fish, pwsh, zsh } from "./completion.js";
5
+ import { bash, fish, nu, pwsh, zsh } from "./completion.js";
6
6
  import { multiple, optional } from "./modifiers.js";
7
7
  import { string } from "./valueparser.js";
8
8
  import { argument, command, constant, flag } from "./primitives.js";
@@ -264,7 +264,7 @@ function classifyResult(result, args) {
264
264
  * Handles shell completion requests.
265
265
  * @since 0.6.0
266
266
  */
267
- function handleCompletion(completionArgs, programName, parser, stdout, stderr, onCompletion, onError, colors) {
267
+ function handleCompletion(completionArgs, programName, parser, stdout, stderr, onCompletion, onError, availableShells, colors) {
268
268
  if (completionArgs.length === 0) {
269
269
  stderr("Error: Missing shell name for completion.\n");
270
270
  stderr("Usage: " + programName + " completion <shell> [args...]\n");
@@ -272,12 +272,6 @@ function handleCompletion(completionArgs, programName, parser, stdout, stderr, o
272
272
  }
273
273
  const shellName = completionArgs[0];
274
274
  const args = completionArgs.slice(1);
275
- const availableShells = {
276
- bash,
277
- fish,
278
- pwsh,
279
- zsh
280
- };
281
275
  const shell = availableShells[shellName];
282
276
  if (!shell) {
283
277
  const available = [];
@@ -340,17 +334,28 @@ function run(parser, programName, args, options = {}) {
340
334
  const completionMode = options.completion?.mode ?? "both";
341
335
  const onCompletion = options.completion?.onShow ?? (() => ({}));
342
336
  if (options.completion) {
343
- if ((completionMode === "command" || completionMode === "both") && args.length >= 1 && args[0] === "completion") return handleCompletion(args.slice(1), programName, parser, stdout, stderr, onCompletion, onError, colors);
337
+ const defaultShells = {
338
+ bash,
339
+ fish,
340
+ nu,
341
+ pwsh,
342
+ zsh
343
+ };
344
+ const availableShells = options.completion.shells ? {
345
+ ...defaultShells,
346
+ ...options.completion.shells
347
+ } : defaultShells;
348
+ if ((completionMode === "command" || completionMode === "both") && args.length >= 1 && args[0] === "completion") return handleCompletion(args.slice(1), programName, parser, stdout, stderr, onCompletion, onError, availableShells, colors);
344
349
  if (completionMode === "option" || completionMode === "both") for (let i = 0; i < args.length; i++) {
345
350
  const arg = args[i];
346
351
  if (arg.startsWith("--completion=")) {
347
352
  const shell = arg.slice(13);
348
353
  const completionArgs = args.slice(i + 1);
349
- return handleCompletion([shell, ...completionArgs], programName, parser, stdout, stderr, onCompletion, onError, colors);
354
+ return handleCompletion([shell, ...completionArgs], programName, parser, stdout, stderr, onCompletion, onError, availableShells, colors);
350
355
  } else if (arg === "--completion" && i + 1 < args.length) {
351
356
  const shell = args[i + 1];
352
357
  const completionArgs = args.slice(i + 2);
353
- return handleCompletion([shell, ...completionArgs], programName, parser, stdout, stderr, onCompletion, onError, colors);
358
+ return handleCompletion([shell, ...completionArgs], programName, parser, stdout, stderr, onCompletion, onError, availableShells, colors);
354
359
  }
355
360
  }
356
361
  }
package/dist/index.cjs CHANGED
@@ -37,6 +37,7 @@ exports.message = require_message.message;
37
37
  exports.metavar = require_message.metavar;
38
38
  exports.multiple = require_modifiers.multiple;
39
39
  exports.normalizeUsage = require_usage.normalizeUsage;
40
+ exports.nu = require_completion.nu;
40
41
  exports.object = require_constructs.object;
41
42
  exports.option = require_primitives.option;
42
43
  exports.optionName = require_message.optionName;
package/dist/index.d.cts CHANGED
@@ -6,6 +6,6 @@ import { MultipleErrorOptions, MultipleOptions, WithDefaultError, WithDefaultOpt
6
6
  import { ArgumentErrorOptions, ArgumentOptions, CommandErrorOptions, CommandOptions, FlagErrorOptions, FlagOptions, OptionErrorOptions, OptionOptions, argument, command, constant, flag, option } from "./primitives.cjs";
7
7
  import { DocState, InferValue, Parser, ParserContext, ParserResult, Result, Suggestion, getDocPage, parse, suggest } from "./parser.cjs";
8
8
  import { LongestMatchErrorOptions, LongestMatchOptions, ObjectErrorOptions, ObjectOptions, OrErrorOptions, OrOptions, concat, group, longestMatch, merge, object, or, tuple } from "./constructs.cjs";
9
+ import { ShellCompletion, bash, fish, nu, pwsh, zsh } from "./completion.cjs";
9
10
  import { RunError, RunOptions, run } from "./facade.cjs";
10
- import { ShellCompletion, bash, fish, pwsh, zsh } from "./completion.cjs";
11
- export { ArgumentErrorOptions, ArgumentOptions, ChoiceOptions, CommandErrorOptions, CommandOptions, DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, DocState, FlagErrorOptions, FlagOptions, FloatOptions, InferValue, IntegerOptionsBigInt, IntegerOptionsNumber, LocaleOptions, LongestMatchErrorOptions, LongestMatchOptions, Message, MessageFormatOptions, MessageTerm, MultipleErrorOptions, MultipleOptions, ObjectErrorOptions, ObjectOptions, OptionErrorOptions, OptionName, OptionOptions, OrErrorOptions, OrOptions, Parser, ParserContext, ParserResult, Result, RunError, RunOptions, ShellCompletion, ShowDefaultOptions, StringOptions, Suggestion, UrlOptions, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, WithDefaultError, WithDefaultOptions, argument, bash, choice, command, concat, constant, envVar, fish, flag, float, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getDocPage, group, integer, isValueParser, locale, longestMatch, map, merge, message, metavar, multiple, normalizeUsage, object, option, optionName, optionNames, optional, or, parse, pwsh, run, string, suggest, text, tuple, url, uuid, value, values, withDefault, zsh };
11
+ export { ArgumentErrorOptions, ArgumentOptions, ChoiceOptions, CommandErrorOptions, CommandOptions, DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, DocState, FlagErrorOptions, FlagOptions, FloatOptions, InferValue, IntegerOptionsBigInt, IntegerOptionsNumber, LocaleOptions, LongestMatchErrorOptions, LongestMatchOptions, Message, MessageFormatOptions, MessageTerm, MultipleErrorOptions, MultipleOptions, ObjectErrorOptions, ObjectOptions, OptionErrorOptions, OptionName, OptionOptions, OrErrorOptions, OrOptions, Parser, ParserContext, ParserResult, Result, RunError, RunOptions, ShellCompletion, ShowDefaultOptions, StringOptions, Suggestion, UrlOptions, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, WithDefaultError, WithDefaultOptions, argument, bash, choice, command, concat, constant, envVar, fish, flag, float, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getDocPage, group, integer, isValueParser, locale, longestMatch, map, merge, message, metavar, multiple, normalizeUsage, nu, object, option, optionName, optionNames, optional, or, parse, pwsh, run, string, suggest, text, tuple, url, uuid, value, values, withDefault, zsh };
package/dist/index.d.ts CHANGED
@@ -6,6 +6,6 @@ import { MultipleErrorOptions, MultipleOptions, WithDefaultError, WithDefaultOpt
6
6
  import { ArgumentErrorOptions, ArgumentOptions, CommandErrorOptions, CommandOptions, FlagErrorOptions, FlagOptions, OptionErrorOptions, OptionOptions, argument, command, constant, flag, option } from "./primitives.js";
7
7
  import { DocState, InferValue, Parser, ParserContext, ParserResult, Result, Suggestion, getDocPage, parse, suggest } from "./parser.js";
8
8
  import { LongestMatchErrorOptions, LongestMatchOptions, ObjectErrorOptions, ObjectOptions, OrErrorOptions, OrOptions, concat, group, longestMatch, merge, object, or, tuple } from "./constructs.js";
9
+ import { ShellCompletion, bash, fish, nu, pwsh, zsh } from "./completion.js";
9
10
  import { RunError, RunOptions, run } from "./facade.js";
10
- import { ShellCompletion, bash, fish, pwsh, zsh } from "./completion.js";
11
- export { ArgumentErrorOptions, ArgumentOptions, ChoiceOptions, CommandErrorOptions, CommandOptions, DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, DocState, FlagErrorOptions, FlagOptions, FloatOptions, InferValue, IntegerOptionsBigInt, IntegerOptionsNumber, LocaleOptions, LongestMatchErrorOptions, LongestMatchOptions, Message, MessageFormatOptions, MessageTerm, MultipleErrorOptions, MultipleOptions, ObjectErrorOptions, ObjectOptions, OptionErrorOptions, OptionName, OptionOptions, OrErrorOptions, OrOptions, Parser, ParserContext, ParserResult, Result, RunError, RunOptions, ShellCompletion, ShowDefaultOptions, StringOptions, Suggestion, UrlOptions, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, WithDefaultError, WithDefaultOptions, argument, bash, choice, command, concat, constant, envVar, fish, flag, float, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getDocPage, group, integer, isValueParser, locale, longestMatch, map, merge, message, metavar, multiple, normalizeUsage, object, option, optionName, optionNames, optional, or, parse, pwsh, run, string, suggest, text, tuple, url, uuid, value, values, withDefault, zsh };
11
+ export { ArgumentErrorOptions, ArgumentOptions, ChoiceOptions, CommandErrorOptions, CommandOptions, DocEntry, DocFragment, DocFragments, DocPage, DocPageFormatOptions, DocSection, DocState, FlagErrorOptions, FlagOptions, FloatOptions, InferValue, IntegerOptionsBigInt, IntegerOptionsNumber, LocaleOptions, LongestMatchErrorOptions, LongestMatchOptions, Message, MessageFormatOptions, MessageTerm, MultipleErrorOptions, MultipleOptions, ObjectErrorOptions, ObjectOptions, OptionErrorOptions, OptionName, OptionOptions, OrErrorOptions, OrOptions, Parser, ParserContext, ParserResult, Result, RunError, RunOptions, ShellCompletion, ShowDefaultOptions, StringOptions, Suggestion, UrlOptions, Usage, UsageFormatOptions, UsageTerm, UsageTermFormatOptions, Uuid, UuidOptions, ValueParser, ValueParserResult, WithDefaultError, WithDefaultOptions, argument, bash, choice, command, concat, constant, envVar, fish, flag, float, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getDocPage, group, integer, isValueParser, locale, longestMatch, map, merge, message, metavar, multiple, normalizeUsage, nu, object, option, optionName, optionNames, optional, or, parse, pwsh, run, string, suggest, text, tuple, url, uuid, value, values, withDefault, zsh };
package/dist/index.js CHANGED
@@ -2,11 +2,11 @@ import { envVar, formatMessage, message, metavar, optionName, optionNames, text,
2
2
  import { concat, group, longestMatch, merge, object, or, tuple } from "./constructs.js";
3
3
  import { formatUsage, formatUsageTerm, normalizeUsage } from "./usage.js";
4
4
  import { formatDocPage } from "./doc.js";
5
- import { bash, fish, pwsh, zsh } from "./completion.js";
5
+ import { bash, fish, nu, pwsh, zsh } from "./completion.js";
6
6
  import { WithDefaultError, map, multiple, optional, withDefault } from "./modifiers.js";
7
7
  import { choice, float, integer, isValueParser, locale, string, url, uuid } from "./valueparser.js";
8
8
  import { argument, command, constant, flag, option } from "./primitives.js";
9
9
  import { getDocPage, parse, suggest } from "./parser.js";
10
10
  import { RunError, run } from "./facade.js";
11
11
 
12
- export { RunError, WithDefaultError, argument, bash, choice, command, concat, constant, envVar, fish, flag, float, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getDocPage, group, integer, isValueParser, locale, longestMatch, map, merge, message, metavar, multiple, normalizeUsage, object, option, optionName, optionNames, optional, or, parse, pwsh, run, string, suggest, text, tuple, url, uuid, value, values, withDefault, zsh };
12
+ export { RunError, WithDefaultError, argument, bash, choice, command, concat, constant, envVar, fish, flag, float, formatDocPage, formatMessage, formatUsage, formatUsageTerm, getDocPage, group, integer, isValueParser, locale, longestMatch, map, merge, message, metavar, multiple, normalizeUsage, nu, object, option, optionName, optionNames, optional, or, parse, pwsh, run, string, suggest, text, tuple, url, uuid, value, values, withDefault, zsh };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@optique/core",
3
- "version": "0.6.0-dev.102+50615c18",
3
+ "version": "0.6.0-dev.104+204b6712",
4
4
  "description": "Type-safe combinatorial command-line interface parser",
5
5
  "keywords": [
6
6
  "CLI",