package arg import ( "errors" "fmt" "io" "strings" "go.wit.com/lib/protobuf/argvpb" ) var useArgv bool var overrideFlags []string // pass in a "pretend" os.Args. Used for bash autocomplete // error is "good" in this case. The application exists // and finishes the output for shell autocomplete // returning "nil" means the application has to figure out what to do func ParseFlagsArgv(dest ...interface{}) (*Parser, error) { p, err := NewParser(Config{}, dest...) if err != nil { return p, err } if argvpb.PB == nil { panic("argvpb.PB is nil") } useArgv = true overrideFlags = append(overrideFlags, argvpb.Real()...) err = p.Parse(argvpb.Real()) if err != nil { fmt.Fprintf(argvpb.Stddbg, "err(%v)\n", err) fmt.Fprintln(argvpb.Stddbg, "arg.ParseFlagsArgv()", argvpb.Real()) fmt.Printf("\n") fmt.Printf("\n") fmt.Printf("err(%v)\n", err) fmt.Println("arg.ParseFlagsArgv()", argvpb.Real()) s := fmt.Sprintf("p.Parse() problem in argv err(%v)\n", err) panic(s) } if argvpb.PB.ErrCounter > 2 || argvpb.PB.OutCounter > 5 { argvpb.PB.ErrCounter = 0 argvpb.PB.OutCounter = 0 if argvpb.Len() == 0 { // user didn't enter any text and is hitting after the command itself p.WriteHelp(argvpb.Stderr) return p, errors.New("WriteHelp() worked") } // user is trying to get help for a subcommand err = p.WriteHelpForAutocomplete("", argvpb.PB.Real...) if err != nil { // fmt.Fprintf(argvpb.Stddbg, "returned from WriteHelpForAutocomplete() pb.Real(%v)\n", argvpb.PB.Real) fmt.Fprintf(argvpb.Stddbg, "WriteHelpForAutocomplete() err(%v)\n", err) } return p, errors.New("WriteHelpForAutocomplete() worked") } if argvpb.PB.GetCmd() != "" { err = p.WriteHelpForAutocomplete(argvpb.PB.Partial, argvpb.PB.Real...) fmt.Fprintf(argvpb.Stddbg, "returned from WriteHelpForAutocomplete() pb.Real(%v)\n", argvpb.PB.Real) fmt.Fprintf(argvpb.Stddbg, "returned from WriteHelpForAutocomplete(%v)\n", err) if p.match != nil { // calls back to the application. this allows the application to provide autocomplete matches p.match() } return p, errors.New("WriteHelpForAutocomplete() subcommand worked") } return p, nil } func (p *Parser) WriteHelpForAutocompleteArgv() error { fmt.Fprintln(argvpb.Stddbg, "") fmt.Fprintf(argvpb.Stddbg, "go-args.WriteHelpForAutocompleteArgv() not finished") fmt.Fprintln(argvpb.Stddbg, "arg.ParseFlagsArgv()", argvpb.PB.Real) return nil } // same as WriteHelpForSubcommand above, but can flip to STDERR and STDOUT // most shell autocomplete behavior usually wants things that way func (p *Parser) WriteHelpForAutocomplete(partialOld string, subcommand ...string) error { var automatch []string fmt.Fprintf(argvpb.Stddbg, "go-args.WriteHelpForAutocomplete() sub(%v)\n", subcommand) // return errors.New("subcommand was nil") if p == nil { return errors.New("arg.Parser == nil") } if subcommand == nil { return errors.New("subcommand was nil") } cmd, err := p.lookupCommand(subcommand...) if err != nil { fmt.Fprintf(argvpb.Stddbg, "go-args.WriteHelpForAutocomplete() lookupCommand failed err(%v)\n", err) return err } var positionals, longOptions, shortOptions, envOnlyOptions []*spec var hasVersionOption bool for _, spec := range cmd.specs { switch { case spec.positional: positionals = append(positionals, spec) case spec.long != "": longOptions = append(longOptions, spec) if spec.long == "version" { hasVersionOption = true } case spec.short != "": shortOptions = append(shortOptions, spec) case spec.short == "" && spec.long == "": envOnlyOptions = append(envOnlyOptions, spec) } } // obtain a flattened list of options from all ancestors // also determine if any ancestor has a version option spec var globals []*spec ancestor := cmd.parent for ancestor != nil { for _, spec := range ancestor.specs { if spec.long == "version" { hasVersionOption = true break } } globals = append(globals, ancestor.specs...) ancestor = ancestor.parent } if p.description != "" { fmt.Fprintln(argvpb.Stderr, p.description) } if !hasVersionOption && p.version != "" { fmt.Fprintln(argvpb.Stderr, p.version) } p.WriteUsageForSubcommand(argvpb.Stderr, subcommand...) // write the list of positionals if len(positionals) > 0 { fmt.Fprint(argvpb.Stderr, "\nPositional arguments:\n") for _, spec := range positionals { print(argvpb.Stderr, spec.placeholder, spec.help, withDefault(spec.defaultString), withEnv(spec.env)) } } // write the list of options with the short-only ones first to match the usage string if len(shortOptions)+len(longOptions) > 0 || cmd.parent == nil { fmt.Fprint(argvpb.Stderr, "\nOptions:\n") for _, spec := range shortOptions { p.printOption(argvpb.Stderr, spec) } for _, spec := range longOptions { p.printOption(argvpb.Stderr, spec) //jwc tmp := "--" + spec.long // if strings.HasPrefix(tmp, partial) { automatch = append(automatch, tmp) // } } } // write the list of global options if len(globals) > 0 { fmt.Fprint(argvpb.Stderr, "\nGlobal options:\n") for _, spec := range globals { p.printOption(argvpb.Stderr, spec) } } // write the list of built in options p.printOption(argvpb.Stderr, &spec{ cardinality: zero, long: "help", short: "h", help: "display this help and exit", }) if !hasVersionOption && p.version != "" { p.printOption(argvpb.Stderr, &spec{ cardinality: zero, long: "version", help: "display version and exit", }) } // write the list of environment only variables if len(envOnlyOptions) > 0 { fmt.Fprint(argvpb.Stderr, "\nEnvironment variables:\n") for _, spec := range envOnlyOptions { p.printEnvOnlyVar(argvpb.Stderr, spec) } } // write the list of subcommands if len(cmd.subcommands) > 0 { fmt.Fprint(argvpb.Stderr, "\nCommands:\n") for _, subcmd := range cmd.subcommands { names := append([]string{subcmd.name}, subcmd.aliases...) print(argvpb.Stderr, strings.Join(names, ", "), subcmd.help) // if strings.HasPrefix(subcmd.name, partial) { automatch = append(automatch, subcmd.name) // } } } if p.epilogue != "" { fmt.Fprintln(argvpb.Stderr, "\n"+p.epilogue) } fmt.Fprintf(argvpb.Stddbg, "go-args.WriteHelpForAutocomplete() got to the end automatch(%v)\n", automatch) // writes out the shell autocomplete matches if len(automatch) > 0 { fmt.Fprintf(argvpb.Stdout, "%s", strings.Join(automatch, " ")) } else { // fmt.Fprintf(argvpb.Stdout, "automatchBlank") } return nil } // GetUsageForSubcommand gets the commands for bash shell completetion func (p *Parser) GetUsageForSubcommand(stdout io.Writer, stderr io.Writer, partial string, s string) (string, error) { var log []string var final []string cmd, err := p.lookupCommand(s) if err != nil { return "", err } var positionals, longOptions, shortOptions []*spec for _, spec := range cmd.specs { switch { case spec.positional: positionals = append(positionals, spec) case spec.long != "": longOptions = append(longOptions, spec) case spec.short != "": shortOptions = append(shortOptions, spec) } } // write the option component of the usage message for _, spec := range shortOptions { // prefix with a space log = append(log, fmt.Sprintf(" ")) if !spec.required { // log = append(log, fmt.Sprintf("[")) } log = append(log, synopsis(spec, "-"+spec.short)) if !spec.required { // log = append(log, fmt.Sprintf("]")) } } for _, spec := range longOptions { // prefix with a space if !spec.required { // log = append(log, fmt.Sprintf("[")) } log = append(log, synopsis(spec, "--"+spec.long)) if !spec.required { // log = append(log, fmt.Sprintf("]")) } } for _, spec := range positionals { if !spec.required { // log = append(log, fmt.Sprintf("[")) } if spec.cardinality == multiple { log = append(log, fmt.Sprintf("%s [%s ...]", spec.placeholder, spec.placeholder)) } else { log = append(log, spec.placeholder) } } for _, subcmd := range cmd.subcommands { names := append([]string{subcmd.name}, subcmd.aliases...) if strings.HasPrefix(subcmd.name, partial) { final = append(final, subcmd.name) } log = append(log, strings.Join(names, ", "), subcmd.help) if stderr != nil { print(stderr, strings.Join(names, ", "), subcmd.help) } } fmt.Fprintf(stdout, "%s", strings.Join(final, " ")) return strings.Join(final, " "), nil }