Compare commits

..

7 Commits

4 changed files with 134 additions and 47 deletions

View File

@ -7,20 +7,31 @@ import (
"github.com/arnodel/golua/lib/packagelib"
)
type Recoverer func(event string, handler, err interface{})
type listenerType int
const (
goListener listenerType = iota
luaListener
)
type Recoverer func(event string, handler *Listener, err interface{})
type Listener struct{
typ listenerType
once bool
caller func(...interface{})
luaCaller *rt.Closure
}
type Bait struct{
Loader packagelib.Loader
recoverer Recoverer
luaHandlers map[string][]*rt.Closure
handlers map[string][]func(...interface{})
handlers map[string][]*Listener
rtm *rt.Runtime
}
func New(rtm *rt.Runtime) Bait {
b := Bait{
luaHandlers: make(map[string][]*rt.Closure),
handlers: make(map[string][]func(...interface{})),
func New(rtm *rt.Runtime) *Bait {
b := &Bait{
handlers: make(map[string][]*Listener),
rtm: rtm,
}
b.Loader = packagelib.Loader{
@ -33,27 +44,19 @@ func New(rtm *rt.Runtime) Bait {
func (b *Bait) Emit(event string, args ...interface{}) {
handles := b.handlers[event]
luaHandles := b.luaHandlers[event]
if handles == nil && luaHandles == nil {
if handles == nil {
return
}
if handles != nil {
for _, handle := range handles {
handle(args...)
}
}
for idx, handle := range handles {
defer func() {
if err := recover(); err != nil {
b.callRecoverer(event, handle, err)
}
}()
if luaHandles != nil {
for _, handle := range luaHandles {
defer func() {
if err := recover(); err != nil {
b.callRecoverer(event, handle, err)
}
}()
funcVal := rt.FunctionValue(handle)
if handle.typ == luaListener {
funcVal := rt.FunctionValue(handle.luaCaller)
var luaArgs []rt.Value
for _, arg := range args {
var luarg rt.Value
@ -66,32 +69,101 @@ func (b *Bait) Emit(event string, args ...interface{}) {
_, err := rt.Call1(b.rtm.MainThread(), funcVal, luaArgs...)
if err != nil {
// 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)
// print the error and remove the hook. reference the recoverer function in lua.go
panic(err)
}
} else {
handle.caller(args...)
}
if handle.once {
b.removeListener(event, idx)
}
}
}
func (b *Bait) On(event string, handler func(...interface{})) {
if b.handlers[event] == nil {
b.handlers[event] = []func(...interface{}){}
func (b *Bait) On(event string, handler func(...interface{})) *Listener {
listener := &Listener{
typ: goListener,
caller: handler,
}
b.handlers[event] = append(b.handlers[event], handler)
b.addListener(event, listener)
return listener
}
func (b *Bait) OnLua(event string, handler *rt.Closure) {
if b.luaHandlers[event] == nil {
b.luaHandlers[event] = []*rt.Closure{}
func (b *Bait) OnLua(event string, handler *rt.Closure) *Listener {
listener :=&Listener{
typ: luaListener,
luaCaller: handler,
}
b.luaHandlers[event] = append(b.luaHandlers[event], handler)
b.addListener(event, listener)
return listener
}
func (b *Bait) Off(event string, listener *Listener) {
handles := b.handlers[event]
for i, handle := range handles {
if handle == listener {
b.removeListener(event, i)
}
}
}
func (b *Bait) OffLua(event string, handler *rt.Closure) {
handles := b.handlers[event]
for i, handle := range handles {
if handle.luaCaller == handler {
b.removeListener(event, i)
}
}
}
func (b *Bait) Once(event string, handler func(...interface{})) *Listener {
listener := &Listener{
typ: goListener,
once: true,
caller: handler,
}
b.addListener(event, listener)
return listener
}
func (b *Bait) OnceLua(event string, handler *rt.Closure) *Listener {
listener := &Listener{
typ: luaListener,
once: true,
luaCaller: handler,
}
b.addListener(event, listener)
return listener
}
func (b *Bait) SetRecoverer(recoverer Recoverer) {
b.recoverer = recoverer
}
func (b *Bait) callRecoverer(event string, handler, err interface{}) {
func (b *Bait) addListener(event string, listener *Listener) {
if b.handlers[event] == nil {
b.handlers[event] = []*Listener{}
}
b.handlers[event] = append(b.handlers[event], listener)
}
func (b *Bait) removeListener(event string, idx int) {
b.handlers[event][idx] = b.handlers[event][len(b.handlers[event]) - 1]
b.handlers[event] = b.handlers[event][:len(b.handlers[event]) - 1]
}
func (b *Bait) callRecoverer(event string, handler *Listener, err interface{}) {
if b.recoverer == nil {
panic(err)
}
@ -103,6 +175,7 @@ func (b *Bait) loaderFunc(rtm *rt.Runtime) (rt.Value, func()) {
"catch": util.LuaExport{b.bcatch, 2, false},
"catchOnce": util.LuaExport{b.bcatchOnce, 2, false},
"throw": util.LuaExport{b.bthrow, 1, true},
"release": util.LuaExport{b.brelease, 2, false},
}
mod := rt.NewTable()
util.SetExports(rtm, mod, exports)
@ -186,10 +259,18 @@ func (b *Bait) bcatchOnce(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return nil, err
}
// todo: add once
b.On(name, func(args ...interface{}) {
handleHook(t, c, name, catcher, args...)
})
b.OnceLua(name, catcher)
return c.Next(), nil
}
func (b *Bait) brelease(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
name, catcher, err := util.HandleStrCallback(t, c)
if err != nil {
return nil, err
}
b.OffLua(name, catcher)
return c.Next(), nil
}

View File

@ -2,20 +2,20 @@ package commander
import (
"hilbish/util"
"hilbish/golibs/bait"
rt "github.com/arnodel/golua/runtime"
"github.com/arnodel/golua/lib/packagelib"
"github.com/chuckpreslar/emission"
)
type Commander struct{
Events *emission.Emitter
Events *bait.Bait
Loader packagelib.Loader
}
func New() Commander {
func New(rtm *rt.Runtime) Commander {
c := Commander{
Events: emission.NewEmitter(),
Events: bait.New(rtm),
}
c.Loader = packagelib.Loader{
Load: c.loaderFunc,

14
lua.go
View File

@ -32,19 +32,25 @@ func luaInit() {
lib.LoadLibs(l, fs.Loader)
lib.LoadLibs(l, terminal.Loader)
cmds := commander.New()
cmds := commander.New(l)
// When a command from Lua is added, register it for use
cmds.Events.On("commandRegister", func(cmdName string, cmd *rt.Closure) {
cmds.Events.On("commandRegister", func(args ...interface{}) {
cmdName := args[0].(string)
cmd := args[1].(*rt.Closure)
commands[cmdName] = cmd
})
cmds.Events.On("commandDeregister", func(cmdName string) {
cmds.Events.On("commandDeregister", func(args ...interface{}) {
cmdName := args[0].(string)
delete(commands, cmdName)
})
lib.LoadLibs(l, cmds.Loader)
hooks = bait.New(l)
hooks.SetRecoverer(func(event string, handler, err interface{}) {
hooks.SetRecoverer(func(event string, handler *bait.Listener, err interface{}) {
fmt.Println("Error in", event, "event:", err)
hooks.Off(event, handler)
})
lib.LoadLibs(l, hooks.Loader)

View File

@ -30,7 +30,7 @@ var (
userDataDir string
curuser *user.User
hooks bait.Bait
hooks *bait.Bait
defaultConfPath string
defaultHistPath string
)