summaryrefslogtreecommitdiff
path: root/complete.go
diff options
context:
space:
mode:
authorJeff Carr <[email protected]>2025-10-12 00:11:00 -0500
committerJeff Carr <[email protected]>2025-10-12 00:11:00 -0500
commit2d5370c9a6dd8ab566ec2943e20738b31a7a6606 (patch)
treec714dafa13c6763ded14638cee98cf33b79786c7 /complete.go
parent7144347aa7dc1e53bb5df051a2c5c3eaacbb34c6 (diff)
restructure code
Diffstat (limited to 'complete.go')
-rw-r--r--complete.go508
1 files changed, 0 insertions, 508 deletions
diff --git a/complete.go b/complete.go
deleted file mode 100644
index 4de01db..0000000
--- a/complete.go
+++ /dev/null
@@ -1,508 +0,0 @@
-package prep
-
-// initializes logging and command line options
-
-import (
- "fmt"
- "os"
- "path/filepath"
- "strconv"
- "strings"
- "time"
-
- "go.wit.com/dev/alexflint/arg"
- "go.wit.com/lib/config"
- "go.wit.com/log"
- durationpb "google.golang.org/protobuf/types/known/durationpb"
- timestamppb "google.golang.org/protobuf/types/known/timestamppb"
-)
-
-// makes a bash autocomplete file for your command
-func (pb *Auto) doHandlePB() error {
- homeDir, err := os.UserHomeDir()
- if err != nil {
- return err
- }
- basedir := filepath.Join(homeDir, ".cache/autocomplete")
- os.MkdirAll(basedir, os.ModePerm)
- fullname := filepath.Join(basedir, pb.Argname+".pb")
-
- all := NewAutos()
- var last *Auto
- data, err := os.ReadFile(fullname)
- if err == nil {
- err = all.Unmarshal(data)
- if err == nil {
- for found := range all.IterAll() {
- dur := time.Since(found.Ctime.AsTime())
- pb.Duration = durationpb.New(dur)
- // found.PrintDebug()
- cmd := fmt.Sprintf("cmd='%s'", found.Cmd)
- arglast := fmt.Sprintf("last='%s'", found.Last)
- partial := fmt.Sprintf("p='%s'", found.Partial)
- age := fmt.Sprintf("age='%-6.6s'", config.FormatDuration(dur))
- pb.Debugf("AUTO HISTORY: %s %-18.18s %-18.18s %-12.12s argv='%v' goargs='%v'", age, cmd, arglast, partial, found.Argv, found.Goargs)
- last = found
- }
- }
- }
-
- if all.Len() > 15 {
- pb.Debugf("DEBUG: trim() history is over 100 len=%d vs new=%d", all.Len(), all.Len()-90)
- all.Autos = all.Autos[all.Len()-10:]
- // newall.Autos = all.Autos[0:10]
- // for _, found := range all.Autos[0:10] {
- // newall.Append(found)
- // }
- }
-
- // need this for the first time the user runs autocomplete
- if last == nil {
- last = new(Auto)
- }
-
- now := time.Now()
- pb.Ctime = timestamppb.New(now)
- duration := time.Since(last.Ctime.AsTime())
- all.Append(pb)
-
- data, err = all.Marshal()
- if err != nil {
- return err
- }
-
- f, err := os.OpenFile(fullname, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0644)
- defer f.Close()
- if err != nil {
- return err
- }
- _, err = f.Write(data)
- pb.Debugf("WRITE DEBUG: write PB='%s' len(pb)=%d len(data)=%d dur=%v err=%v", fullname, all.Len(), len(data), duration, err)
- return err
-}
-
-func (pb *Auto) SubCommand(cmd ...string) {
- partial := strings.Trim(pb.Partial, "'")
- if pb.Debug {
- if myAuto.examples == nil {
- pb.Debugf("WRITE DEBUG: argv.Examples() not defined")
- // log.Fprintf(os.Stderr, "\n")
- // log.Fprintf(os.Stderr, "examples was nil\n")
- // log.Fprintf(os.Stderr, "\n")
- } else {
- log.Fprintf(os.Stderr, "\n")
- log.Fprintf(os.Stderr, "\n")
- log.Fprintf(os.Stderr, "Examples:\n")
- for _, line := range strings.Split(myAuto.examples(), "\n") {
- log.Fprintf(os.Stderr, " %s\n", line)
- }
- // log.Fprintf(os.Stderr, "\n")
- }
- myAuto.pp.WriteHelpForAutocomplete(os.Stderr, os.Stdout, partial, cmd...)
- // myAuto.pp.GetUsageForSubcommand(os.Stdout, os.Stderr, partial, cmd)
- // myAuto.pp.GetUsageForSubcommand(os.Stdout, nil, partial, cmd)
- } else {
- f, _ := os.OpenFile("/tmp/outlook", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
- myAuto.pp.WriteHelpForAutocomplete(f, os.Stdout, partial, cmd...)
- // myAuto.pp.GetUsageForSubcommand(os.Stdout, nil, partial, cmd)
- }
- os.Exit(0)
- // SubCommand(cmd)
-}
-
-/*
-func (pb *Auto) SubCommandShow() {
- partial := strings.Trim(pb.Partial, "'")
- if pb.Debug {
- myAuto.pp.WriteHelpForAutocomplete(os.Stderr, os.Stdout, partial, "show", "repo")
- } else {
- f, _ := os.OpenFile("/tmp/outlook", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
- myAuto.pp.WriteHelpForAutocomplete(f, os.Stdout, partial, "show", "repo")
- }
- os.Exit(0)
-}
-
-func (pb *Auto) SubCommand2(cmd string, addmatch []string) {
- partial := strings.Trim(pb.Partial, "'")
- if pb.Debug {
- // myAuto.pp.WriteHelpForAutocomplete(os.Stderr, os.Stdout, partial, pb.Cmd)
- myAuto.pp.WriteHelpForAutocomplete(os.Stderr, os.Stdout, "", "")
- // myAuto.pp.GetUsageForSubcommand(os.Stdout, os.Stderr, partial, cmd)
- // myAuto.pp.GetUsageForSubcommand(os.Stdout, nil, partial, cmd)
- } else {
- f, _ := os.OpenFile("/tmp/outlook", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
- myAuto.pp.WriteHelpForAutocomplete(f, os.Stdout, partial, pb.Cmd)
- // myAuto.pp.GetUsageForSubcommand(os.Stdout, nil, partial, cmd)
- }
- os.Exit(0)
- // SubCommand(cmd)
-}
-*/
-
-// todo: move everything to this?
-func (pb *Auto) Autocomplete3(sendthis []string) {
- pb.Autocomplete2(strings.Join(sendthis, " "))
-}
-
-func (pb *Auto) Autocomplete2(sendthis string) {
- dur := pb.Duration.AsDuration()
- if dur < time.Millisecond*200 {
- pb.Debug = true
- /*
- pb.Debugf("TODO: show extended help here '%s' '%s' %v dur=%v\n", pb.Arg0, pb.Arg1, pb.Argv, config.FormatDuration(dur))
- pb.PrintDebug()
- fmt.Println(" ")
- */
- if myAuto.pp == nil {
- pb.Debugf("myAuto.pp == nil")
- } else {
- pb.Debugf("myAuto.pp != nil")
- if pb.Cmd == "" {
- myAuto.pp.WriteHelp(os.Stderr)
- } else {
- myAuto.pp.WriteHelpForSubcommand(os.Stderr, pb.Cmd)
- }
- }
- }
-
- parts := strings.Split(sendthis, " ")
- var all []string
- for _, part := range parts {
- var found bool
- for _, s := range os.Args {
- if s == part {
- found = true
- }
- }
- if found {
- continue
- }
- all = append(all, part)
- }
- fmt.Printf("%s", strings.Join(all, " "))
- /*
- if dur > time.Millisecond*200 {
- if dur < time.Millisecond*800 {
- // fmt.Println("a b")
- fmt.Println(pb.Partial + " hello world")
- }
- }
- */
-}
-
-func parseArgv(argname string) *Auto {
- pb := new(Auto)
- pb.Argname = argname
- if len(os.Args) == 0 {
- return pb
- }
-
- if len(os.Args) > 1 && os.Args[1] == "--bash" {
- pb.SetupAuto = true
- return pb
- }
-
- // HACK: set debug flag if --autodebug is passed
- for _, s := range os.Args {
- if s == "--autodebug" {
- pb.Debug = true
- }
- }
-
- // "complete -C /usr/bin/argv forge" bash ENV values:
- // * `COMP_LINE`: A string containing the entire current command line (forge first second third forth ). This is what you are looking for.
- // * `COMP_POINT`: A number indicating the cursor's position (index) within the COMP_LINE.
- // * `COMP_WORDS`: An array in Bash (seen differently by Go) containing each individual word on the command line.
- // * `COMP_CWORD`: A number indicating the index of the word the cursor is currently on within the COMP_WORDS array.
- if len(os.Args) > 1 && os.Args[1] == pb.Argname {
- pb.IsAuto = true
- parts := strings.Split(os.Getenv("COMP_LINE"), " ")
- pb.Debug = true
- log.Fprintf(os.Stderr, "\n")
- pb.Debugf("MATCH Partial os.Args=%v COMP_LINE=%v", os.Args, os.Getenv("COMP_LINE"))
- // log.Fprintf(os.Stdout, "jcarr")
- if len(parts) > 0 {
- pb.Arg0 = parts[0]
- }
- pb.Arg1 = os.Args[1]
- os.Exit(0)
- }
-
- if len(os.Args) > 1 && os.Args[1] == "--version" {
- if myAuto.buildtime != nil {
- // if binary defined buildtime() then process standard version output here
- doVersion(pb)
- os.Exit(0)
- }
- }
- if len(os.Args) == 0 {
- return pb
- }
-
- if len(os.Args) == 1 {
- pb.Arg0 = os.Args[0]
- return pb
- }
- if os.Args[1] != "--auto-complete" {
- pb.Cmd = os.Args[1]
- return pb
- }
-
- // should we do auto complete here?
- if len(os.Args) > 1 && os.Args[1] == "--auto-complete" {
- pb.IsAuto = true
- pb.Arg0 = os.Args[0]
- pb.Arg1 = os.Args[1]
- pb.Partial = os.Args[2]
- pb.Arg3 = os.Args[3]
- if len(os.Args) < 5 {
- // the user is doing autocomplete on the command itself
- pb.Partial = ""
- pb.Cmd = ""
- pb.Argv = []string{""}
- return pb
- }
- if pb.Partial == "''" {
- pb.Partial = ""
- }
- // pb.Argv = os.Args[4:]
- for _, s := range os.Args[4:] {
- if s == "--autodebug" {
- continue
- }
- tmp := strings.Trim(pb.Partial, "'")
- if tmp == s {
- // don't put pb.Partial into Argv
- continue
- }
- pb.Argv = append(pb.Argv, s)
- }
- // set pb.Cmd to the first thing that doesn't have a '-' arg
- for _, s := range pb.Argv {
- if strings.HasPrefix(s, "-") {
- continue
- }
- pb.Cmd = s
- break
- }
- if pb.Partial == "'"+pb.Cmd+"'" {
- // not really a command, it's just a partially inputed string from the user
- pb.Cmd = ""
- }
- // try to figure out what the last argv is
- for _, s := range pb.Argv {
- p := fmt.Sprintf("'%s'", s)
- if pb.Partial == p {
- pb.Debugf("DEBUG: Last argv MATCHES Partial %s %s", s, p)
- continue
- } else {
- pb.Debugf("DEBUG: Last argv DOES NOT MATCH Partial %s %s", s, p)
- }
- pb.Last = s
- if strings.HasPrefix(s, "-") {
- // skip args like -test --verbose when sending subcommands to go-args for help text
- continue
- }
- pb.Goargs = append(pb.Goargs, s)
- }
- // if pb.Cmd == "" {
- // pb.Cmd = strings.Join(pb.Argv, "BLAH")
- // }
- }
- return pb
-}
-
-func Bash3(dest any) *Auto {
- return Bash(dest)
-}
-
-func Bash(dest any) *Auto {
- myAuto = new(AutoArgs)
- findAppInfo(dest) // parses back to main() for argv info
-
- pb := parseArgv(myAuto.appName)
- if pb.SetupAuto {
- // --bash was passed. try to configure bash-completion
- doBash(myAuto.appName)
- os.Exit(0)
- }
-
- myAuto.match = make(map[string]string)
- myAuto.match["--gui"] = "andlabs gocui"
-
- if pb.Debug {
- // dump debug info
- pb.PrintDebug()
- }
-
- pb.doHandlePB() // read in the history protobuf file
-
- // turn on debugging if duration < 200 milliseconds
- dur := pb.Duration.AsDuration()
- if dur < time.Millisecond*200 {
- pb.Debug = true
- }
-
- // prepart["--gui"] = "andlabs gocui"
-
- arg.Register(&argBash)
- flags := []string{}
- for _, s := range pb.Argv {
- if s == "--autodebug" {
- continue
- }
- flags = append(flags, s)
- }
- // pb.Debug = true
- // pb.Debugf("DEBUG: MustParse(%v)", flags)
- var err error
- myAuto.pp, err = arg.ParseFlags(flags, dest)
- if err != nil {
- pb.Debugf("DEBUG: Parse error: %v", err)
- }
-
- if myAuto.pp == nil {
- pb.Debugf("DEBUG: myAuto.pp == nil after ParseFlags()")
- } else {
- // pb.Debugf("DEBUG: myAuto.pp is ok after ParseFlags()")
- }
-
- if pb.IsAuto {
- for key, val := range myAuto.match {
- if pb.Last == key {
- pb.Debugf("DEBUG: last=%s found key %s = %s", pb.Last, key, val)
- pb.Autocomplete2(val)
- os.Exit(0)
- } else {
- // pb.Debugf("DEBUG: NO MATCH last='%s' found key '%s' = %s", pb.Last, key, val)
- }
- }
- if myAuto.autoFunc == nil {
- pb.SubCommand(pb.Argv...)
- } else {
- myAuto.autoFunc(pb) // run the autocomplete function the user made for their application
- }
- if pb.Debug {
- // TODO:
- // check here to see if there was any completion text sent
- // if not, send "reset bash newline\n" to cause bash to redraw PS1 for the user
- }
- os.Exit(0)
- }
-
- arg.Register(&argBash)
- myAuto.pp = arg.MustParse(dest)
- return pb
-}
-
-// makes a bash autocomplete file for your command
-func doBash(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 config.Exists(filename) {
- log.Fprintln(os.Stderr, "# file already exists", filename)
- // os.Exit(0)
- }
- basedir, _ := filepath.Split(filename)
- if !config.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()
- } else {
- log.Fprintln(os.Stderr, "# open file error", filename, err)
- }
- os.Exit(0)
-}
-
-func (pb *Auto) Version() string {
- return pb.getVersion()
-}
-
-func doVersion(pb *Auto) {
- log.Info(pb.getVersion())
- os.Exit(0)
-}
-
-func (pb *Auto) getVersion() string {
- if myAuto.buildtime == nil {
- return "app doesn't have argv.BuildVersion()"
- }
- BUILDTIME, VERSION := myAuto.buildtime()
-
- parts := strings.Split(BUILDTIME, ".")
- if len(parts) == 1 {
- // The input epoch seconds
- // epochSeconds := int64(1758646486)
- num, err := strconv.Atoi(BUILDTIME)
- epochSeconds := int64(num)
- if err == nil {
-
- // 1. Convert the epoch seconds to a time.Time object.
- // time.Unix() creates the time in the UTC timezone by default.
- t := time.Unix(epochSeconds, 0)
-
- // 2. Convert the UTC time to the computer's local timezone.
- localTime := t.Local()
-
- // 3. Print the result. The default format is clear and includes the timezone.
- // fmt.Println("Default format:", localTime)
- // For a more human-friendly format, use the Format() method.
- // Go uses a special reference time for formatting: Mon Jan 2 15:04:05 2006 MST
- // You lay out your desired format using these specific numbers.
- // formattedString := localTime.Format("Monday, January 2, 2006 at 3:04:05 PM (MST)")
- // fmt.Println(" Custom format:", formattedString)
-
- // now := time.Now()
- // dur := time.Since(localTime)
- // BUILDTIME = fmt.Sprintf("%s age(%v)", localTime.String(), , config.FormatDuration(time.Since(localTime)))
- stamp := log.Sprintf("Built %s Age(%s)", localTime.Format("2006-01-02 15:04"), config.FormatDuration(time.Since(localTime)))
- return fmt.Sprintf("%s %s %s", pb.Argname, VERSION, stamp)
- }
- }
-
- return fmt.Sprintf("%s %s Built on %s", pb.Argname, VERSION, BUILDTIME)
-}
-
-func StandardVersion(ARGNAME, VERSION, BUILDTIME string) string {
- parts := strings.Split(BUILDTIME, ".")
- if len(parts) == 1 {
- // The input epoch seconds
- // epochSeconds := int64(1758646486)
- num, err := strconv.Atoi(BUILDTIME)
- epochSeconds := int64(num)
- if err == nil {
-
- // 1. Convert the epoch seconds to a time.Time object.
- // time.Unix() creates the time in the UTC timezone by default.
- t := time.Unix(epochSeconds, 0)
-
- // 2. Convert the UTC time to the computer's local timezone.
- localTime := t.Local()
-
- // 3. Print the result. The default format is clear and includes the timezone.
- // fmt.Println("Default format:", localTime)
- // For a more human-friendly format, use the Format() method.
- // Go uses a special reference time for formatting: Mon Jan 2 15:04:05 2006 MST
- // You lay out your desired format using these specific numbers.
- // formattedString := localTime.Format("Monday, January 2, 2006 at 3:04:05 PM (MST)")
- // fmt.Println(" Custom format:", formattedString)
-
- // now := time.Now()
- // dur := time.Since(localTime)
- // BUILDTIME = fmt.Sprintf("%s age(%v)", localTime.String(), , config.FormatDuration(time.Since(localTime)))
- stamp := log.Sprintf("Built %s Age(%s)", localTime.Format("2006-01-02 15:04"), config.FormatDuration(time.Since(localTime)))
- return fmt.Sprintf("%s %s %s", ARGNAME, VERSION, stamp)
- }
- }
-
- return fmt.Sprintf("%s %s Built on %s", ARGNAME, VERSION, BUILDTIME)
-}