-
Notifications
You must be signed in to change notification settings - Fork 9
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[feature request] call auto-complete on next command #21
Comments
Thanks for the feature request. I spent some time looking into this, and it isn't straight forward. First off, there isn't a good way to get the completion for the next command from within a Go program because it doesn't have access to the shell it was run from. The closest I see is for bash is something like With that eliminated, we need to register a completion outside of the go program. I came up with something that works for click to expandpackage main
import (
"fmt"
"os"
"os/exec"
"path/filepath"
"text/template"
"github.com/alecthomas/kong"
"github.com/riywo/loginshell"
"github.com/willabides/kongplete"
)
type rootCmd struct {
Exec execCmd `kong:"cmd"`
Greet greetCmd `kong:"cmd"`
Debug bool
InstallCompletions installCompletionsCmd `kong:"cmd"`
}
type execCmd struct {
Cmd []string `kong:"arg"`
}
func (c *execCmd) Run(ctx *kong.Context) error {
if len(c.Cmd) == 0 {
return nil
}
cmd := exec.Command(c.Cmd[0], c.Cmd[1:]...)
cmd.Stdout = ctx.Stdout
cmd.Stderr = ctx.Stderr
cmd.Stdin = os.Stdin
return cmd.Run()
}
type greetCmd struct {
Name string `kong:"arg,default=World"`
}
func (c *greetCmd) Run() error {
fmt.Printf("Hello, %s!\n", c.Name)
return nil
}
var bashTmpl = template.Must(template.New("").Parse(`
_kongplete_plain_completion_helper() {
local bin="$1"
if [ -z "$COMP_LINE" ]; then
return
fi
COMPREPLY=(
$(
COMP_LINE=$COMP_LINE \
COMP_POINT=$COMP_POINT \
"$bin"
)
)
}
_kongplete_exec_completion_helper() {
local bin="$1"
local args=()
local arg
local arg_index=0
local exec_index=0
local dash_dash_index=0
local arg_count=$COMP_CWORD
while [ $arg_index -lt $arg_count ]; do
arg="${COMP_WORDS[arg_index]}"
if [ "$arg" = "exec" ]; then
exec_index=$arg_index
fi
if [ $exec_index -gt 0 ]; then
if [ "$arg" = "--" ]; then
dash_dash_index=$arg_index
break
fi
fi
args+=("$arg")
arg_index=$((arg_index + 1))
done
if [ $dash_dash_index -gt 0 ]; then
_command_offset $((dash_dash_index + 1))
return
fi
_kongplete_plain_completion_helper "$bin"
}
_{{ .cmd }}_plain_completion() {
_kongplete_plain_completion_helper "{{ .bin }}"
}
_{{ .cmd }}_exec_completion() {
_kongplete_exec_completion_helper "{{ .bin }}"
}
complete -F _{{ .cmd }}_plain_completion {{ .cmd }}
if declare -f _command_offset >/dev/null 2>&1; then
complete -F _{{ .cmd }}_exec_completion {{ .cmd }}
fi
`))
type installCompletionsCmd struct{}
func (c *installCompletionsCmd) Run(ctx *kong.Context) error {
shell, err := loginshell.Shell()
if err != nil {
return fmt.Errorf("couldn't determine user's shell: %w", err)
}
cmd := ctx.Model.Name
bin, err := os.Executable()
if err != nil {
return err
}
bin, err = filepath.Abs(bin)
if err != nil {
return err
}
if filepath.Base(shell) == "bash" {
return bashTmpl.Execute(ctx.Stdout, map[string]string{
"cmd": cmd,
"bin": bin,
})
}
return (&kongplete.InstallCompletions{}).BeforeApply(ctx)
}
func main() {
var cmd rootCmd
// Create a kong parser as usual, but don't run Parse quite yet.
parser := kong.Must(&cmd, kong.UsageOnError())
// Run kongplete.Complete to handle completion requests
kongplete.Complete(parser)
// Proceed as normal after kongplete.Complete.
ctx, err := parser.Parse(os.Args[1:])
parser.FatalIfErrorf(err)
parser.FatalIfErrorf(ctx.Run())
} I haven't done any significant testing of this, so it's status is strictly "works on my machine". I'm not sure if I would want to incorporate something like this into kongplete or not. It's more shell scripting than I want to be responsible for maintaining. I might just make it easier to hook custom scripts into install completions and use this as an example. I will probably be adding an exec command to bindown soon, so I'm interested in seeing what you come up with. |
So my program behaves a bit like sudo. Ie: you can use it to exec other commands. Technically it does it as a sub-command, ie:
aws-sso exec [flags] <command> [flags]
Doesn't seem to be a way to tell kongplete to do basically what sudo does? https://github.com/scop/bash-completion/blob/master/completions/sudo#L17
The text was updated successfully, but these errors were encountered: