summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeff Carr <[email protected]>2025-10-25 02:34:49 -0500
committerJeff Carr <[email protected]>2025-10-25 02:34:49 -0500
commit87dbb0b212c48e55a50b612f022b4b035e25477f (patch)
treea156655fd9ae955fabf884cdbb816d25506b7b34
parent27e0ddeaf127f561ca7fd5a8083ab52eba6ca2fb (diff)
cleanup on aisle 9
-rw-r--r--argv.Print.go2
-rw-r--r--argv.proto1
-rw-r--r--defer_after.go66
-rw-r--r--exit.go24
-rw-r--r--theMagicOfAutocomplete.go38
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() {
diff --git a/argv.proto b/argv.proto
index 9a38585..a67a896 100644
--- a/argv.proto
+++ b/argv.proto
@@ -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()
+}
diff --git a/exit.go b/exit.go
index 107230c..4bffade 100644
--- a/exit.go
+++ b/exit.go
@@ -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 {