diff --git a/cmd/micro/initlua.go b/cmd/micro/initlua.go index f3e302db..1c6715e1 100644 --- a/cmd/micro/initlua.go +++ b/cmd/micro/initlua.go @@ -85,6 +85,8 @@ func luaImportMicroShell() *lua.LTable { ulua.L.SetField(pkg, "JobSpawn", luar.New(ulua.L, shell.JobSpawn)) ulua.L.SetField(pkg, "JobStop", luar.New(ulua.L, shell.JobStop)) ulua.L.SetField(pkg, "JobSend", luar.New(ulua.L, shell.JobSend)) + ulua.L.SetField(pkg, "RunTermEmulator", luar.New(ulua.L, action.RunTermEmulator)) + ulua.L.SetField(pkg, "TermEmuSupported", luar.New(ulua.L, action.TermEmuSupported)) return pkg } @@ -106,6 +108,9 @@ func luaImportMicroBuffer() *lua.LTable { ulua.L.SetField(pkg, "BTScratch", luar.New(ulua.L, buffer.BTScratch.Kind)) ulua.L.SetField(pkg, "BTRaw", luar.New(ulua.L, buffer.BTRaw.Kind)) ulua.L.SetField(pkg, "BTInfo", luar.New(ulua.L, buffer.BTInfo.Kind)) + ulua.L.SetField(pkg, "NewBufferFromFile", luar.New(ulua.L, func(path string) (*buffer.Buffer, error) { + return buffer.NewBufferFromFile(path, buffer.BTDefault) + })) return pkg } diff --git a/cmd/micro/micro.go b/cmd/micro/micro.go index 734539de..53c88bc5 100644 --- a/cmd/micro/micro.go +++ b/cmd/micro/micro.go @@ -236,6 +236,7 @@ func main() { for _, b := range buffer.OpenBuffers { b.Save() } + case <-shell.CloseTerms: case event = <-events: case <-screen.DrawChan: } diff --git a/internal/action/bufpane.go b/internal/action/bufpane.go index 4f487604..4f03e640 100644 --- a/internal/action/bufpane.go +++ b/internal/action/bufpane.go @@ -30,8 +30,14 @@ func init() { func LuaAction(fn string) func(*BufPane) bool { luaFn := strings.Split(fn, ".") + if len(luaFn) <= 1 { + return nil + } plName, plFn := luaFn[0], luaFn[1] pl := config.FindPlugin(plName) + if pl == nil { + return nil + } return func(h *BufPane) bool { val, err := pl.Call(plFn, luar.New(ulua.L, h)) if err != nil { diff --git a/internal/action/command.go b/internal/action/command.go index 082d0746..37ff0788 100644 --- a/internal/action/command.go +++ b/internal/action/command.go @@ -78,8 +78,14 @@ func LuaMakeCommand(name, function string, completer buffer.Completer) { // so that a command can be bound to a lua function func LuaFunctionCommand(fn string) func(*BufPane, []string) { luaFn := strings.Split(fn, ".") + if len(luaFn) <= 1 { + return nil + } plName, plFn := luaFn[0], luaFn[1] pl := config.FindPlugin(plName) + if pl == nil { + return nil + } return func(bp *BufPane, args []string) { var luaArgs []lua.LValue luaArgs = append(luaArgs, luar.New(ulua.L, bp)) @@ -872,9 +878,8 @@ func (h *BufPane) TermCmd(args []string) { } term := func(i int, newtab bool) { - t := new(shell.Terminal) - t.Start(args, false, true) + t.Start(args, false, true, "", nil) id := h.ID() if newtab { diff --git a/internal/action/terminal_supported.go b/internal/action/terminal_supported.go new file mode 100644 index 00000000..50e0a75d --- /dev/null +++ b/internal/action/terminal_supported.go @@ -0,0 +1,30 @@ +// +build linux darwin dragonfly openbsd_amd64 freebsd + +package action + +import ( + "github.com/zyedidia/micro/internal/shell" + "github.com/zyedidia/micro/pkg/shellwords" +) + +const TermEmuSupported = true + +func RunTermEmulator(h *BufPane, input string, wait bool, getOutput bool, callback string, userargs []interface{}) error { + args, err := shellwords.Split(input) + if err != nil { + return err + } + + t := new(shell.Terminal) + t.Start(args, getOutput, wait, callback, userargs) + + id := h.ID() + h.AddTab() + id = MainTab().Panes[0].ID() + + v := h.GetView() + MainTab().Panes[0] = NewTermPane(v.X, v.Y, v.Width, v.Height, t, id) + MainTab().SetActive(0) + + return nil +} diff --git a/internal/action/terminal_unsupported.go b/internal/action/terminal_unsupported.go new file mode 100644 index 00000000..b295fd62 --- /dev/null +++ b/internal/action/terminal_unsupported.go @@ -0,0 +1,11 @@ +// +build !linux,!darwin,!freebsd,!dragonfly,!openbsd_amd64 + +package action + +import "errors" + +const TermEmuSupported = false + +func RunTermEmulator(input string, wait bool, getOutput bool, callback string, userargs []interface{}) error { + return errors.New("Unsupported operating system") +} diff --git a/internal/action/termpane.go b/internal/action/termpane.go index 5a934567..779f473d 100644 --- a/internal/action/termpane.go +++ b/internal/action/termpane.go @@ -80,9 +80,9 @@ func (t *TermPane) HandleEvent(event tcell.Event) { } else if t.Status != shell.TTDone { t.WriteString(event.EscSeq()) } - } else if e, ok := event.(*tcell.EventMouse); !ok || t.State.Mode(terminal.ModeMouseMask) { + } else if e, ok := event.(*tcell.EventMouse); e != nil && (!ok || t.State.Mode(terminal.ModeMouseMask)) { t.WriteString(event.EscSeq()) - } else { + } else if e != nil { x, y := e.Position() v := t.GetView() x -= v.X @@ -109,6 +109,10 @@ func (t *TermPane) HandleEvent(event tcell.Event) { t.mouseReleased = true } } + + if t.Status == shell.TTClose { + t.Quit() + } } func (t *TermPane) HandleCommand(input string) { diff --git a/internal/display/statusline.go b/internal/display/statusline.go index 0fae348c..172f850d 100644 --- a/internal/display/statusline.go +++ b/internal/display/statusline.go @@ -53,8 +53,14 @@ var statusInfo = map[string]func(*buffer.Buffer) string{ func SetStatusInfoFnLua(s string, fn string) { luaFn := strings.Split(fn, ".") + if len(luaFn) <= 1 { + return + } plName, plFn := luaFn[0], luaFn[1] pl := config.FindPlugin(plName) + if pl == nil { + return + } statusInfo[s] = func(b *buffer.Buffer) string { if pl == nil || !pl.IsEnabled() { return "" diff --git a/internal/shell/job.go b/internal/shell/job.go index 3dfb8ced..8c29bb9c 100644 --- a/internal/shell/job.go +++ b/internal/shell/job.go @@ -106,8 +106,14 @@ func JobSend(cmd *exec.Cmd, data string) { // to the lua function func luaFunctionJob(fn string) func(string, ...interface{}) { luaFn := strings.Split(fn, ".") + if len(luaFn) <= 1 { + return nil + } plName, plFn := luaFn[0], luaFn[1] pl := config.FindPlugin(plName) + if pl == nil { + return nil + } return func(output string, args ...interface{}) { var luaArgs []lua.LValue luaArgs = append(luaArgs, luar.New(ulua.L, output)) diff --git a/internal/shell/terminal.go b/internal/shell/terminal.go index 9732e5cb..608271d4 100644 --- a/internal/shell/terminal.go +++ b/internal/shell/terminal.go @@ -6,13 +6,19 @@ import ( "os" "os/exec" "strconv" + "strings" + lua "github.com/yuin/gopher-lua" "github.com/zyedidia/micro/internal/buffer" + "github.com/zyedidia/micro/internal/config" + ulua "github.com/zyedidia/micro/internal/lua" "github.com/zyedidia/micro/internal/screen" "github.com/zyedidia/terminal" + luar "layeh.com/gopher-luar" ) type TermType int +type CallbackFunc func(string) const ( TTClose = iota // Should be closed @@ -20,6 +26,12 @@ const ( TTDone // Finished running a command ) +var CloseTerms chan bool + +func init() { + CloseTerms = make(chan bool) +} + // A Terminal holds information for the terminal emulator type Terminal struct { State terminal.State @@ -30,7 +42,7 @@ type Terminal struct { wait bool getOutput bool output *bytes.Buffer - callback string + callback CallbackFunc } // HasSelection returns whether this terminal has a valid selection @@ -64,7 +76,7 @@ func (t *Terminal) GetSelection(width int) string { } // Start begins a new command in this terminal with a given view -func (t *Terminal) Start(execCmd []string, getOutput bool, wait bool) error { +func (t *Terminal) Start(execCmd []string, getOutput bool, wait bool, callback string, userargs []interface{}) error { if len(execCmd) <= 0 { return nil } @@ -84,6 +96,25 @@ func (t *Terminal) Start(execCmd []string, getOutput bool, wait bool) error { t.title = execCmd[0] + ":" + strconv.Itoa(cmd.Process.Pid) t.wait = wait + luaFn := strings.Split(callback, ".") + if len(luaFn) >= 2 { + plName, plFn := luaFn[0], luaFn[1] + pl := config.FindPlugin(plName) + if pl != nil { + t.callback = func(out string) { + var luaArgs []lua.LValue + luaArgs = append(luaArgs, luar.New(ulua.L, out)) + for _, v := range userargs { + luaArgs = append(luaArgs, luar.New(ulua.L, v)) + } + _, err := pl.Call(plFn, luaArgs...) + if err != nil { + screen.TermMessage(err) + } + } + } + } + go func() { for { err := Term.Parse() @@ -108,6 +139,7 @@ func (t *Terminal) Stop() { t.Status = TTDone } else { t.Close() + CloseTerms <- true } } @@ -117,11 +149,9 @@ func (t *Terminal) Close() { t.Status = TTClose // call the lua function that the user has given as a callback if t.getOutput { - // TODO: plugin callback on Term emulator - // _, err := Call(t.callback, t.output.String()) - // if err != nil && !strings.HasPrefix(err.Error(), "function does not exist") { - // TermMessage(err) - // } + if t.callback != nil { + t.callback(t.output.String()) + } } }