diff options
| author | Jeff Carr <[email protected]> | 2025-10-25 08:26:54 -0500 |
|---|---|---|
| committer | Jeff Carr <[email protected]> | 2025-10-25 08:26:54 -0500 |
| commit | b95f2cab06e5de8fec71b0b991777f5d5cb1fa90 (patch) | |
| tree | 821deba3f06e10e908a9f3990e434bf47b5da022 | |
| parent | 31e1d6afbe3ad9e39d0f0115b1ae3ddb0bb5de64 (diff) | |
isolate history handling
| -rw-r--r-- | argv.Print.go | 25 | ||||
| -rw-r--r-- | argv.SendStrings.go | 1 | ||||
| -rw-r--r-- | argv.proto | 13 | ||||
| -rw-r--r-- | makeAutocompleteFiles.bash.go | 4 | ||||
| -rw-r--r-- | structs.go | 3 | ||||
| -rw-r--r-- | theMagicOfAutocomplete.go | 163 |
6 files changed, 121 insertions, 88 deletions
diff --git a/argv.Print.go b/argv.Print.go index 368cf7b..bb1de4a 100644 --- a/argv.Print.go +++ b/argv.Print.go @@ -23,20 +23,17 @@ func Debugf(fmts string, parts ...any) { } // print out auto complete debugging info -func (pb *Argv) PrintDebug() { - pb.PrintDebugNew("ARGV") +func (pb *Argv) PrintDebug(last string) { + pb.PrintDebugNew("ARGV", last) } -func (pb *Argv) PrintDebugNew(msg string) { +func (pb *Argv) PrintDebugNew(msg string, last string) { cmd := fmt.Sprintf("cmd='%s'", pb.GetCmd()) var arglast string - if me.last == nil { - arglast = fmt.Sprintf("last='%s'", "<nil>") - } else { - arglast = fmt.Sprintf("last='%s'", me.last.GetCmd()) - } + arglast = fmt.Sprintf("last='%s'", last) partial := fmt.Sprintf("p='%s'", pb.Partial) - dur := cobol.Duration(pb.Ctime) + age := cobol.Duration(pb.Ctime) + dur := cobol.Duration(pb.Duration) var fast string if pb.Fast { fast = "fast=1," + pb.GetCmd() @@ -44,12 +41,16 @@ func (pb *Argv) PrintDebugNew(msg string) { fast = "fast=0," + pb.GetCmd() } sargv := fmt.Sprintf("argv(%v)", pb.Real) - Debugf("%s: age=(%s) %-12.12s %-12.12s %-12.12s %s %s goargs='%v' len(%d)", msg, dur, cmd, arglast, partial, fast, sargv, pb.Goargs, me.all.Len()) + top := fmt.Sprintf("age=(%s)dur(%s)%-4.4s %-12.12s %-12.12s", age, dur, pb.Uuid, cmd, arglast) + Debugf("%s: %s %-12.12s %s %s goargs='%v' len(%d)", msg, top, partial, fast, sargv, pb.Goargs, me.all.Len()) } -func (all *Argvs) PrintHistory() { +func (all *Argvs) PrintHistory(last string) { + counter := 0 for pb := range all.IterAll() { - pb.PrintDebugNew("HIST") + counter += 1 + hist := fmt.Sprintf("HIST(%d)", counter) + pb.PrintDebugNew(hist, last) } } diff --git a/argv.SendStrings.go b/argv.SendStrings.go index c890608..4e074e0 100644 --- a/argv.SendStrings.go +++ b/argv.SendStrings.go @@ -56,7 +56,6 @@ func (pb *Argv) SubCommand(cmd ...string) { // me.pp.GetUsageForSubcommand(Stdout, Stderr, partial, cmd) // me.pp.GetUsageForSubcommand(Stdout, nil, partial, cmd) } else { - // last working: f, _ := os.OpenFile("/tmp/outlook", os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644) // last working: me.writeHelpForAutocomplete(f, Stdout, partial, cmd...) me.writeHelpForAutocompleteFunc(partial, cmd...) } @@ -14,11 +14,11 @@ message App { } message StructEntry { - string structName = 2; // 'Force', 'Verbose', 'SortCmd' - string structType = 3; // 'string', 'int', '[]string', '*FindCmd' - string defaultVal = 4; // "true" - string helpText = 5; // "This will help you" - string matchText = 6; // "--help-me + string structName = 1; // 'Force', 'Verbose', 'SortCmd' + string structType = 2; // 'string', 'int', '[]string', '*FindCmd' + string defaultVal = 3; // "true" + string helpText = 4; // "This will help you" + string matchText = 5; // "--help-me } message ArgTree { @@ -39,11 +39,12 @@ message Argv { // `autogenpb:marshal string stdout = 10; // all output is loaded here before being sent to the shell string stderr = 11; // all output is loaded here before being sent to the shell int32 helpCounter = 12; // counter to track if the help text has been sent to Stderr + string uuid = 13; // all output is loaded here before being sent to the shell } message Argvs { // `autogenpb:marshal` `autogenpb:sort` `autogenpb:nomutex` string uuid = 1; // `autogenpb:uuid:1e6e765c-0c77-4c81-a622-0d819bfcce9a` - string version = 2; // `autogenpb:version:v0.0.3` + string version = 2; // `autogenpb:version:v0.0.4` repeated Argv argvs = 3; string filename = 4; // `autogenpb:save` -- this enables autogenerated pb.Load() and pb.Save() } diff --git a/makeAutocompleteFiles.bash.go b/makeAutocompleteFiles.bash.go index 259d795..45be925 100644 --- a/makeAutocompleteFiles.bash.go +++ b/makeAutocompleteFiles.bash.go @@ -26,10 +26,10 @@ func MakeBashCompleteFiles(argname string) { } basedir, _ := filepath.Split(filename) if !config.IsDir(basedir) { - os.MkdirAll(basedir, os.ModePerm) + os.MkdirAll(basedir, 0755) } - if f, err := os.OpenFile(filename, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644); err == nil { + if f, err := os.OpenFile(filename, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644); err == nil { f.Write([]byte(makeBashCompletionText2(argname))) f.Close() } else { @@ -6,7 +6,7 @@ var me *AutoArgs // this is a work in progress type AutoArgs struct { pb *Argv // the protobuf for the current process - all *Argvs // the protobuf for the current process + all *Argvs // the history of argv last *Argv // the pb from the last time the user tried autocomplete id int // should be unique Argv func([]string) // the function for shell autocomplete @@ -27,5 +27,4 @@ type AutoArgs struct { debug bool // is dubugging on? setupAuto bool // do shell autocomplete setup isAuto bool // try to do autocomplete - } diff --git a/theMagicOfAutocomplete.go b/theMagicOfAutocomplete.go index 9c9010b..0e92af3 100644 --- a/theMagicOfAutocomplete.go +++ b/theMagicOfAutocomplete.go @@ -10,6 +10,7 @@ import ( "strings" "time" + "github.com/google/uuid" "go.wit.com/lib/cobol" "go.wit.com/lib/config" "go.wit.com/lib/env" @@ -24,6 +25,7 @@ import ( func Autocomplete(dest any) *Argv { me = new(AutoArgs) // todo: redo this me.pb = new(Argv) + me.pb.Uuid = uuid.New().String() // set the start time of the binary now := time.Now() @@ -42,28 +44,8 @@ func Autocomplete(dest any) *Argv { // initializes the lib/env library env.Init(me.pb.AppInfo.APPNAME, me.pb.AppInfo.VERSION, cobol.Time(me.pb.AppInfo.BUILDTIME), me.pb.Real, GoodExit, BadExit) - // loads the argv autocomplete history file - me.all = NewArgvs() - me.Err = config.CreateCacheDirPB(me.all, "argv", me.pb.AppInfo.APPNAME) - if me.Err == nil { - me.all.PrintHistory() - } else { - // there is no history. - // todo: initialize the history file - // todo: check if this is automatically done already - // - // ALWAYS KEEP THESE LINES AND THE panic() - // - // They are needed to debug autocomplete. - // - // This code is only executed when the user is hitting tab in the shell, - // so this panic() is safe and can never be triggered by normal program execution. - // - me.debug = true - me.pb.Stderr += fmt.Sprintf("config.CreateCacheDirPB() err(%v)\n", me.Err) - me.pb.PrintStderr() - panic("argvpb.Load() history file failed") - } + // open the argv cache history file to figure out the timing + examineArgvHistory() // try to register bash args for go-args // arg.Register(&ArgvBash) @@ -79,10 +61,10 @@ func Autocomplete(dest any) *Argv { // user is trying to setup bash or zsh autocomplete // --bash or --zsh is the first os.Args if me.setupAuto { - savePB() // --bash or --zsh was passed. try to configure bash-completion MakeAutocompleteFiles(me.pb.AppInfo.APPNAME) // never forget to exit here or you will hate yourself and the choices you have made + savePB() saveAndExit() } @@ -97,64 +79,41 @@ func Autocomplete(dest any) *Argv { // do autocomplete and exit savePB() doAutocomplete() - // todo: this never gets here. fix that + panic("blah") + savePB() saveAndExit() } // not autocompleting. return to the application // save the pb & history - me.all.Clone(me.pb) - errors.Join(me.Err, me.all.Save()) - + savePB() // me.pp = arg.MustParse(dest) me.Err = errors.Join(me.Err, me.mustParseFunc()) return me.pb } -// prints what is needed to Stdout & Stderr, then exits -// EVERYTHING PAST HERE IS FOR AUTOCOMPLETE +// doAutocomplete(): print what is needed to Stdout & Stderr, then exit +// // everything sent to STDOUT and STDERR matters past this point +// any "junk" output, hidden fmt(), print() or log() statements are bad func doAutocomplete() { - // set the duration since the last auto complete - // find the last entry. this is dumb way to do it - me.last = new(Argv) - me.last.Ctime = timestamppb.New(time.Now().Add(-time.Second)) - for found := range me.all.IterAll() { - me.last = found + if me.last.Fast { + // user is probably holding down the <TAB> key + me.debug = true + // me.fastcmd = me.pb.GetCmd() + if me.pb.GetCmd() != me.last.GetCmd() { + // do the smart something here + // probably debug == true && printStderr() + } } - dur := time.Since(me.last.Ctime.AsTime()) - me.pb.Duration = durationpb.New(dur) if me.debug { // dump debug info - me.pb.PrintDebug() - me.all.PrintHistory() + me.pb.PrintDebug(me.last.GetCmd()) + // me.all.PrintHistory() // me.pb.PrintStderr() } - // roll the autocomplete file - if me.all.Len() > 15 { - me.pb.Debugf("DEBUG: trim() history is over 100 len=%d vs new=%d", me.all.Len(), me.all.Len()-90) - me.all.Argvs = me.all.Argvs[me.all.Len()-10:] - // newall.Autos = me.all.Autos[0:10] - // for _, found := range me.all.Autos[0:10] { - // newall.Append(found) - // } - } - - // turn on debugging if duration < 200 milliseconds - dur = me.pb.Duration.AsDuration() - if dur < time.Millisecond*200 { - me.debug = true - me.pb.Fast = true - // me.fastcmd = me.pb.GetCmd() - if me.last.Fast { - if me.pb.GetCmd() == me.last.GetCmd() { - // do the smart something here - } - } - } - flags := []string{} for _, s := range me.pb.Real { if s == "--autodebug" { @@ -186,9 +145,10 @@ func doAutocomplete() { me.pb.Debugf("DEBUG: real=(%v) found --gui", me.pb.Real) me.pb.PrintStderr() me.pb.SendString("andlabs gogui") + savePB() saveAndExit() } else { - // me.pb.Debugf("DEBUG: NO MATCH last='%s' found key '%s' = %s", me.last, key, val) + // me.pb.Debugf("DEBUG: NO MATCH last='%s' found key '%s' = %s", last, key, val) } } @@ -224,6 +184,7 @@ func doAutocomplete() { if me.pb.HelpCounter < 3 { me.pb.PrintStderr() } else { + savePB() saveAndExit() } } else { @@ -249,16 +210,88 @@ func doAutocomplete() { // if not, send "reset bash newline\n" to cause bash to redraw PS1 for the user } } + savePB() saveAndExit() } func savePB() { // save now. this is near the end probably - me.all.Clone(me.pb) + me.all.Append(me.pb) + npb := new(Argv) + // npb.Uuid = uuid.New().String() + me.all.Append(npb) errors.Join(me.Err, me.all.Save()) } func saveAndExit() { - savePB() os.Exit(0) } + +// sets me.last +// computes me.pb.Duration +func examineArgvHistory() { + me.all = NewArgvs() + // loads the argv autocomplete history file + me.Err = config.ForceCreateCacheDirPB(me.all, "argv", me.pb.AppInfo.APPNAME) + if me.Err != nil { + // there is no history. + // ALWAYS KEEP THESE LINES AND THE panic() + // They are needed to debug autocomplete. + // + // This code is only executed when the user is hitting tab in the shell, + // so this panic() is safe and can never be triggered by normal program execution. + // + me.debug = true + me.pb.Stderr += fmt.Sprintf("config.CreateCacheDirPB() err(%v)\n", me.Err) + me.pb.PrintStderr() + panic("argvpb.Load() history file failed") + } + me.all.PrintHistory("<?>") + if me.all.Len() > 0 { + last := me.all.Argvs[me.all.Len()-1] + me.pb.Debugf("DEBUG LAST %v", last) + } + // roll the autocomplete file + maxsize := 17 + trim := 10 + if me.all.Len() > maxsize { + me.pb.Debugf("DEBUG: trim() history is over 17 len=%d vs new=%d", me.all.Len(), me.all.Len()-trim) + me.pb.Debugf("DEBUG: trim() history is over 17 len=%d vs new=%d", me.all.Len(), me.all.Len()-trim) + me.pb.Debugf("DEBUG: trim() history is over 17 len=%d vs new=%d", me.all.Len(), me.all.Len()-trim) + me.all.Argvs = me.all.Argvs[me.all.Len()-trim:] + // newall.Autos = me.all.Autos[0:10] + } + + counter := 0 + for pb := range me.all.IterAll() { + counter += 1 + if pb.Ctime == nil { + me.all.Delete(pb) + continue + } + hist := fmt.Sprintf("HISTNIL(%d)", counter) + pb.PrintDebugNew(hist, "jwc") + } + if me.all.Len() == 0 { + me.pb.PrintStderr() + // todo: make a blak entry here + panic("examineArgvHistory() couldn't find a valid last entry") + } + me.last = me.all.Argvs[me.all.Len()-1] + + // compute the duration since the last time + dur := time.Since(me.last.Ctime.AsTime()) + me.pb.Duration = durationpb.New(dur) + + // turn on debugging if duration < 200 milliseconds + if me.pb.Duration.AsDuration() < time.Millisecond*200 { + me.debug = true + me.pb.Fast = true + // me.fastcmd = me.pb.GetCmd() + if me.last.Fast { + if me.pb.GetCmd() != me.last.GetCmd() { + // do the smart something here + } + } + } +} |
