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") }