From 5515dc747cf0362bc26873b8ef8854fb82b32b15 Mon Sep 17 00:00:00 2001 From: Marcus Hultman Date: Sat, 3 Jul 2021 10:57:41 -0400 Subject: [PATCH] add token argument to ICompleteHandler Sometimes you want to provide completion alternatives based on the commandline buffer. This adds `token` (string) to both option- and argument completion, containing the partially written word to complete. --- command/command.ts | 3 ++- .../completions/_fish_completions_generator.ts | 17 +++++++++++++---- command/completions/complete.ts | 7 ++++--- command/type.ts | 1 + command/types.ts | 2 +- command/types/child_command.ts | 2 +- command/types/command.ts | 2 +- 7 files changed, 23 insertions(+), 11 deletions(-) diff --git a/command/command.ts b/command/command.ts index cca6cd61d..1a6c50894 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 9ae9fe6a8..1329e11f2 100644 --- a/command/completions/_fish_completions_generator.ts +++ b/command/completions/_fish_completions_generator.ts @@ -73,7 +73,7 @@ ${this.generateCompletions(this.cmd).trim()} if (commandArgs.length) { result += "\n" + this.complete(command, { arguments: commandArgs.length - ? this.getCompletionCommand( + ? this.getArgumentCompletionCommand( commandArgs[0].action + " " + getCompletionsPath(command), ) : undefined, @@ -108,7 +108,9 @@ ${this.generateCompletions(this.cmd).trim()} required: true, standalone: option.standalone, arguments: option.args.length - ? this.getCompletionCommand( + ? this.getOptionCompletionCommand( + shortOption, + longOption, option.args[0].action + " " + getCompletionsPath(command), ) : undefined, @@ -137,8 +139,15 @@ ${this.generateCompletions(this.cmd).trim()} return cmd.join(" "); } - private getCompletionCommand(cmd: string): string { - return `'(${this.cmd.getName()} completions complete ${cmd.trim()})'`; + private getArgumentCompletionCommand(cmd: string): string { + const token = `commandline -tc`; + return `'(${this.cmd.getName()} completions complete -t (${token}) ${cmd.trim()})'`; + } + + private getOptionCompletionCommand(shortOption: string | undefined, longOption: string | undefined, cmd: string): string { + const pipeReplace = (prefix: string, opt?: string) => opt ? `| string replace -- ${prefix}${opt} ""` : ''; + const token = `commandline -tc ${pipeReplace('-', shortOption)} ${pipeReplace('--', longOption)}`; + return `'(${this.cmd.getName()} completions complete -t (${token}) ${cmd.trim()})'`; } } diff --git a/command/completions/complete.ts b/command/completions/complete.ts index 5c54f94e3..b995ffed7 100644 --- a/command/completions/complete.ts +++ b/command/completions/complete.ts @@ -4,12 +4,13 @@ import type { ICompletion } from "../types.ts"; /** Execute auto completion method of command and action. */ export class CompleteCommand - extends Command]> { + 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) => { + .action(async ({ token = '' }, action: string, commandNames?: Array) => { let parent: Command | undefined; const completeCommand: Command = commandNames ?.reduce((cmd: Command, name: string): Command => { @@ -24,7 +25,7 @@ export class CompleteCommand const completion: ICompletion | undefined = completeCommand .getCompletion(action); const result: Array = - await completion?.complete(completeCommand, parent) ?? []; + await completion?.complete(token, completeCommand, parent) ?? []; if (result?.length) { Deno.stdout.writeSync(new TextEncoder().encode(result.join("\n"))); diff --git a/command/type.ts b/command/type.ts index 77c8433e1..8e7457d2b 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 57010b1f2..c0a8f30ef 100644 --- a/command/types.ts +++ b/command/types.ts @@ -208,7 +208,7 @@ 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 542e841fb..1d1487488 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 4722782b6..3df78ee8d 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()) || []; }