mirror of
https://github.com/zyedidia/micro.git
synced 2026-02-06 15:10:27 +09:00
Add support for job control
This commit adds support for job control (running processes asynchronously from plugins) with the JobStart, JobSend, and JobStop functions (copying neovim's job control). This commit also makes the linter plugin work asynchronously, so the editor won't be frozen while the linter checks your code for errors.
This commit is contained in:
67
cmd/micro/job.go
Normal file
67
cmd/micro/job.go
Normal file
@@ -0,0 +1,67 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"io"
|
||||
"os/exec"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type JobFunction struct {
|
||||
function func(string, ...string)
|
||||
output string
|
||||
args []string
|
||||
}
|
||||
|
||||
type CallbackFile struct {
|
||||
io.Writer
|
||||
|
||||
callback func(string, ...string)
|
||||
args []string
|
||||
}
|
||||
|
||||
func (f *CallbackFile) Write(data []byte) (int, error) {
|
||||
jobFunc := JobFunction{f.callback, string(data), f.args}
|
||||
jobs <- jobFunc
|
||||
return f.Writer.Write(data)
|
||||
}
|
||||
|
||||
func JobStart(cmd string, onStdout, onStderr, onExit string, userargs ...string) *exec.Cmd {
|
||||
split := strings.Split(cmd, " ")
|
||||
args := split[1:]
|
||||
cmdName := split[0]
|
||||
|
||||
proc := exec.Command(cmdName, args...)
|
||||
var outbuf bytes.Buffer
|
||||
if onStdout != "" {
|
||||
proc.Stdout = &CallbackFile{&outbuf, LuaFunctionJob(onStdout), userargs}
|
||||
} else {
|
||||
proc.Stdout = &outbuf
|
||||
}
|
||||
if onStderr != "" {
|
||||
proc.Stderr = &CallbackFile{&outbuf, LuaFunctionJob(onStderr), userargs}
|
||||
} else {
|
||||
proc.Stderr = &outbuf
|
||||
}
|
||||
|
||||
go func() {
|
||||
proc.Run()
|
||||
jobFunc := JobFunction{LuaFunctionJob(onExit), string(outbuf.Bytes()), userargs}
|
||||
jobs <- jobFunc
|
||||
}()
|
||||
|
||||
return proc
|
||||
}
|
||||
|
||||
func JobStop(cmd *exec.Cmd) {
|
||||
cmd.Process.Kill()
|
||||
}
|
||||
|
||||
func JobSend(cmd *exec.Cmd, data string) {
|
||||
stdin, err := cmd.StdinPipe()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
stdin.Write([]byte(data))
|
||||
}
|
||||
@@ -106,7 +106,7 @@ func (m *Messenger) YesNoPrompt(prompt string) (bool, bool) {
|
||||
m.Display()
|
||||
screen.ShowCursor(Count(m.message), h-1)
|
||||
screen.Show()
|
||||
event := screen.PollEvent()
|
||||
event := <-events
|
||||
|
||||
switch e := event.(type) {
|
||||
case *tcell.EventKey:
|
||||
@@ -149,7 +149,7 @@ func (m *Messenger) Prompt(prompt, historyType string, completionType Completion
|
||||
m.Clear()
|
||||
m.Display()
|
||||
|
||||
event := screen.PollEvent()
|
||||
event := <-events
|
||||
|
||||
switch e := event.(type) {
|
||||
case *tcell.EventKey:
|
||||
|
||||
@@ -52,6 +52,9 @@ var (
|
||||
// This is the currently open tab
|
||||
// It's just an index to the tab in the tabs array
|
||||
curTab int
|
||||
|
||||
jobs chan JobFunction
|
||||
events chan tcell.Event
|
||||
)
|
||||
|
||||
// LoadInput loads the file input for the editor
|
||||
@@ -245,14 +248,33 @@ func main() {
|
||||
L.SetGlobal("MakeCommand", luar.New(L, MakeCommand))
|
||||
L.SetGlobal("CurView", luar.New(L, CurView))
|
||||
|
||||
L.SetGlobal("JobStart", luar.New(L, JobStart))
|
||||
L.SetGlobal("JobSend", luar.New(L, JobSend))
|
||||
L.SetGlobal("JobStop", luar.New(L, JobStop))
|
||||
|
||||
LoadPlugins()
|
||||
|
||||
jobs = make(chan JobFunction, 100)
|
||||
events = make(chan tcell.Event)
|
||||
|
||||
go func() {
|
||||
for {
|
||||
events <- screen.PollEvent()
|
||||
}
|
||||
}()
|
||||
|
||||
for {
|
||||
// Display everything
|
||||
RedrawAll()
|
||||
|
||||
// Wait for the user's action
|
||||
event := screen.PollEvent()
|
||||
var event tcell.Event
|
||||
select {
|
||||
case f := <-jobs:
|
||||
f.function(f.output, f.args...)
|
||||
continue
|
||||
case event = <-events:
|
||||
}
|
||||
|
||||
if TabbarHandleMouseEvent(event) {
|
||||
continue
|
||||
}
|
||||
|
||||
@@ -23,12 +23,15 @@ func Call(function string, args []string) error {
|
||||
if luaFunc.String() == "nil" {
|
||||
return errors.New("function does not exist: " + function)
|
||||
}
|
||||
luaArgs := luar.New(L, args)
|
||||
var luaArgs []lua.LValue
|
||||
for _, v := range args {
|
||||
luaArgs = append(luaArgs, luar.New(L, v))
|
||||
}
|
||||
err := L.CallByParam(lua.P{
|
||||
Fn: luaFunc,
|
||||
NRet: 0,
|
||||
Protect: true,
|
||||
}, luaArgs)
|
||||
}, luaArgs...)
|
||||
return err
|
||||
}
|
||||
|
||||
@@ -57,6 +60,15 @@ func LuaFunctionCommand(function string) func([]string) {
|
||||
}
|
||||
}
|
||||
|
||||
func LuaFunctionJob(function string) func(string, ...string) {
|
||||
return func(output string, args ...string) {
|
||||
err := Call(function, append([]string{output}, args...))
|
||||
if err != nil {
|
||||
TermMessage(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// LoadPlugins loads the pre-installed plugins and the plugins located in ~/.config/micro/plugins
|
||||
func LoadPlugins() {
|
||||
files, _ := ioutil.ReadDir(configDir + "/plugins")
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -34,9 +34,11 @@ end
|
||||
function linter_lint(linter, cmd, errorformat)
|
||||
CurView():ClearGutterMessages(linter)
|
||||
|
||||
local handle = io.popen("(" .. cmd .. ")" .. " 2>&1")
|
||||
local lines = linter_split(handle:read("*a"), "\n")
|
||||
handle:close()
|
||||
JobStart(cmd, "", "", "linter_onExit", linter, errorformat)
|
||||
end
|
||||
|
||||
function linter_onExit(output, linter, errorformat)
|
||||
local lines = linter_split(output, "\n")
|
||||
|
||||
local regex = errorformat:gsub("%%f", "(.+)"):gsub("%%l", "(%d+)"):gsub("%%m", "(.+)")
|
||||
for _,line in ipairs(lines) do
|
||||
|
||||
Reference in New Issue
Block a user