Compare commits

..

No commits in common. "ce625aca0cb1b9218b4652216ef76e1f73d6ff3c" and "c342f4f6f5f820fca228b9d92173c4e92ac53ee0" have entirely different histories.

11 changed files with 38 additions and 331 deletions

50
api.go
View File

@ -131,11 +131,6 @@ Check out the {blue}{bold}guide{reset} command to get started.
jobModule := jobs.loader(rtm) jobModule := jobs.loader(rtm)
util.Document(jobModule, "(Background) job interface.") util.Document(jobModule, "(Background) job interface.")
mod.Set(rt.StringValue("jobs"), rt.TableValue(jobModule)) mod.Set(rt.StringValue("jobs"), rt.TableValue(jobModule))
timers = newTimerHandler()
timerModule := timers.loader(rtm)
util.Document(timerModule, "Timer interface, for control of all intervals and timeouts.")
mod.Set(rt.StringValue("timers"), rt.TableValue(timerModule))
return rt.TableValue(mod), nil return rt.TableValue(mod), nil
} }
@ -471,10 +466,8 @@ func hlgoro(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
// timeout(cb, time) // timeout(cb, time)
// Runs the `cb` function after `time` in milliseconds // Runs the `cb` function after `time` in milliseconds
// Returns a `timer` object (see `doc timers`).
// --- @param cb function // --- @param cb function
// --- @param time number // --- @param time number
// --- @return table
func hltimeout(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func hltimeout(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.CheckNArgs(2); err != nil { if err := c.CheckNArgs(2); err != nil {
return nil, err return nil, err
@ -488,19 +481,21 @@ func hltimeout(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
return nil, err return nil, err
} }
interval := time.Duration(ms) * time.Millisecond timeout := time.Duration(ms) * time.Millisecond
timer := timers.create(timerTimeout, interval, cb) time.Sleep(timeout)
timer.start()
_, err = rt.Call1(l.MainThread(), rt.FunctionValue(cb))
return c.PushingNext1(t.Runtime, timer.lua()), nil if err != nil {
return nil, err
}
return c.Next(), nil
} }
// interval(cb, time) // interval(cb, time)
// Runs the `cb` function every `time` milliseconds. // Runs the `cb` function every `time` milliseconds
// Returns a `timer` object (see `doc timers`).
// --- @param cb function // --- @param cb function
// --- @param time number // --- @param time number
// --- @return table
func hlinterval(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) { func hlinterval(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.CheckNArgs(2); err != nil { if err := c.CheckNArgs(2); err != nil {
return nil, err return nil, err
@ -513,12 +508,29 @@ func hlinterval(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err != nil { if err != nil {
return nil, err return nil, err
} }
interval := time.Duration(ms) * time.Millisecond interval := time.Duration(ms) * time.Millisecond
timer := timers.create(timerInterval, interval, cb)
timer.start()
return c.PushingNext1(t.Runtime, timer.lua()), nil ticker := time.NewTicker(interval)
stop := make(chan rt.Value)
go func() {
for {
select {
case <-ticker.C:
_, err := rt.Call1(l.MainThread(), rt.FunctionValue(cb))
if err != nil {
fmt.Fprintln(os.Stderr, "Error in interval function:\n\n", err)
stop <- rt.BoolValue(true) // stop the interval
}
case <-stop:
ticker.Stop()
return
}
}
}()
// TODO: return channel
return c.Next(), nil
} }
// complete(scope, cb) // complete(scope, cb)

View File

@ -28,8 +28,7 @@ which will be used for the hint.
inputMode(mode) > Sets the input mode for Hilbish's line reader. Accepts either emacs for vim inputMode(mode) > Sets the input mode for Hilbish's line reader. Accepts either emacs for vim
interval(cb, time) > Runs the `cb` function every `time` milliseconds. interval(cb, time) > Runs the `cb` function every `time` milliseconds
Returns a `timer` object (see `doc timers`).
multiprompt(str) > Changes the continued line prompt to `str` multiprompt(str) > Changes the continued line prompt to `str`
@ -57,7 +56,6 @@ sh, and lua. It also accepts a function, to which if it is passed one
will call it to execute user input instead. will call it to execute user input instead.
timeout(cb, time) > Runs the `cb` function after `time` in milliseconds timeout(cb, time) > Runs the `cb` function after `time` in milliseconds
Returns a `timer` object (see `doc timers`).
which(binName) > Searches for an executable called `binName` in the directories of $PATH which(binName) > Searches for an executable called `binName` in the directories of $PATH

View File

@ -1,30 +0,0 @@
If you ever want to run a piece of code on a timed interval, or want to wait
a few seconds, you don't have to rely on timing tricks, as Hilbish has a
timer API to set intervals and timeouts.
These are the simple functions `hilbish.interval` and `hilbish.timeout` (doc
accessible with `doc hilbish`). But if you want slightly more control over
them, there is the `hilbish.timers` interface. It allows you to get
a timer via ID.
# Timer Interface
## Functions
- `get(id)` -> timer: get a timer via its id
- `create(type, ms, callback)` -> timer: creates a timer, adding it to the timer pool.
`type` is the type of timer it will be. 0 is an interval, 1 is a timeout.
`ms` is the time it will run for in seconds. callback is the function called
when the timer is triggered.
# Timer Object
Those previously mentioned functions return a `timer` object, to which you can
stop and start a timer again. The functions of the timers interface also
return a timer object.
## Properties
- `duration`: amount of time the timer runs for in milliseconds
- `running`: whether the timer is running or not
- `type`: the type of timer (0 is interval, 1 is timeout)
## Functions
- `stop()`: stops the timer. returns an error if it's already stopped
- `start()`: starts the timer. returns an error if it's already started

View File

@ -51,11 +51,9 @@ function hilbish.hinter(cb) end
--- @param mode string --- @param mode string
function hilbish.inputMode(mode) end function hilbish.inputMode(mode) end
--- Runs the `cb` function every `time` milliseconds. --- Runs the `cb` function every `time` milliseconds
--- Returns a `timer` object (see `doc timers`).
--- @param cb function --- @param cb function
--- @param time number --- @param time number
--- @return table
function hilbish.interval(cb, time) end function hilbish.interval(cb, time) end
--- Changes the continued line prompt to `str` --- Changes the continued line prompt to `str`
@ -96,10 +94,8 @@ function hilbish.run(cmd) end
function hilbish.runnerMode(mode) end function hilbish.runnerMode(mode) end
--- Runs the `cb` function after `time` in milliseconds --- Runs the `cb` function after `time` in milliseconds
--- Returns a `timer` object (see `doc timers`).
--- @param cb function --- @param cb function
--- @param time number --- @param time number
--- @return table
function hilbish.timeout(cb, time) end function hilbish.timeout(cb, time) end
--- Searches for an executable called `binName` in the directories of $PATH --- Searches for an executable called `binName` in the directories of $PATH

View File

@ -125,9 +125,6 @@ func handleSh(cmdString string) (uint8, error) {
if err != nil { if err != nil {
// If input is incomplete, start multiline prompting // If input is incomplete, start multiline prompting
if syntax.IsIncomplete(err) { if syntax.IsIncomplete(err) {
if !interactive {
return 126, err
}
for { for {
cmdString, err = continuePrompt(strings.TrimSuffix(cmdString, "\\")) cmdString, err = continuePrompt(strings.TrimSuffix(cmdString, "\\"))
if err != nil { if err != nil {

14
main.go
View File

@ -145,7 +145,6 @@ func main() {
text := scanner.Text() text := scanner.Text()
runInput(text, true) runInput(text, true)
} }
exit(0)
} }
if *cmdflag != "" { if *cmdflag != "" {
@ -162,9 +161,9 @@ func main() {
err := util.DoFile(l, getopt.Arg(0)) err := util.DoFile(l, getopt.Arg(0))
if err != nil { if err != nil {
fmt.Fprintln(os.Stderr, err) fmt.Fprintln(os.Stderr, err)
exit(1) os.Exit(1)
} }
exit(0) os.Exit(0)
} }
initialized = true initialized = true
@ -300,12 +299,3 @@ func contains(s []string, e string) bool {
} }
return false return false
} }
func exit(code int) {
// wait for all timers to finish before exiting
for {
if timers.running == 0 {
os.Exit(code)
}
}
}

View File

@ -49,8 +49,6 @@ var (
seqEndSc = string([]byte{27, 91, 52, 126}) seqEndSc = string([]byte{27, 91, 52, 126})
seqDelete = string([]byte{27, 91, 51, 126}) seqDelete = string([]byte{27, 91, 51, 126})
seqDelete2 = string([]byte{27, 91, 80}) seqDelete2 = string([]byte{27, 91, 80})
seqCtrlDelete = string([]byte{27, 91, 51, 59, 53, 126})
seqCtrlDelete2 = string([]byte{27, 91, 77})
seqShiftTab = string([]byte{27, 91, 90}) seqShiftTab = string([]byte{27, 91, 90})
seqAltQuote = string([]byte{27, 34}) // Added for showing registers ^[" seqAltQuote = string([]byte{27, 34}) // Added for showing registers ^["
seqAltR = string([]byte{27, 114}) // Used for alternative history seqAltR = string([]byte{27, 114}) // Used for alternative history

View File

@ -182,27 +182,3 @@ func (rl *Instance) deleteToEnd() {
// Keep everything before the cursor // Keep everything before the cursor
rl.line = rl.line[:rl.pos] rl.line = rl.line[:rl.pos]
} }
func (rl *Instance) emacsForwardWord(tokeniser tokeniser) (adjust int) {
// when emacs has more specific stuff, move this in a file with then
split, index, pos := tokeniser(rl.line, rl.pos)
if len(split) == 0 {
return
}
word := rTrimWhiteSpace(split[index])
switch {
case len(split) == 0:
return
case index == len(split)-1 && pos >= len(word)-1:
return
case pos >= len(word)-1:
word = rTrimWhiteSpace(split[index+1])
adjust = len(split[index]) - pos
adjust += len(word)
default:
adjust = len(word) - pos
}
return
}

View File

@ -170,18 +170,9 @@ func (rl *Instance) Readline() (string, error) {
rl.clearHelpers() rl.clearHelpers()
return "", CtrlC return "", CtrlC
case charEOF: // ctrl d case charEOF:
if len(rl.line) == 0 { rl.clearHelpers()
rl.clearHelpers() return "", EOF
return "", EOF
}
if rl.modeTabFind {
rl.backspaceTabFind()
} else {
if (rl.pos < len(rl.line)) {
rl.deleteBackspace(true)
}
}
// Clear screen // Clear screen
case charCtrlL: case charCtrlL:
@ -786,19 +777,6 @@ func (rl *Instance) escapeSeq(r []rune) {
rl.viDeleteByAdjust(rl.viJumpB(tokeniseLine)) rl.viDeleteByAdjust(rl.viJumpB(tokeniseLine))
rl.updateHelpers() rl.updateHelpers()
case seqCtrlDelete, seqCtrlDelete2:
if rl.modeTabCompletion {
rl.resetVirtualComp(false)
}
// This is only available in Insert mode
if rl.modeViMode != VimInsert {
return
}
rl.saveToRegister(rl.emacsForwardWord(tokeniseLine))
// vi delete, emacs forward, funny huh
rl.viDeleteByAdjust(rl.emacsForwardWord(tokeniseLine))
rl.updateHelpers()
default: default:
if rl.modeTabFind { if rl.modeTabFind {
return return

106
timer.go
View File

@ -1,106 +0,0 @@
package main
import (
"errors"
"fmt"
"os"
"time"
"hilbish/util"
rt "github.com/arnodel/golua/runtime"
)
type timerType int64
const (
timerInterval timerType = iota
timerTimeout
)
type timer struct{
id int
typ timerType
running bool
dur time.Duration
fun *rt.Closure
th *timerHandler
ticker *time.Ticker
channel chan bool
}
func (t *timer) start() error {
if t.running {
return errors.New("timer is already running")
}
t.running = true
t.th.running++
t.ticker = time.NewTicker(t.dur)
go func() {
for {
select {
case <-t.ticker.C:
_, err := rt.Call1(l.MainThread(), rt.FunctionValue(t.fun))
if err != nil {
fmt.Fprintln(os.Stderr, "Error in function:\n", err)
t.stop()
}
// only run one for timeout
if t.typ == timerTimeout {
t.stop()
}
case <-t.channel:
t.ticker.Stop()
return
}
}
}()
return nil
}
func (t *timer) stop() error {
if !t.running {
return errors.New("timer not running")
}
t.channel <- true
t.running = false
t.th.running--
return nil
}
func (t *timer) luaStart(thr *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
err := t.start()
if err != nil {
return nil, err
}
return c.Next(), nil
}
func (t *timer) luaStop(thr *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
err := t.stop()
if err != nil {
return nil, err
}
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,102 +0,0 @@
package main
import (
"sync"
"time"
"hilbish/util"
rt "github.com/arnodel/golua/runtime"
)
var timers *timerHandler
type timerHandler struct {
mu *sync.RWMutex
timers map[int]*timer
latestID int
running int
}
func newTimerHandler() *timerHandler {
return &timerHandler{
timers: make(map[int]*timer),
latestID: 0,
mu: &sync.RWMutex{},
}
}
func (th *timerHandler) create(typ timerType, dur time.Duration, fun *rt.Closure) *timer {
th.mu.Lock()
defer th.mu.Unlock()
th.latestID++
t := &timer{
typ: typ,
fun: fun,
dur: dur,
channel: make(chan bool, 1),
th: th,
id: th.latestID,
}
th.timers[th.latestID] = t
return t
}
func (th *timerHandler) get(id int) *timer {
th.mu.RLock()
defer th.mu.RUnlock()
return th.timers[id]
}
func (th *timerHandler) luaCreate(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.CheckNArgs(3); err != nil {
return nil, err
}
timerTypInt, err := c.IntArg(0)
if err != nil {
return nil, err
}
ms, err := c.IntArg(1)
if err != nil {
return nil, err
}
cb, err := c.ClosureArg(2)
if err != nil {
return nil, err
}
timerTyp := timerType(timerTypInt)
tmr := th.create(timerTyp, time.Duration(ms) * time.Millisecond, cb)
return c.PushingNext1(t.Runtime, tmr.lua()), nil
}
func (th *timerHandler) luaGet(thr *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
if err := c.Check1Arg(); err != nil {
return nil, err
}
id, err := c.IntArg(0)
if err != nil {
return nil, err
}
t := th.get(int(id))
if t != nil {
return c.PushingNext1(thr.Runtime, t.lua()), nil
}
return c.Next(), nil
}
func (th *timerHandler) loader(rtm *rt.Runtime) *rt.Table {
thExports := map[string]util.LuaExport{
"create": {th.luaCreate, 3, false},
"get": {th.luaGet, 1, false},
}
luaTh := rt.NewTable()
util.SetExports(rtm, luaTh, thExports)
return luaTh
}