diff --git a/completion.go b/completion.go index 4379f7557e..55321a5e2b 100644 --- a/completion.go +++ b/completion.go @@ -2,7 +2,6 @@ package cli import ( "context" - "embed" "fmt" "sort" ) @@ -10,36 +9,28 @@ import ( const ( completionCommandName = "completion" - // This flag is supposed to only be used by the completion script itself, to generate completions on the fly. + // This flag is supposed to only be used by the completion script itself to generate completions on the fly. completionFlagName = "generate-shell-completion" completionFlag = "--" + completionFlagName ) -var ( - //go:embed autocomplete - autoCompleteFS embed.FS - - shellCompletions = map[string]renderCompletionFunc{ - "bash": getCompletion("autocomplete/bash_autocomplete"), - "ps": getCompletion("autocomplete/powershell_autocomplete.ps1"), - "zsh": func(cmd *Command, appName string) (string, error) { - return genZshCompletion(appName), nil - }, - "fish": func(c *Command, appName string) (string, error) { - return c.ToFishCompletion() - }, - } -) +var shellCompletions = map[string]renderCompletionFunc{ + "bash": func(cmd *Command, appName string) (string, error) { + return genBashCompletion(appName), nil + }, + "zsh": func(cmd *Command, appName string) (string, error) { + return genZshCompletion(appName), nil + }, + "fish": func(c *Command, appName string) (string, error) { + return c.ToFishCompletion() + }, + "pwsh": func(cmd *Command, appName string) (string, error) { + return genPwshCompletion(), nil + }, +} type renderCompletionFunc func(cmd *Command, appName string) (string, error) -func getCompletion(s string) renderCompletionFunc { - return func(cmd *Command, appName string) (string, error) { - b, err := autoCompleteFS.ReadFile(s) - return string(b), err - } -} - func buildCompletionCommand(appName string) *Command { return &Command{ Name: completionCommandName, @@ -81,11 +72,49 @@ func printShellCompletion(_ context.Context, cmd *Command, appName string) error return nil } +func genBashCompletion(appName string) string { + return fmt.Sprintf(`#!/usr/env/bin bash + +# This is a shell completion script auto-generated by https://github.com/urfave/cli for bash. + +# Macs have bash3 for which the bash-completion package doesn't include +# _init_completion. This is a minimal version of that function. +_cli_init_completion() { + COMPREPLY=() + _get_comp_words_by_ref "$@" cur prev words cword +} + +_cli_bash_autocomplete() { + if [[ "${COMP_WORDS[0]}" != "source" ]]; then + local cur opts base words + COMPREPLY=() + cur="${COMP_WORDS[COMP_CWORD]}" + if declare -F _init_completion >/dev/null 2>&1; then + _init_completion -n "=:" || return + else + _cli_init_completion -n "=:" || return + fi + words=("${words[@]:0:$cword}") + if [[ "$cur" == "-"* ]]; then + requestComp="${words[*]} ${cur} --generate-shell-completion" + else + requestComp="${words[*]} --generate-shell-completion" + fi + opts=$(eval "${requestComp}" 2>/dev/null) + COMPREPLY=($(compgen -W "${opts}" -- ${cur})) + return 0 + fi +} + +complete -o bashdefault -o default -o nospace -F _cli_bash_autocomplete %[1]s +`, appName) +} + func genZshCompletion(appName string) string { return fmt.Sprintf(`#compdef %[1]s compdef _%[1]s %[1]s -# This is a shell completion script auto-generated by https://github.com/urfave/cli. +# This is a shell completion script auto-generated by https://github.com/urfave/cli for zsh. _%[1]s() { local -a opts # Declare a local array @@ -113,3 +142,15 @@ if [ "$funcstack[1]" = "_%[1]s" ]; then fi `, appName) } + +func genPwshCompletion() string { + return fmt.Sprintf(`$fn = $($MyInvocation.MyCommand.Name) +$name = $fn -replace "(.*)\.ps1$", '$1' +Register-ArgumentCompleter -Native -CommandName $name -ScriptBlock { + param($commandName, $wordToComplete, $cursorPosition) + $other = "$wordToComplete --generate-shell-completion" + Invoke-Expression $other | ForEach-Object { + [System.Management.Automation.CompletionResult]::new($_, $_, 'ParameterValue', $_) + } + }`) +}