9 luar "layeh.com/gopher-luar"
11 lua "github.com/yuin/gopher-lua"
12 "github.com/zyedidia/micro/internal/config"
13 ulua "github.com/zyedidia/micro/internal/lua"
14 "github.com/zyedidia/micro/internal/screen"
17 var Jobs chan JobFunction
20 Jobs = make(chan JobFunction, 100)
23 // Jobs are the way plugins can run processes in the background
24 // A job is simply a process that gets executed asynchronously
25 // There are callbacks for when the job exits, when the job creates stdout
26 // and when the job creates stderr
28 // These jobs run in a separate goroutine but the lua callbacks need to be
29 // executed in the main thread (where the Lua VM is running) so they are
30 // put into the jobs channel which gets read by the main loop
32 // JobFunction is a representation of a job (this data structure is what is loaded
33 // into the jobs channel)
34 type JobFunction struct {
35 Function func(string, ...interface{})
40 // A CallbackFile is the data structure that makes it possible to catch stderr and stdout write events
41 type CallbackFile struct {
44 callback func(string, ...interface{})
48 func (f *CallbackFile) Write(data []byte) (int, error) {
49 // This is either stderr or stdout
50 // In either case we create a new job function callback and put it in the jobs channel
51 jobFunc := JobFunction{f.callback, string(data), f.args}
53 return f.Writer.Write(data)
56 // JobStart starts a shell command in the background with the given callbacks
57 // It returns an *exec.Cmd as the job id
58 func JobStart(cmd string, onStdout, onStderr, onExit string, userargs ...interface{}) *exec.Cmd {
59 return JobSpawn("sh", []string{"-c", cmd}, onStdout, onStderr, onExit, userargs...)
62 // JobSpawn starts a process with args in the background with the given callbacks
63 // It returns an *exec.Cmd as the job id
64 func JobSpawn(cmdName string, cmdArgs []string, onStdout, onStderr, onExit string, userargs ...interface{}) *exec.Cmd {
65 // Set up everything correctly if the functions have been provided
66 proc := exec.Command(cmdName, cmdArgs...)
67 var outbuf bytes.Buffer
69 proc.Stdout = &CallbackFile{&outbuf, luaFunctionJob(onStdout), userargs}
74 proc.Stderr = &CallbackFile{&outbuf, luaFunctionJob(onStderr), userargs}
80 // Run the process in the background and create the onExit callback
82 jobFunc := JobFunction{luaFunctionJob(onExit), string(outbuf.Bytes()), userargs}
89 // JobStop kills a job
90 func JobStop(cmd *exec.Cmd) {
94 // JobSend sends the given data into the job's stdin stream
95 func JobSend(cmd *exec.Cmd, data string) {
96 stdin, err := cmd.StdinPipe()
101 stdin.Write([]byte(data))
104 // luaFunctionJob returns a function that will call the given lua function
105 // structured as a job call i.e. the job output and arguments are provided
106 // to the lua function
107 func luaFunctionJob(fn string) func(string, ...interface{}) {
108 luaFn := strings.Split(fn, ".")
112 plName, plFn := luaFn[0], luaFn[1]
113 pl := config.FindPlugin(plName)
117 return func(output string, args ...interface{}) {
118 var luaArgs []lua.LValue
119 luaArgs = append(luaArgs, luar.New(ulua.L, output))
120 for _, v := range args {
121 luaArgs = append(luaArgs, luar.New(ulua.L, v))
123 _, err := pl.Call(plFn, luaArgs...)
124 if err != nil && err != config.ErrNoSuchFunction {
125 screen.TermMessage(err)