diff options
| author | Jeff Carr <[email protected]> | 2025-10-25 02:34:49 -0500 |
|---|---|---|
| committer | Jeff Carr <[email protected]> | 2025-10-25 02:34:49 -0500 |
| commit | 87dbb0b212c48e55a50b612f022b4b035e25477f (patch) | |
| tree | a156655fd9ae955fabf884cdbb816d25506b7b34 | |
| parent | 27e0ddeaf127f561ca7fd5a8083ab52eba6ca2fb (diff) | |
cleanup on aisle 9
| -rw-r--r-- | argv.Print.go | 2 | ||||
| -rw-r--r-- | argv.proto | 1 | ||||
| -rw-r--r-- | defer_after.go | 66 | ||||
| -rw-r--r-- | exit.go | 24 | ||||
| -rw-r--r-- | theMagicOfAutocomplete.go | 38 |
5 files changed, 122 insertions, 9 deletions
diff --git a/argv.Print.go b/argv.Print.go index ceab8a6..5f57421 100644 --- a/argv.Print.go +++ b/argv.Print.go @@ -40,7 +40,7 @@ func (pb *Argv) PrintDebugNew(msg string) { fast = "fast=0," + pb.GetCmd() } sargv := fmt.Sprintf("argv(%v)", pb.Real) - pb.Debugf("%s: age=(%s) %-12.12s %-12.12s %-12.12s %s %s goargs='%v'", msg, dur, cmd, arglast, partial, fast, sargv, pb.Goargs) + pb.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()) } func (all *Argvs) PrintHistory() { @@ -38,6 +38,7 @@ message Argv { // `autogenpb:marshal bool fast = 9; // is autocomplete running quickly? 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 } message Argvs { // `autogenpb:marshal` `autogenpb:sort` `autogenpb:nomutex` diff --git a/defer_after.go b/defer_after.go new file mode 100644 index 0000000..ce20f81 --- /dev/null +++ b/defer_after.go @@ -0,0 +1,66 @@ +package argvpb + +import ( + "fmt" + "time" +) + +// RunIfTakesLonger schedules a function `f` to run after a duration `d`. +// It returns a "cancel" function. If the cancel function is called before +// the duration `d` has passed, the scheduled function `f` will not be executed. +// +// This is designed to be used with defer, like so: +// +// func myFunc() { +// cancel := RunIfTakesLonger(time.Second, func() { +// fmt.Println("myFunc took more than a second!") +// }) +// defer cancel() +// +// // ... rest of the function +// } +func RunIfTakesLonger(d time.Duration, f func()) (cancel func()) { + // time.AfterFunc executes a function in its own goroutine after the + // specified duration. It returns a Timer that can be used to cancel it. + timer := time.AfterFunc(d, f) + + // We return a function that stops the timer. + // The caller is expected to call this function using `defer`. + return func() { + // The Stop method returns true if it successfully prevented the + // timer from firing. We don't need the return value here, but it's + // useful for understanding. + timer.Stop() + } +} + +// A task that takes less time than the timeout. +func shortTask() { + fmt.Println("--- Running shortTask (will take 500ms) ---") + cancel := RunIfTakesLonger(time.Second, func() { + fmt.Println("!!! This should NOT be printed !!!") + }) + defer cancel() + + fmt.Println("shortTask: working...") + time.Sleep(500 * time.Millisecond) + fmt.Println("shortTask: finished.") +} + +// A task that takes more time than the timeout. +func longTask() { + fmt.Println("\n--- Running longTask (will take 2 seconds) ---") + cancel := RunIfTakesLonger(time.Second, func() { + fmt.Println(">>> longTask took more than 1 second! Clean-up function executed. <<<") + }) + defer cancel() + + fmt.Println("longTask: working...") + time.Sleep(2 * time.Second) + fmt.Println("longTask: finished.") +} + +func main() { + shortTask() + longTask() +} @@ -92,3 +92,27 @@ func ExitWatchdog() { } } } + +func Defer(dur time.Duration, f func()) { + dog := time.NewTicker(5 * time.Second) + defer dog.Stop() + dogchan := make(chan bool) + /* + // this example would exit/destroy the ticker in 10 seconds + go func() { + time.Sleep(10 * time.Second) + done <- true + }() + */ + for { + select { + case <-dogchan: + fmt.Println("Done!") + return + case t := <-dog.C: + _ = t + fmt.Println("argv.Exit() watchdog: stalled in", me.pb.AppInfo.APPNAME+".Exit()") + // h.Scan() + } + } +} diff --git a/theMagicOfAutocomplete.go b/theMagicOfAutocomplete.go index 5b5370b..571c280 100644 --- a/theMagicOfAutocomplete.go +++ b/theMagicOfAutocomplete.go @@ -44,12 +44,25 @@ func Autocomplete(dest any) *Argv { // loads the argv autocomplete history file me.all = NewArgvs() - fullname := config.MakeCacheFilename("argv", me.pb.AppInfo.APPNAME) - me.Err = config.LoadFromFilename(me.all, fullname) - if me.Err != nil { + 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") } // try to register bash args for go-args @@ -143,6 +156,10 @@ func doAutocomplete() { if s == "--autodebug" { continue } + if s == "--argvdebug" { + me.pb.PrintStderr() + continue + } if strings.TrimSpace(s) == "" { // skip anything blank continue @@ -195,14 +212,14 @@ func doAutocomplete() { } } - // save now. this is near the end probably - me.all.Clone(me.pb) - errors.Join(me.Err, me.all.Save()) - + me.pb.HelpCounter = me.last.HelpCounter if me.pb.Fast { if me.last.Fast { me.debug = true - me.pb.PrintStderr() + me.pb.HelpCounter = me.last.HelpCounter + 1 + if me.pb.HelpCounter < 3 { + me.pb.PrintStderr() + } } else { // this means the user is pressing tab. no longer doing stderr if me.pb.GetCmd() == "" { @@ -212,10 +229,15 @@ func doAutocomplete() { // me.pp.WriteHelpForSubcommand(Stderr, me.pb.Cmd) me.writeHelpForSubcommandFunc(me.pb.GetCmd()) } + me.pb.HelpCounter = 0 } } else { } + // save now. this is near the end probably + me.all.Clone(me.pb) + errors.Join(me.Err, me.all.Save()) + if me.autoFunc == nil { me.pb.SubCommand(me.pb.Real...) } else { |
