mirror of https://github.com/Hilbis/Hilbish
Compare commits
7 Commits
c342f4f6f5
...
ce625aca0c
Author | SHA1 | Date |
---|---|---|
sammyette | ce625aca0c | |
TorchedSammy | 1715a1f626 | |
TorchedSammy | f002eca258 | |
TorchedSammy | 2814f44163 | |
TorchedSammy | ea7517be05 | |
TorchedSammy | 508fd5f8a2 | |
TorchedSammy | c95ff42dee |
48
api.go
48
api.go
|
@ -132,6 +132,11 @@ Check out the {blue}{bold}guide{reset} command to get started.
|
|||
util.Document(jobModule, "(Background) job interface.")
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -466,8 +471,10 @@ func hlgoro(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|||
|
||||
// timeout(cb, time)
|
||||
// Runs the `cb` function after `time` in milliseconds
|
||||
// Returns a `timer` object (see `doc timers`).
|
||||
// --- @param cb function
|
||||
// --- @param time number
|
||||
// --- @return table
|
||||
func hltimeout(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.CheckNArgs(2); err != nil {
|
||||
return nil, err
|
||||
|
@ -481,21 +488,19 @@ func hltimeout(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|||
return nil, err
|
||||
}
|
||||
|
||||
timeout := time.Duration(ms) * time.Millisecond
|
||||
time.Sleep(timeout)
|
||||
interval := time.Duration(ms) * time.Millisecond
|
||||
timer := timers.create(timerTimeout, interval, cb)
|
||||
timer.start()
|
||||
|
||||
_, err = rt.Call1(l.MainThread(), rt.FunctionValue(cb))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return c.Next(), nil
|
||||
return c.PushingNext1(t.Runtime, timer.lua()), nil
|
||||
}
|
||||
|
||||
// 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 time number
|
||||
// --- @return table
|
||||
func hlinterval(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
||||
if err := c.CheckNArgs(2); err != nil {
|
||||
return nil, err
|
||||
|
@ -508,29 +513,12 @@ func hlinterval(t *rt.Thread, c *rt.GoCont) (rt.Cont, error) {
|
|||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
interval := time.Duration(ms) * time.Millisecond
|
||||
timer := timers.create(timerInterval, interval, cb)
|
||||
timer.start()
|
||||
|
||||
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
|
||||
return c.PushingNext1(t.Runtime, timer.lua()), nil
|
||||
}
|
||||
|
||||
// complete(scope, cb)
|
||||
|
|
|
@ -28,7 +28,8 @@ which will be used for the hint.
|
|||
|
||||
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`
|
||||
|
||||
|
@ -56,6 +57,7 @@ sh, and lua. It also accepts a function, to which if it is passed one
|
|||
will call it to execute user input instead.
|
||||
|
||||
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
|
||||
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
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
|
|
@ -51,9 +51,11 @@ function hilbish.hinter(cb) end
|
|||
--- @param mode string
|
||||
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 time number
|
||||
--- @return table
|
||||
function hilbish.interval(cb, time) end
|
||||
|
||||
--- Changes the continued line prompt to `str`
|
||||
|
@ -94,8 +96,10 @@ function hilbish.run(cmd) end
|
|||
function hilbish.runnerMode(mode) end
|
||||
|
||||
--- Runs the `cb` function after `time` in milliseconds
|
||||
--- Returns a `timer` object (see `doc timers`).
|
||||
--- @param cb function
|
||||
--- @param time number
|
||||
--- @return table
|
||||
function hilbish.timeout(cb, time) end
|
||||
|
||||
--- Searches for an executable called `binName` in the directories of $PATH
|
||||
|
|
3
exec.go
3
exec.go
|
@ -125,6 +125,9 @@ func handleSh(cmdString string) (uint8, error) {
|
|||
if err != nil {
|
||||
// If input is incomplete, start multiline prompting
|
||||
if syntax.IsIncomplete(err) {
|
||||
if !interactive {
|
||||
return 126, err
|
||||
}
|
||||
for {
|
||||
cmdString, err = continuePrompt(strings.TrimSuffix(cmdString, "\\"))
|
||||
if err != nil {
|
||||
|
|
14
main.go
14
main.go
|
@ -145,6 +145,7 @@ func main() {
|
|||
text := scanner.Text()
|
||||
runInput(text, true)
|
||||
}
|
||||
exit(0)
|
||||
}
|
||||
|
||||
if *cmdflag != "" {
|
||||
|
@ -161,9 +162,9 @@ func main() {
|
|||
err := util.DoFile(l, getopt.Arg(0))
|
||||
if err != nil {
|
||||
fmt.Fprintln(os.Stderr, err)
|
||||
os.Exit(1)
|
||||
exit(1)
|
||||
}
|
||||
os.Exit(0)
|
||||
exit(0)
|
||||
}
|
||||
|
||||
initialized = true
|
||||
|
@ -299,3 +300,12 @@ func contains(s []string, e string) bool {
|
|||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func exit(code int) {
|
||||
// wait for all timers to finish before exiting
|
||||
for {
|
||||
if timers.running == 0 {
|
||||
os.Exit(code)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -49,6 +49,8 @@ var (
|
|||
seqEndSc = string([]byte{27, 91, 52, 126})
|
||||
seqDelete = string([]byte{27, 91, 51, 126})
|
||||
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})
|
||||
seqAltQuote = string([]byte{27, 34}) // Added for showing registers ^["
|
||||
seqAltR = string([]byte{27, 114}) // Used for alternative history
|
||||
|
|
|
@ -182,3 +182,27 @@ func (rl *Instance) deleteToEnd() {
|
|||
// Keep everything before the cursor
|
||||
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
|
||||
}
|
||||
|
|
|
@ -170,9 +170,18 @@ func (rl *Instance) Readline() (string, error) {
|
|||
rl.clearHelpers()
|
||||
return "", CtrlC
|
||||
|
||||
case charEOF:
|
||||
rl.clearHelpers()
|
||||
return "", EOF
|
||||
case charEOF: // ctrl d
|
||||
if len(rl.line) == 0 {
|
||||
rl.clearHelpers()
|
||||
return "", EOF
|
||||
}
|
||||
if rl.modeTabFind {
|
||||
rl.backspaceTabFind()
|
||||
} else {
|
||||
if (rl.pos < len(rl.line)) {
|
||||
rl.deleteBackspace(true)
|
||||
}
|
||||
}
|
||||
|
||||
// Clear screen
|
||||
case charCtrlL:
|
||||
|
@ -777,6 +786,19 @@ func (rl *Instance) escapeSeq(r []rune) {
|
|||
rl.viDeleteByAdjust(rl.viJumpB(tokeniseLine))
|
||||
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:
|
||||
if rl.modeTabFind {
|
||||
return
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
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)
|
||||
}
|
|
@ -0,0 +1,102 @@
|
|||
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
|
||||
}
|
Loading…
Reference in New Issue