gum/completion/zsh.go
2022-08-05 02:33:40 -04:00

147 lines
3.5 KiB
Go

package completion
import (
"fmt"
"io"
"strings"
"github.com/alecthomas/kong"
)
// Zsh is zsh completion generator.
type Zsh struct{}
// Run generates zsh completion script.
func (z Zsh) Run(ctx *kong.Context) error {
var out strings.Builder
format := `#compdef %[1]s
# zsh completion for %[1]s
# generated by gum completion
`
fmt.Fprintf(&out, format, ctx.Model.Name)
z.gen(&out, ctx.Model.Node)
_, err := fmt.Fprint(ctx.Stdout, out.String())
if err != nil {
return fmt.Errorf("unable to generate zsh completion: %w", err)
}
return nil
}
func (z Zsh) writeFlag(buf io.StringWriter, f *kong.Flag) {
var str strings.Builder
str.WriteString(" ")
if f.Short != 0 {
str.WriteString("'(")
str.WriteString(fmt.Sprintf("-%c --%s", f.Short, f.Name))
if !f.IsBool() {
str.WriteString("=")
}
str.WriteString(")'")
str.WriteString("{")
str.WriteString(fmt.Sprintf("-%c,--%s", f.Short, f.Name))
if !f.IsBool() {
str.WriteString("=")
}
str.WriteString("}")
str.WriteString("\"")
} else {
str.WriteString("\"")
str.WriteString(fmt.Sprintf("--%s", f.Name))
if !f.IsBool() {
str.WriteString("=")
}
}
str.WriteString(fmt.Sprintf("[%s]", f.Help))
if !f.IsBool() {
str.WriteString(":")
str.WriteString(strings.ToLower(f.Help))
str.WriteString(":")
}
values := flagPossibleValues(f)
if len(values) > 0 {
str.WriteString("(")
for i, v := range f.EnumSlice() {
str.WriteString(v)
if i < len(values)-1 {
str.WriteString(" ")
}
}
str.WriteString(")")
}
str.WriteString("\"")
writeString(buf, str.String())
}
func (z Zsh) writeFlags(buf io.StringWriter, cmd *kong.Node) {
for i, f := range cmd.Flags {
if f.Hidden {
continue
}
z.writeFlag(buf, f)
if i < len(cmd.Flags)-1 {
writeString(buf, " \\\n")
}
}
}
func (z Zsh) writeCommand(buf io.StringWriter, c *kong.Node) {
writeString(buf, fmt.Sprintf(" \"%s[%s]\"", c.Name, c.Help))
}
func (z Zsh) writeCommands(buf io.StringWriter, cmd *kong.Node) {
for i, c := range cmd.Children {
if c == nil || c.Hidden {
continue
}
z.writeCommand(buf, c)
if i < len(cmd.Children)-1 {
_, _ = buf.WriteString(" \\")
}
writeString(buf, "\n")
}
}
func (z Zsh) gen(buf io.StringWriter, cmd *kong.Node) {
for _, c := range cmd.Children {
if c == nil || c.Hidden {
continue
}
z.gen(buf, c)
}
cmdName := commandName(cmd)
writeString(buf, fmt.Sprintf("_%s() {\n", cmdName))
if hasCommands(cmd) {
writeString(buf, " local line state\n")
}
writeString(buf, " _arguments -C \\\n")
z.writeFlags(buf, cmd)
if hasCommands(cmd) {
writeString(buf, " \\\n")
writeString(buf, " \"1: :->cmds\" \\\n")
writeString(buf, " \"*::arg:->args\"\n")
writeString(buf, " case \"$state\" in\n")
writeString(buf, " cmds)\n")
writeString(buf, fmt.Sprintf(" _values \"%s command\" \\\n", cmdName))
z.writeCommands(buf, cmd)
writeString(buf, " ;;\n")
writeString(buf, " args)\n")
writeString(buf, " case \"$line[1]\" in\n")
for _, c := range cmd.Children {
if c == nil || c.Hidden {
continue
}
writeString(buf, fmt.Sprintf(" %s)\n", c.Name))
writeString(buf, fmt.Sprintf(" _%s\n", commandName(c)))
writeString(buf, " ;;\n")
}
writeString(buf, " esac\n")
writeString(buf, " ;;\n")
writeString(buf, " esac\n")
}
// writeArgAliases(buf, cmd)
writeString(buf, "\n")
writeString(buf, "}\n\n")
}