diff --git a/command/command.ts b/command/command.ts index cca6cd61..1a6c5089 100644 --- a/command/command.ts +++ b/command/command.ts @@ -587,9 +587,10 @@ export class Command< typeof handler.values !== "undefined") ) { const completeHandler: ICompleteHandler = ( + token: string, cmd: Command, parent?: Command, - ) => handler.complete?.(cmd, parent) || []; + ) => handler.complete?.(token, cmd, parent) || []; this.complete(name, completeHandler, options); } diff --git a/command/completions/_fish_completions_generator.ts b/command/completions/_fish_completions_generator.ts index 9ae9fe6a..dfa831a7 100644 --- a/command/completions/_fish_completions_generator.ts +++ b/command/completions/_fish_completions_generator.ts @@ -108,8 +108,10 @@ ${this.generateCompletions(this.cmd).trim()} required: true, standalone: option.standalone, arguments: option.args.length - ? this.getCompletionCommand( + ? this.getOptionCompletionCommand( option.args[0].action + " " + getCompletionsPath(command), + shortOption, + longOption, ) : undefined, }); @@ -137,8 +139,26 @@ ${this.generateCompletions(this.cmd).trim()} return cmd.join(" "); } - private getCompletionCommand(cmd: string): string { - return `'(${this.cmd.getName()} completions complete ${cmd.trim()})'`; + private getOptionCompletionCommand( + cmd: string, + shortOption: string | undefined, + longOption: string | undefined, + ): string { + const makeFilter = (s: string) => `string replace -- ${s} ""`; + const filters = []; + shortOption && filters.push(makeFilter(`-${shortOption}`)); + longOption && filters.push(makeFilter(`--${longOption}`)); + return this.getCompletionCommand(cmd, filters); + } + + private getCompletionCommand(cmd: string, filters: string[] = []): string { + const cmds = [ + "commandline -tc", + ...filters, + `string replace -r -- "^\-*" ""`, + ]; + const token = cmds.join(" | "); + return `'(${this.cmd.getName()} completions complete -t (${token}) ${cmd.trim()})'`; } } diff --git a/command/completions/complete.ts b/command/completions/complete.ts index 5c54f94e..b231b386 100644 --- a/command/completions/complete.ts +++ b/command/completions/complete.ts @@ -3,33 +3,42 @@ import { UnknownCompletionCommand } from "../_errors.ts"; import type { ICompletion } from "../types.ts"; /** Execute auto completion method of command and action. */ -export class CompleteCommand - extends Command]> { +export class CompleteCommand extends Command< + { token: string }, + [action: string, commandNames?: Array] +> { public constructor(cmd?: Command) { super(); this.description("Get completions for given action from given command.") + .option("-t, --token [t:string]", "the current cmd token.") .arguments(" [command...:string]") - .action(async (_, action: string, commandNames?: Array) => { - let parent: Command | undefined; - const completeCommand: Command = commandNames - ?.reduce((cmd: Command, name: string): Command => { - parent = cmd; - const childCmd: Command | undefined = cmd.getCommand(name, false); - if (!childCmd) { - throw new UnknownCompletionCommand(name, cmd.getCommands()); - } - return childCmd; - }, cmd || this.getMainCommand()) ?? (cmd || this.getMainCommand()); + .action( + async ( + { token = "" }, + action: string, + commandNames?: Array, + ) => { + let parent: Command | undefined; + const completeCommand: Command = commandNames + ?.reduce((cmd: Command, name: string): Command => { + parent = cmd; + const childCmd: Command | undefined = cmd.getCommand(name, false); + if (!childCmd) { + throw new UnknownCompletionCommand(name, cmd.getCommands()); + } + return childCmd; + }, cmd || this.getMainCommand()) ?? (cmd || this.getMainCommand()); - const completion: ICompletion | undefined = completeCommand - .getCompletion(action); - const result: Array = - await completion?.complete(completeCommand, parent) ?? []; + const completion: ICompletion | undefined = completeCommand + .getCompletion(action); + const result: Array = + await completion?.complete(token, completeCommand, parent) ?? []; - if (result?.length) { - Deno.stdout.writeSync(new TextEncoder().encode(result.join("\n"))); - } - }) + if (result?.length) { + Deno.stdout.writeSync(new TextEncoder().encode(result.join("\n"))); + } + }, + ) .reset(); } } diff --git a/command/test/integration/fixtures/completions_generate_fish.out b/command/test/integration/fixtures/completions_generate_fish.out index cfde4a7e..7c7097df 100644 --- a/command/test/integration/fixtures/completions_generate_fish.out +++ b/command/test/integration/fixtures/completions_generate_fish.out @@ -25,54 +25,54 @@ end complete -c completions-test -n '__fish_completions_test_using_command __completions_test' -s h -l help -x -k -f -d 'Show this help.' complete -c completions-test -n '__fish_completions_test_using_command __completions_test' -s V -l version -x -k -f -d 'Show the version number for this program.' -complete -c completions-test -n '__fish_completions_test_using_command __completions_test' -s g -l global -k -f -r -a '(completions-test completions complete boolean)' -d 'Foo option.' -complete -c completions-test -n '__fish_completions_test_using_command __completions_test' -s m -l main -k -f -r -a '(completions-test completions complete boolean)' -d 'Bar option.' -complete -c completions-test -n '__fish_completions_test_using_command __completions_test' -s c -l color -k -f -r -a '(completions-test completions complete color)' -d 'Color option.' +complete -c completions-test -n '__fish_completions_test_using_command __completions_test' -s g -l global -k -f -r -a '(completions-test completions complete -t (commandline -tc | string replace -- -g "" | string replace -- --global "" | string replace -r -- "^-*" "") boolean)' -d 'Foo option.' +complete -c completions-test -n '__fish_completions_test_using_command __completions_test' -s m -l main -k -f -r -a '(completions-test completions complete -t (commandline -tc | string replace -- -m "" | string replace -- --main "" | string replace -r -- "^-*" "") boolean)' -d 'Bar option.' +complete -c completions-test -n '__fish_completions_test_using_command __completions_test' -s c -l color -k -f -r -a '(completions-test completions complete -t (commandline -tc | string replace -- -c "" | string replace -- --color "" | string replace -r -- "^-*" "") color)' -d 'Color option.' complete -c completions-test -n '__fish_completions_test_using_command __completions_test' -k -f -a foo -d 'Foo command.' complete -c completions-test -n '__fish_completions_test_using_command __completions_test_foo' -s h -l help -x -k -f -d 'Show this help.' -complete -c completions-test -n '__fish_completions_test_using_command __completions_test_foo' -s g -l global -k -f -r -a '(completions-test completions complete boolean foo)' -d 'Foo option.' +complete -c completions-test -n '__fish_completions_test_using_command __completions_test_foo' -s g -l global -k -f -r -a '(completions-test completions complete -t (commandline -tc | string replace -- -g "" | string replace -- --global "" | string replace -r -- "^-*" "") boolean foo)' -d 'Foo option.' complete -c completions-test -n '__fish_completions_test_using_command __completions_test_foo' -s f -l foo -k -f -d 'Foo option.' complete -c completions-test -n '__fish_completions_test_using_command __completions_test' -k -f -a help -d 'Show this help or the help of a sub-command.' -complete -c completions-test -n '__fish_completions_test_using_command __completions_test_help' -k -f -a '(completions-test completions complete command help)' +complete -c completions-test -n '__fish_completions_test_using_command __completions_test_help' -k -f -a '(completions-test completions complete -t (commandline -tc | string replace -r -- "^-*" "") command help)' complete -c completions-test -n '__fish_completions_test_using_command __completions_test_help' -s h -l help -x -k -f -d 'Show this help.' -complete -c completions-test -n '__fish_completions_test_using_command __completions_test_help' -s g -l global -k -f -r -a '(completions-test completions complete boolean help)' -d 'Foo option.' +complete -c completions-test -n '__fish_completions_test_using_command __completions_test_help' -s g -l global -k -f -r -a '(completions-test completions complete -t (commandline -tc | string replace -- -g "" | string replace -- --global "" | string replace -r -- "^-*" "") boolean help)' -d 'Foo option.' complete -c completions-test -n '__fish_completions_test_using_command __completions_test_foo' -k -f -a bar -d 'Bar command.' complete -c completions-test -n '__fish_completions_test_using_command __completions_test_foo_bar' -s h -l help -x -k -f -d 'Show this help.' -complete -c completions-test -n '__fish_completions_test_using_command __completions_test_foo_bar' -s g -l global -k -f -r -a '(completions-test completions complete boolean foo bar)' -d 'Foo option.' +complete -c completions-test -n '__fish_completions_test_using_command __completions_test_foo_bar' -s g -l global -k -f -r -a '(completions-test completions complete -t (commandline -tc | string replace -- -g "" | string replace -- --global "" | string replace -r -- "^-*" "") boolean foo bar)' -d 'Foo option.' complete -c completions-test -n '__fish_completions_test_using_command __completions_test_foo_bar' -s b -l bar -k -f -d 'Bar option.' complete -c completions-test -n '__fish_completions_test_using_command __completions_test' -k -f -a help -d 'Show this help or the help of a sub-command.' -complete -c completions-test -n '__fish_completions_test_using_command __completions_test_help' -k -f -a '(completions-test completions complete command help)' +complete -c completions-test -n '__fish_completions_test_using_command __completions_test_help' -k -f -a '(completions-test completions complete -t (commandline -tc | string replace -r -- "^-*" "") command help)' complete -c completions-test -n '__fish_completions_test_using_command __completions_test_help' -s h -l help -x -k -f -d 'Show this help.' -complete -c completions-test -n '__fish_completions_test_using_command __completions_test_help' -s g -l global -k -f -r -a '(completions-test completions complete boolean help)' -d 'Foo option.' +complete -c completions-test -n '__fish_completions_test_using_command __completions_test_help' -s g -l global -k -f -r -a '(completions-test completions complete -t (commandline -tc | string replace -- -g "" | string replace -- --global "" | string replace -r -- "^-*" "") boolean help)' -d 'Foo option.' complete -c completions-test -n '__fish_completions_test_using_command __completions_test' -k -f -a help -d 'Show this help or the help of a sub-command.' -complete -c completions-test -n '__fish_completions_test_using_command __completions_test_help' -k -f -a '(completions-test completions complete command help)' +complete -c completions-test -n '__fish_completions_test_using_command __completions_test_help' -k -f -a '(completions-test completions complete -t (commandline -tc | string replace -r -- "^-*" "") command help)' complete -c completions-test -n '__fish_completions_test_using_command __completions_test_help' -s h -l help -x -k -f -d 'Show this help.' -complete -c completions-test -n '__fish_completions_test_using_command __completions_test_help' -s g -l global -k -f -r -a '(completions-test completions complete boolean help)' -d 'Foo option.' +complete -c completions-test -n '__fish_completions_test_using_command __completions_test_help' -s g -l global -k -f -r -a '(completions-test completions complete -t (commandline -tc | string replace -- -g "" | string replace -- --global "" | string replace -r -- "^-*" "") boolean help)' -d 'Foo option.' complete -c completions-test -n '__fish_completions_test_using_command __completions_test' -k -f -a completions -d 'Generate shell completions.' complete -c completions-test -n '__fish_completions_test_using_command __completions_test_completions' -s h -l help -x -k -f -d 'Show this help.' -complete -c completions-test -n '__fish_completions_test_using_command __completions_test_completions' -s g -l global -k -f -r -a '(completions-test completions complete boolean completions)' -d 'Foo option.' +complete -c completions-test -n '__fish_completions_test_using_command __completions_test_completions' -s g -l global -k -f -r -a '(completions-test completions complete -t (commandline -tc | string replace -- -g "" | string replace -- --global "" | string replace -r -- "^-*" "") boolean completions)' -d 'Foo option.' complete -c completions-test -n '__fish_completions_test_using_command __completions_test' -k -f -a help -d 'Show this help or the help of a sub-command.' -complete -c completions-test -n '__fish_completions_test_using_command __completions_test_help' -k -f -a '(completions-test completions complete command help)' +complete -c completions-test -n '__fish_completions_test_using_command __completions_test_help' -k -f -a '(completions-test completions complete -t (commandline -tc | string replace -r -- "^-*" "") command help)' complete -c completions-test -n '__fish_completions_test_using_command __completions_test_help' -s h -l help -x -k -f -d 'Show this help.' -complete -c completions-test -n '__fish_completions_test_using_command __completions_test_help' -s g -l global -k -f -r -a '(completions-test completions complete boolean help)' -d 'Foo option.' +complete -c completions-test -n '__fish_completions_test_using_command __completions_test_help' -s g -l global -k -f -r -a '(completions-test completions complete -t (commandline -tc | string replace -- -g "" | string replace -- --global "" | string replace -r -- "^-*" "") boolean help)' -d 'Foo option.' complete -c completions-test -n '__fish_completions_test_using_command __completions_test_completions' -k -f -a bash -d 'Generate shell completions for bash.' complete -c completions-test -n '__fish_completions_test_using_command __completions_test_completions_bash' -s h -l help -x -k -f -d 'Show this help.' -complete -c completions-test -n '__fish_completions_test_using_command __completions_test_completions_bash' -s g -l global -k -f -r -a '(completions-test completions complete boolean completions bash)' -d 'Foo option.' +complete -c completions-test -n '__fish_completions_test_using_command __completions_test_completions_bash' -s g -l global -k -f -r -a '(completions-test completions complete -t (commandline -tc | string replace -- -g "" | string replace -- --global "" | string replace -r -- "^-*" "") boolean completions bash)' -d 'Foo option.' complete -c completions-test -n '__fish_completions_test_using_command __completions_test' -k -f -a help -d 'Show this help or the help of a sub-command.' -complete -c completions-test -n '__fish_completions_test_using_command __completions_test_help' -k -f -a '(completions-test completions complete command help)' +complete -c completions-test -n '__fish_completions_test_using_command __completions_test_help' -k -f -a '(completions-test completions complete -t (commandline -tc | string replace -r -- "^-*" "") command help)' complete -c completions-test -n '__fish_completions_test_using_command __completions_test_help' -s h -l help -x -k -f -d 'Show this help.' -complete -c completions-test -n '__fish_completions_test_using_command __completions_test_help' -s g -l global -k -f -r -a '(completions-test completions complete boolean help)' -d 'Foo option.' +complete -c completions-test -n '__fish_completions_test_using_command __completions_test_help' -s g -l global -k -f -r -a '(completions-test completions complete -t (commandline -tc | string replace -- -g "" | string replace -- --global "" | string replace -r -- "^-*" "") boolean help)' -d 'Foo option.' complete -c completions-test -n '__fish_completions_test_using_command __completions_test_completions' -k -f -a fish -d 'Generate shell completions for fish.' complete -c completions-test -n '__fish_completions_test_using_command __completions_test_completions_fish' -s h -l help -x -k -f -d 'Show this help.' -complete -c completions-test -n '__fish_completions_test_using_command __completions_test_completions_fish' -s g -l global -k -f -r -a '(completions-test completions complete boolean completions fish)' -d 'Foo option.' +complete -c completions-test -n '__fish_completions_test_using_command __completions_test_completions_fish' -s g -l global -k -f -r -a '(completions-test completions complete -t (commandline -tc | string replace -- -g "" | string replace -- --global "" | string replace -r -- "^-*" "") boolean completions fish)' -d 'Foo option.' complete -c completions-test -n '__fish_completions_test_using_command __completions_test' -k -f -a help -d 'Show this help or the help of a sub-command.' -complete -c completions-test -n '__fish_completions_test_using_command __completions_test_help' -k -f -a '(completions-test completions complete command help)' +complete -c completions-test -n '__fish_completions_test_using_command __completions_test_help' -k -f -a '(completions-test completions complete -t (commandline -tc | string replace -r -- "^-*" "") command help)' complete -c completions-test -n '__fish_completions_test_using_command __completions_test_help' -s h -l help -x -k -f -d 'Show this help.' -complete -c completions-test -n '__fish_completions_test_using_command __completions_test_help' -s g -l global -k -f -r -a '(completions-test completions complete boolean help)' -d 'Foo option.' +complete -c completions-test -n '__fish_completions_test_using_command __completions_test_help' -s g -l global -k -f -r -a '(completions-test completions complete -t (commandline -tc | string replace -- -g "" | string replace -- --global "" | string replace -r -- "^-*" "") boolean help)' -d 'Foo option.' complete -c completions-test -n '__fish_completions_test_using_command __completions_test_completions' -k -f -a zsh -d 'Generate shell completions for zsh.' complete -c completions-test -n '__fish_completions_test_using_command __completions_test_completions_zsh' -s h -l help -x -k -f -d 'Show this help.' -complete -c completions-test -n '__fish_completions_test_using_command __completions_test_completions_zsh' -s g -l global -k -f -r -a '(completions-test completions complete boolean completions zsh)' -d 'Foo option.' +complete -c completions-test -n '__fish_completions_test_using_command __completions_test_completions_zsh' -s g -l global -k -f -r -a '(completions-test completions complete -t (commandline -tc | string replace -- -g "" | string replace -- --global "" | string replace -r -- "^-*" "") boolean completions zsh)' -d 'Foo option.' complete -c completions-test -n '__fish_completions_test_using_command __completions_test' -k -f -a help -d 'Show this help or the help of a sub-command.' -complete -c completions-test -n '__fish_completions_test_using_command __completions_test_help' -k -f -a '(completions-test completions complete command help)' +complete -c completions-test -n '__fish_completions_test_using_command __completions_test_help' -k -f -a '(completions-test completions complete -t (commandline -tc | string replace -r -- "^-*" "") command help)' complete -c completions-test -n '__fish_completions_test_using_command __completions_test_help' -s h -l help -x -k -f -d 'Show this help.' -complete -c completions-test -n '__fish_completions_test_using_command __completions_test_help' -s g -l global -k -f -r -a '(completions-test completions complete boolean help)' -d 'Foo option.' +complete -c completions-test -n '__fish_completions_test_using_command __completions_test_help' -s g -l global -k -f -r -a '(completions-test completions complete -t (commandline -tc | string replace -- -g "" | string replace -- --global "" | string replace -r -- "^-*" "") boolean help)' -d 'Foo option.' diff --git a/command/type.ts b/command/type.ts index 77c8433e..8e7457d2 100644 --- a/command/type.ts +++ b/command/type.ts @@ -46,6 +46,7 @@ export abstract class Type { * values from the values method are used. */ public complete?( + token: string, // deno-lint-ignore no-explicit-any cmd: Command, // deno-lint-ignore no-explicit-any diff --git a/command/types.ts b/command/types.ts index 57010b1f..d46de1de 100644 --- a/command/types.ts +++ b/command/types.ts @@ -208,7 +208,11 @@ export type ICompleteHandler< PG extends Record | void = any, // deno-lint-ignore no-explicit-any P extends Command | undefined = any, -> = (cmd: Command, parent?: Command) => CompleteHandlerResult; +> = ( + token: string, + cmd: Command, + parent?: Command, +) => CompleteHandlerResult; /** Help callback method to print the help. Invoked by the `--help` option and `help` command and the `.getHelp()` and `.showHelp()` method's. */ export type IHelpHandler< diff --git a/command/types/child_command.ts b/command/types/child_command.ts index 542e841f..1d148748 100644 --- a/command/types/child_command.ts +++ b/command/types/child_command.ts @@ -11,7 +11,7 @@ export class ChildCommandType extends StringType { } /** Complete child command names. */ - public complete(cmd: Command): string[] { + public complete(_token: string, cmd: Command): string[] { return (this.#cmd ?? cmd)?.getCommands(false) .map((cmd: Command) => cmd.getName()) || []; } diff --git a/command/types/command.ts b/command/types/command.ts index 4722782b..3df78ee8 100644 --- a/command/types/command.ts +++ b/command/types/command.ts @@ -4,7 +4,7 @@ import { StringType } from "./string.ts"; /** String type with auto completion of sibling command names. */ export class CommandType extends StringType { /** Complete sub-command names of global parent command. */ - public complete(_cmd: Command, parent?: Command): string[] { + public complete(_token: string, _cmd: Command, parent?: Command): string[] { return parent?.getCommands(false) .map((cmd: Command) => cmd.getName()) || []; }