Compare commits

...

5 Commits

Author SHA1 Message Date
TorchedSammy 55926368a3
fix: pass userdata to job.done hook 2022-05-28 19:00:29 -04:00
TorchedSammy 10337af11e
docs: update changelog and docs referencing how objects are userdata 2022-05-28 17:24:14 -04:00
TorchedSammy e656b8f210
refactor!: use userdata for timer objects 2022-05-28 17:17:59 -04:00
TorchedSammy a004f315be
chore: merge from master 2022-05-28 14:22:27 -04:00
TorchedSammy e3c25586e4
fix: broken ansi escape codes on windows cmd 2022-05-28 09:07:28 -04:00
8 changed files with 120 additions and 32 deletions

View File

@ -56,6 +56,9 @@ This is probably one of (if not the) biggest things in this release.
user input, exit code, and error. User input has been added to the return to user input, exit code, and error. User input has been added to the return to
account for runners wanting to prompt for continued input, and to add it account for runners wanting to prompt for continued input, and to add it
properly to history. properly to history.
- **Breaking Change:** Job objects and timers are now Lua userdata instead
of a table, so their functions require you to call them with a colon instead
of a dot. (ie. `job.stop()` -> `job:stop()`)
- All `fs` module functions which take paths now implicitly expand ~ to home. - All `fs` module functions which take paths now implicitly expand ~ to home.
### Fixed ### Fixed

4
api.go
View File

@ -476,7 +476,7 @@ func hltimeout(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
timer := timers.create(timerTimeout, interval, cb) timer := timers.create(timerTimeout, interval, cb)
timer.start() timer.start()
return c.PushingNext1(t.Runtime, timer.lua()), nil return c.PushingNext1(t.Runtime, rt.UserDataValue(timer.ud)), nil
} }
// interval(cb, time) // interval(cb, time)
@ -502,7 +502,7 @@ func hlinterval(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
timer := timers.create(timerInterval, interval, cb) timer := timers.create(timerInterval, interval, cb)
timer.start() timer.start()
return c.PushingNext1(t.Runtime, timer.lua()), nil return c.PushingNext1(t.Runtime, rt.UserDataValue(timer.ud)), nil
} }
// complete(scope, cb) // complete(scope, cb)

View File

@ -20,8 +20,11 @@ and `execPath` is an absolute path for the command executable.
- `disown(id)`: Removes a job by ID from the job table. - `disown(id)`: Removes a job by ID from the job table.
# Job Object # Job Object
A job object on the Lua side is a table with some functions. A job object is a piece of `userdata`. All the functions of a job require
On the under side it represents a job in the job table. you to call them with a colon, since they are *methods* for the job object.
Example: hilbish.jobs.last():foreground()
Which will foreground the last job.
You can still have a job object for a disowned job, You can still have a job object for a disowned job,
it just won't be *working* anywhere. :^) it just won't be *working* anywhere. :^)

View File

@ -16,9 +16,17 @@ a timer via ID.
when the timer is triggered. when the timer is triggered.
# Timer Object # Timer Object
Those previously mentioned functions return a `timer` object, to which you can All those previously mentioned functions return a `timer` object, to which
stop and start a timer again. The functions of the timers interface also you can stop and start a timer again.
return a timer object.
An example of usage:
local t = hilbish.timers.create(1, 5000, function()
print 'hello!'
end)
t:stop()
print(t.running, t.duration, t.type)
t:start()
## Properties ## Properties
- `duration`: amount of time the timer runs for in milliseconds - `duration`: amount of time the timer runs for in milliseconds

11
init_windows.go 100644
View File

@ -0,0 +1,11 @@
// +build windows
package main
import "golang.org/x/sys/windows"
func init() {
var mode uint32
windows.GetConsoleMode(windows.Stdout, &mode)
windows.SetConsoleMode(windows.Stdout, mode | windows.ENABLE_VIRTUAL_TERMINAL_PROCESSING)
}

2
job.go
View File

@ -82,7 +82,7 @@ func (j *job) stop() {
func (j *job) finish() { func (j *job) finish() {
j.running = false j.running = false
hooks.Em.Emit("job.done", j.ud) hooks.Em.Emit("job.done", rt.UserDataValue(j.ud))
} }
func (j *job) wait() { func (j *job) wait() {

View File

@ -6,8 +6,6 @@ import (
"os" "os"
"time" "time"
"hilbish/util"
rt "github.com/arnodel/golua/runtime" rt "github.com/arnodel/golua/runtime"
) )
@ -25,6 +23,7 @@ type timer struct{
fun *rt.Closure fun *rt.Closure
th *timerHandler th *timerHandler
ticker *time.Ticker ticker *time.Ticker
ud *rt.UserData
channel chan struct{} channel chan struct{}
} }
@ -74,8 +73,17 @@ func (t *timer) stop() error {
return nil return nil
} }
func (t *timer) luaStart(thr *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func timerStart(thr *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
err := t.start() if err := c.Check1Arg(); err != nil {
return nil, err
}
t, err := timerArg(c, 0)
if err != nil {
return nil, err
}
err = t.start()
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -83,26 +91,20 @@ func (t *timer) luaStart(thr *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return c.Next(), nil return c.Next(), nil
} }
func (t *timer) luaStop(thr *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func timerStop(thr *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
err := t.stop() if err := c.Check1Arg(); err != nil {
return nil, err
}
t, err := timerArg(c, 0)
if err != nil {
return nil, err
}
err = t.stop()
if err != nil { if err != nil {
return nil, err return nil, err
} }
return c.Next(), nil return c.Next(), nil
} }
func (t *timer) lua() rt.Value {
tExports := map[string]util.LuaExport{
"start": {t.luaStart, 0, false},
"stop": {t.luaStop, 0, false},
}
luaTimer := rt.NewTable()
util.SetExports(l, luaTimer, tExports)
luaTimer.Set(rt.StringValue("type"), rt.IntValue(int64(t.typ)))
luaTimer.Set(rt.StringValue("running"), rt.BoolValue(t.running))
luaTimer.Set(rt.StringValue("duration"), rt.IntValue(int64(t.dur / time.Millisecond)))
return rt.TableValue(luaTimer)
}

View File

@ -1,6 +1,7 @@
package main package main
import ( import (
"fmt"
"sync" "sync"
"time" "time"
@ -10,6 +11,8 @@ import (
) )
var timers *timerHandler var timers *timerHandler
var timerMetaKey = rt.StringValue("hshtimer")
type timerHandler struct { type timerHandler struct {
mu *sync.RWMutex mu *sync.RWMutex
wg *sync.WaitGroup wg *sync.WaitGroup
@ -44,6 +47,8 @@ func (th *timerHandler) create(typ timerType, dur time.Duration, fun *rt.Closure
th: th, th: th,
id: th.latestID, id: th.latestID,
} }
t.ud = timerUserData(t)
th.timers[th.latestID] = t th.timers[th.latestID] = t
return t return t
@ -75,7 +80,7 @@ func (th *timerHandler) luaCreate(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
timerTyp := timerType(timerTypInt) timerTyp := timerType(timerTypInt)
tmr := th.create(timerTyp, time.Duration(ms) * time.Millisecond, cb) tmr := th.create(timerTyp, time.Duration(ms) * time.Millisecond, cb)
return c.PushingNext1(t.Runtime, tmr.lua()), nil return c.PushingNext1(t.Runtime, rt.UserDataValue(tmr.ud)), nil
} }
func (th *timerHandler) luaGet(thr *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func (th *timerHandler) luaGet(thr *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
@ -89,13 +94,45 @@ func (th *timerHandler) luaGet(thr *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
t := th.get(int(id)) t := th.get(int(id))
if t != nil { if t != nil {
return c.PushingNext1(thr.Runtime, t.lua()), nil return c.PushingNext1(thr.Runtime, rt.UserDataValue(t.ud)), nil
} }
return c.Next(), nil return c.Next(), nil
} }
func (th *timerHandler) loader(rtm *rt.Runtime) *rt.Table { func (th *timerHandler) loader(rtm *rt.Runtime) *rt.Table {
timerMethods := rt.NewTable()
timerFuncs := map[string]util.LuaExport{
"start": {timerStart, 1, false},
"stop": {timerStop, 1, false},
}
util.SetExports(rtm, timerMethods, timerFuncs)
timerMeta := rt.NewTable()
timerIndex := func(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
ti, _ := timerArg(c, 0)
arg := c.Arg(1)
val := timerMethods.Get(arg)
if val != rt.NilValue {
return c.PushingNext1(t.Runtime, val), nil
}
keyStr, _ := arg.TryString()
switch keyStr {
case "type": val = rt.IntValue(int64(ti.typ))
case "running": val = rt.BoolValue(ti.running)
case "duration": val = rt.IntValue(int64(ti.dur / time.Millisecond))
}
return c.PushingNext1(t.Runtime, val), nil
}
timerMeta.Set(rt.StringValue("__index"), rt.FunctionValue(rt.NewGoFunction(timerIndex, "__index", 2, false)))
l.SetRegistry(timerMetaKey, rt.TableValue(timerMeta))
thExports := map[string]util.LuaExport{ thExports := map[string]util.LuaExport{
"create": {th.luaCreate, 3, false}, "create": {th.luaCreate, 3, false},
"get": {th.luaGet, 1, false}, "get": {th.luaGet, 1, false},
@ -106,3 +143,27 @@ func (th *timerHandler) loader(rtm *rt.Runtime) *rt.Table {
return luaTh return luaTh
} }
func timerArg(c *rt.GoCont, arg int) (*timer, error) {
j, ok := valueToTimer(c.Arg(arg))
if !ok {
return nil, fmt.Errorf("#%d must be a timer", arg + 1)
}
return j, nil
}
func valueToTimer(val rt.Value) (*timer, bool) {
u, ok := val.TryUserData()
if !ok {
return nil, false
}
j, ok := u.Value().(*timer)
return j, ok
}
func timerUserData(j *timer) *rt.UserData {
timerMeta := l.Registry(timerMetaKey)
return rt.NewUserData(j, timerMeta.AsTable())
}