From 9089e9ec83ddbf8cecd33b2c29abf2477143a6d6 Mon Sep 17 00:00:00 2001 From: Dmytro Maluka Date: Mon, 13 Nov 2023 17:51:19 +0100 Subject: [PATCH] Add micro's own lua timer function micro.After() Directly using Go's time.AfterFunc() from lua is tricky. First, it requires the lua timer callback to explicitly lock ulua.Lock to prevent races. Second, it requires the lua timer callback to explicitly redraw the screen if the callback changes the screen contents (see #2923). So instead provide micro's own timer API which ensures both synchronization and redrawing on its own, instead of leaving this burden to lua code. In fact, its implementation runs the lua timer callback in the main micro's goroutine (i.e. from micro's perspective it is synchronous, not asynchronous), so both redrawing and synchronization are ensured automatically. Fixes #2923 --- cmd/micro/initlua.go | 6 ++++++ cmd/micro/micro.go | 6 ++++++ runtime/help/plugins.md | 5 +++++ 3 files changed, 17 insertions(+) diff --git a/cmd/micro/initlua.go b/cmd/micro/initlua.go index 5acb979d..41d752cc 100644 --- a/cmd/micro/initlua.go +++ b/cmd/micro/initlua.go @@ -2,6 +2,7 @@ package main import ( "log" + "time" lua "github.com/yuin/gopher-lua" luar "layeh.com/gopher-luar" @@ -55,6 +56,11 @@ func luaImportMicro() *lua.LTable { return action.Tabs })) ulua.L.SetField(pkg, "Lock", luar.New(ulua.L, &ulua.Lock)) + ulua.L.SetField(pkg, "After", luar.New(ulua.L, func(t time.Duration, f func()) { + time.AfterFunc(t, func() { + timerChan <- f + }) + })) return pkg } diff --git a/cmd/micro/micro.go b/cmd/micro/micro.go index e0ef0987..09c9ab2b 100644 --- a/cmd/micro/micro.go +++ b/cmd/micro/micro.go @@ -46,6 +46,8 @@ var ( sigterm chan os.Signal sighup chan os.Signal + + timerChan chan func() ) func InitFlags() { @@ -364,6 +366,8 @@ func main() { signal.Notify(sigterm, syscall.SIGTERM, syscall.SIGINT, syscall.SIGQUIT, syscall.SIGABRT) signal.Notify(sighup, syscall.SIGHUP) + timerChan = make(chan func()) + // Here is the event loop which runs in a separate thread go func() { for { @@ -429,6 +433,8 @@ func DoEvent() { for len(screen.DrawChan()) > 0 { <-screen.DrawChan() } + case f := <-timerChan: + f() case <-sighup: for _, b := range buffer.OpenBuffers { if !b.Modified() { diff --git a/runtime/help/plugins.md b/runtime/help/plugins.md index 19dd0c3e..497f623d 100644 --- a/runtime/help/plugins.md +++ b/runtime/help/plugins.md @@ -121,6 +121,11 @@ The packages and functions are listed below (in Go type signatures): current pane is not a BufPane. - `CurTab() *Tab`: returns the current tab. + + - `After(t time.Duration, f func())`: run function `f` in the background + after time `t` elapses. See https://pkg.go.dev/time#Duration for the + usage of `time.Duration`. + * `micro/config` - `MakeCommand(name string, action func(bp *BufPane, args[]string), completer buffer.Completer)`: