diff --git a/Makefile b/Makefile index 4255c53..3c6bbb6 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ # git remote add github git@github.com:wit-go/gui-debugger.git -all: goimports vet +all: auto.pb.go goimports vet @echo common init code for common packages goimports: @@ -8,3 +8,11 @@ goimports: vet: @GO111MODULE=off go vet + +clean: + rm -f *.pb.go *.patch + -rm -f go.* + go-mod-clean purge + +auto.pb.go: auto.proto + autogenpb --proto auto.proto diff --git a/auto.proto b/auto.proto new file mode 100644 index 0000000..82a094d --- /dev/null +++ b/auto.proto @@ -0,0 +1,26 @@ +// Copyright 2025 WIT.COM Inc Licensed GPL 3.0 + +syntax = "proto3"; + +package httppb; + +import "google/protobuf/timestamp.proto"; // Import the well-known type for Timestamp + +message Auto { + google.protobuf.Timestamp ctime = 1; // when the user tried this autocomplete + string argname = 2; // what the shell thinks the name of the executable is + string partial = 3; // set to the partial string trying to be matched + string cmd = 4; // the cmd being processed. For "git pull ", cmd would be "pull" + repeated string argv = 5; // use this to store whatever you want while the whole POST happens + string arg0 = 6; // what os.Exec() has as os.Argv[0] // not interesting + string arg1 = 7; // should always be "--auto-complete" // not interesting + bool isAuto = 8; // is true if '--auto-complete' is set + bool setupAuto = 9; // is true if '--bash' is set // setup bash autocomplete here + bool debug = 10; // print debugging info if true +} + +message Autos { // `autogenpb:marshal` `autogenpb:mutex` + string uuid = 1; // `autogenpb:uuid:94210ebf-a534-4b33-aadd-2f5e1f56ae38` + string version = 2; // `autogenpb:version:v0.0.1` + repeated Auto auto = 3; // THIS MUST BE HttpRequest and then HttpRequests +} diff --git a/bash.go b/bash.go index 6d7b868..c0c21e8 100644 --- a/bash.go +++ b/bash.go @@ -6,6 +6,7 @@ import ( "fmt" "os" "path/filepath" + "strings" "go.wit.com/dev/alexflint/arg" "go.wit.com/lib/gui/shell" @@ -58,25 +59,80 @@ func Bash(argname string, autocomplete func([]string)) *BashAuto { return myBash } -// also try to parse/send cur (?) -func Bash2(argname string, autocomplete func(string, string, []string)) *BashAuto { +// print out auto complete debugging info +func AutoDebug(pb *Auto) { + fmt.Fprintf(os.Stderr, "\n") + fmt.Fprintf(os.Stderr, "AUTO DEBUG: arg0='%s' arg1='%s' partial='%s' argv=%v\n", pb.Arg0, pb.Arg1, pb.Partial, pb.Argv) + // fmt.Println("--all --gui --verbose --force") +} + +// returns the last command (is blank if the current arg is not blank) +func GetLast(cur string, argv []string) string { + if cur != "''" { + return "" + } + for _, s := range argv { + if strings.HasPrefix(s, "-") { + continue + } + return s + } + return "" +} + +func parseArgv(argname string) *Auto { + newauto := new(Auto) + newauto.Argname = argname + if len(os.Args) == 0 { + return newauto + } + if len(os.Args) > 1 && os.Args[1] == "--bash" { - doBash(argname) + newauto.SetupAuto = true + return newauto + } + + // set debug flag + if os.Getenv("AUTOCOMPLETE_VERBOSE") == "true" { + newauto.Debug = true + } + for _, s := range os.Args { + if s == "--debug" { + newauto.Debug = true + } + } + + // should we do auto complete here? + if len(os.Args) > 1 && os.Args[1] == "--auto-complete" { + newauto.IsAuto = true + newauto.Arg0 = os.Args[0] + newauto.Arg1 = os.Args[1] + newauto.Partial = os.Args[2] + newauto.Argv = os.Args[3:] + return newauto + } + return newauto +} + +// also try to parse/send cur (?) +func Bash2(argname string, autocomplete func(*Auto)) { + newauto := parseArgv(argname) + if newauto.SetupAuto { + doBash2(argname) os.Exit(0) } + if newauto.Debug { + // dump debug info + AutoDebug(newauto) + } + if len(os.Args) > 1 && os.Args[1] == "--auto-complete" { - autocomplete(os.Args[0], os.Args[1], os.Args[2:]) + autocomplete(newauto) os.Exit(0) } arg.Register(&argBash) - - myBash = new(BashAuto) - myBash.appName = argname - - // parse go.Arg here? - return myBash } // returns the name of the executable registered for shell autocomplete @@ -114,6 +170,57 @@ func doBash(argname string) { os.Exit(0) } +// makes a bash autocomplete file for your command +func doBash2(argname string) { + fmt.Println(makeBashCompletionText2(argname)) + + homeDir, err := os.UserHomeDir() + if err != nil { + log.Printf("%v\n", err) + os.Exit(0) + } + filename := filepath.Join(homeDir, ".local/share/bash-completion/completions", argname) + if shell.Exists(filename) { + log.Println(filename, "file already exists") + os.Exit(0) + } + basedir, _ := filepath.Split(filename) + if !shell.IsDir(basedir) { + os.MkdirAll(basedir, os.ModePerm) + } + + if f, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644); err == nil { + f.Write([]byte(makeBashCompletionText2(argname))) + f.Close() + log.Println("bash file created:", filename) + log.Println("restart bash") + } else { + log.Info(filename, err) + } + os.Exit(0) +} + +/* +// prints help to STDERR // TODO: move everything below this to go-args +func (args) doBashHelp() { + if argv.BashAuto[1] != "''" { + // if this is not blank, then the user has typed something + return + } + if argv.BashAuto[0] != ARGNAME { + // if this is not the name of the command, the user already started doing something + return + } + if argv.BashAuto[0] == ARGNAME { + me.pp.WriteHelp(os.Stderr) + return + } + fmt.Fprintln(os.Stderr, "") + fmt.Fprintln(os.Stderr, "hello world") + fmt.Fprintln(os.Stderr, "") +} +*/ + func makeBashCompletionText(argname string) string { var out string @@ -148,23 +255,36 @@ func makeBashCompletionText(argname string) string { return out } -/* -// prints help to STDERR // TODO: move everything below this to go-args -func (args) doBashHelp() { - if argv.BashAuto[1] != "''" { - // if this is not blank, then the user has typed something - return - } - if argv.BashAuto[0] != ARGNAME { - // if this is not the name of the command, the user already started doing something - return - } - if argv.BashAuto[0] == ARGNAME { - me.pp.WriteHelp(os.Stderr) - return - } - fmt.Fprintln(os.Stderr, "") - fmt.Fprintln(os.Stderr, "hello world") - fmt.Fprintln(os.Stderr, "") +func makeBashCompletionText2(argname string) string { + var out string + + out += fmt.Sprintf("# add this in your bashrc:\n") + out += fmt.Sprintf("\n") + out += fmt.Sprintf("# todo: add this to go-arg as a 'hidden' go-arg option --bash\n") + out += fmt.Sprintf("#\n") + out += fmt.Sprintf("# Put the below in the file: ~/.local/share/bash-completion/completions/%s\n", argname) + out += fmt.Sprintf("#\n") + out += fmt.Sprintf("# todo: make this output work/parse with:\n") + out += fmt.Sprintf("# complete -C %s --bash go\n", argname) + out += fmt.Sprintf("\n") + out += fmt.Sprintf("_%s_complete()\n", argname) + out += fmt.Sprintf("{\n") + out += fmt.Sprintf(" # sets local to this func vars\n") + out += fmt.Sprintf(" local cur prev all\n") + out += fmt.Sprintf(" cur=${COMP_WORDS[COMP_CWORD]}\n") + out += fmt.Sprintf(" # prev=${COMP_WORDS[COMP_CWORD-1]}\n") + out += fmt.Sprintf(" all=${COMP_WORDS[@]}\n") + out += fmt.Sprintf("\n") + out += fmt.Sprintf(" # this is where we generate the go-arg output\n") + out += fmt.Sprintf(" GOARGS=$(%s --auto-complete \\'$cur\\' $all)\n", argname) + out += fmt.Sprintf("\n") + out += fmt.Sprintf(" # this compares the command line input from the user\n") + out += fmt.Sprintf(" # to whatever strings we output\n") + out += fmt.Sprintf(" COMPREPLY=( $(compgen -W \"$GOARGS\" -- $cur) ) # THIS WORKS\n") + out += fmt.Sprintf(" return 0\n") + out += fmt.Sprintf("}\n") + out += fmt.Sprintf("complete -F _%s_complete %s\n", argname, argname) + out += fmt.Sprintf("\n") + out += fmt.Sprintf("# copy and paste the above into your bash shell should work\n") + return out } -*/