From 80d72a29bb1601561e93e6be02bdf85c259cfde8 Mon Sep 17 00:00:00 2001
From: sammyette <torchedsammy@gmail.com>
Date: Wed, 23 Apr 2025 20:10:08 -0400
Subject: [PATCH] feat: return values returned by bait hooks (closes #297)

---
 CHANGELOG.md        |  1 +
 golibs/bait/bait.go | 53 ++++++++++++++++++---------------------------
 lua.go              |  3 ++-
 3 files changed, 24 insertions(+), 33 deletions(-)

diff --git a/CHANGELOG.md b/CHANGELOG.md
index c07d540..58a0cd6 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,7 @@
 ## Unreleased
 ### Added
 - Forward/Right arrow key will fill in hint text (#327)
+- Values returned by bait hooks will be passed to the `throw` caller
 
 ## [2.3.4] - 2024-12-28
 ### Fixed
diff --git a/golibs/bait/bait.go b/golibs/bait/bait.go
index 1f85c76..9a38d02 100644
--- a/golibs/bait/bait.go
+++ b/golibs/bait/bait.go
@@ -47,7 +47,7 @@ type Recoverer func(event string, handler *Listener, err interface{})
 type Listener struct{
 	typ listenerType
 	once bool
-	caller func(...interface{})
+	caller func(...interface{}) rt.Value
 	luaCaller *rt.Closure
 }
 
@@ -73,10 +73,11 @@ func New(rtm *rt.Runtime) *Bait {
 }
 
 // Emit throws an event.
-func (b *Bait) Emit(event string, args ...interface{}) {
+func (b *Bait) Emit(event string, args ...interface{}) []rt.Value {
+	var returns []rt.Value
 	handles := b.handlers[event]
 	if handles == nil {
-		return
+		return nil
 	}
 
 	for idx, handle := range handles {
@@ -97,28 +98,37 @@ func (b *Bait) Emit(event string, args ...interface{}) {
 				}
 				luaArgs = append(luaArgs, luarg)
 			}
-			_, err := rt.Call1(b.rtm.MainThread(), funcVal, luaArgs...)
+			luaRet, err := rt.Call1(b.rtm.MainThread(), funcVal, luaArgs...)
 			if err != nil {
 				if event != "error" {
 					b.Emit("error", event, handle.luaCaller, err.Error())
-					return
+					return nil
 				}
 				// if there is an error in an error event handler, panic instead
 				// (calls the go recoverer function)
 				panic(err)
 			}
+
+			if luaRet != rt.NilValue {
+				returns = append(returns, luaRet)
+			}
 		} else {
-			handle.caller(args...)
+			ret := handle.caller(args...)
+			if ret != rt.NilValue {
+				returns = append(returns, ret)
+			}
 		}
 
 		if handle.once {
 			b.removeListener(event, idx)
 		}
 	}
+
+	return returns
 }
 
 // On adds a Go function handler for an event.
-func (b *Bait) On(event string, handler func(...interface{})) *Listener {
+func (b *Bait) On(event string, handler func(...interface{}) rt.Value) *Listener {
 	listener := &Listener{
 		typ: goListener,
 		caller: handler,
@@ -130,7 +140,7 @@ func (b *Bait) On(event string, handler func(...interface{})) *Listener {
 
 // OnLua adds a Lua function handler for an event.
 func (b *Bait) OnLua(event string, handler *rt.Closure) *Listener {
-	listener :=&Listener{
+	listener := &Listener{
 		typ: luaListener,
 		luaCaller: handler,
 	}
@@ -162,7 +172,7 @@ func (b *Bait) OffLua(event string, handler *rt.Closure) {
 }
 
 // Once adds a Go function listener for an event that only runs once.
-func (b *Bait) Once(event string, handler func(...interface{})) *Listener {
+func (b *Bait) Once(event string, handler func(...interface{}) rt.Value) *Listener {
 	listener := &Listener{
 		typ: goListener,
 		once: true,
@@ -226,27 +236,6 @@ func (b *Bait) loaderFunc(rtm *rt.Runtime) (rt.Value, func()) {
 	return rt.TableValue(mod), nil
 }
 
-func handleHook(t *rt.Thread, c *rt.GoCont, name string, catcher *rt.Closure, args ...interface{}) {
-	funcVal := rt.FunctionValue(catcher)
-	var luaArgs []rt.Value
-	for _, arg := range args {
-		var luarg rt.Value
-		switch arg.(type) {
-			case rt.Value: luarg = arg.(rt.Value)
-			default: luarg = rt.AsValue(arg)
-		}
-		luaArgs = append(luaArgs, luarg)
-	}
-	_, err := rt.Call1(t, funcVal, luaArgs...)
-	if err != nil {
-		e := rt.NewError(rt.StringValue(err.Error()))
-		e = e.AddContext(c.Next(), 1)
-		// panicking here won't actually cause hilbish to panic and instead will
-		// print the error and remove the hook (look at emission recover from above)
-		panic(e)
-	}
-}
-
 // catch(name, cb)
 // Catches an event. This function can be used to act on events.
 // #param name string The name of the hook.
@@ -370,7 +359,7 @@ func (b *Bait) bthrow(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
 	for i, v := range c.Etc() {
 		ifaceSlice[i] = v
 	}
-	b.Emit(name, ifaceSlice...)
+	ret := b.Emit(name, ifaceSlice...)
 
-	return c.Next(), nil
+	return c.PushingNext(t.Runtime, ret...), nil
 }
diff --git a/lua.go b/lua.go
index 859a39d..9ae030a 100644
--- a/lua.go
+++ b/lua.go
@@ -46,10 +46,11 @@ func luaInit() {
 	lib.LoadLibs(l, hooks.Loader)
 
 	// Add Ctrl-C handler
-	hooks.On("signal.sigint", func(...interface{}) {
+	hooks.On("signal.sigint", func(...interface{}) rt.Value {
 		if !interactive {
 			os.Exit(0)
 		}
+		return rt.NilValue
 	})
 
 	lr.rl.RawInputCallback = func(r []rune) {