]> git.lizzy.rs Git - micro.git/blob - cmd/micro/plugin.go
Merge pull request #314 from mame98/master
[micro.git] / cmd / micro / plugin.go
1 package main
2
3 import (
4         "errors"
5         "io/ioutil"
6         "os"
7         "strings"
8
9         "github.com/layeh/gopher-luar"
10         "github.com/yuin/gopher-lua"
11 )
12
13 var loadedPlugins []string
14
15 var preInstalledPlugins = []string{
16         "go",
17         "linter",
18         "autoclose",
19 }
20
21 // Call calls the lua function 'function'
22 // If it does not exist nothing happens, if there is an error,
23 // the error is returned
24 func Call(function string, args ...interface{}) (lua.LValue, error) {
25         var luaFunc lua.LValue
26         if strings.Contains(function, ".") {
27                 plugin := L.GetGlobal(strings.Split(function, ".")[0])
28                 if plugin.String() == "nil" {
29                         return nil, errors.New("function does not exist: " + function)
30                 }
31                 luaFunc = L.GetField(plugin, strings.Split(function, ".")[1])
32         } else {
33                 luaFunc = L.GetGlobal(function)
34         }
35
36         if luaFunc.String() == "nil" {
37                 return nil, errors.New("function does not exist: " + function)
38         }
39         var luaArgs []lua.LValue
40         for _, v := range args {
41                 luaArgs = append(luaArgs, luar.New(L, v))
42         }
43         err := L.CallByParam(lua.P{
44                 Fn:      luaFunc,
45                 NRet:    1,
46                 Protect: true,
47         }, luaArgs...)
48         ret := L.Get(-1) // returned value
49         if ret.String() != "nil" {
50                 L.Pop(1) // remove received value
51         }
52         return ret, err
53 }
54
55 // LuaFunctionBinding is a function generator which takes the name of a lua function
56 // and creates a function that will call that lua function
57 // Specifically it creates a function that can be called as a binding because this is used
58 // to bind keys to lua functions
59 func LuaFunctionBinding(function string) func(*View, bool) bool {
60         return func(v *View, _ bool) bool {
61                 _, err := Call(function, nil)
62                 if err != nil {
63                         TermMessage(err)
64                 }
65                 return false
66         }
67 }
68
69 func unpack(old []string) []interface{} {
70         new := make([]interface{}, len(old))
71         for i, v := range old {
72                 new[i] = v
73         }
74         return new
75 }
76
77 // LuaFunctionCommand is the same as LuaFunctionBinding except it returns a normal function
78 // so that a command can be bound to a lua function
79 func LuaFunctionCommand(function string) func([]string) {
80         return func(args []string) {
81                 _, err := Call(function, unpack(args)...)
82                 if err != nil {
83                         TermMessage(err)
84                 }
85         }
86 }
87
88 // LuaFunctionComplete returns a function which can be used for autocomplete in plugins
89 func LuaFunctionComplete(function string) func(string) []string {
90         return func(input string) (result []string) {
91
92                 res, err := Call(function, input)
93                 if err != nil {
94                         TermMessage(err)
95                 }
96                 if tbl, ok := res.(*lua.LTable); !ok {
97                         TermMessage(function, "should return a table of strings")
98                 } else {
99                         for i := 1; i <= tbl.Len(); i++ {
100                                 val := tbl.RawGetInt(i)
101                                 if v, ok := val.(lua.LString); !ok {
102                                         TermMessage(function, "should return a table of strings")
103                                 } else {
104                                         result = append(result, string(v))
105                                 }
106                         }
107                 }
108                 return result
109         }
110 }
111
112 func LuaFunctionJob(function string) func(string, ...string) {
113         return func(output string, args ...string) {
114                 _, err := Call(function, unpack(append([]string{output}, args...))...)
115                 if err != nil {
116                         TermMessage(err)
117                 }
118         }
119 }
120
121 // LoadPlugins loads the pre-installed plugins and the plugins located in ~/.config/micro/plugins
122 func LoadPlugins() {
123         files, _ := ioutil.ReadDir(configDir + "/plugins")
124         for _, plugin := range files {
125                 if plugin.IsDir() {
126                         pluginName := plugin.Name()
127                         files, _ := ioutil.ReadDir(configDir + "/plugins/" + pluginName)
128                         for _, f := range files {
129                                 if f.Name() == pluginName+".lua" {
130                                         data, _ := ioutil.ReadFile(configDir + "/plugins/" + pluginName + "/" + f.Name())
131                                         pluginDef := "\nlocal P = {}\n" + pluginName + " = P\nsetmetatable(" + pluginName + ", {__index = _G})\nsetfenv(1, P)\n"
132
133                                         if err := L.DoString(pluginDef + string(data)); err != nil {
134                                                 TermMessage(err)
135                                                 continue
136                                         }
137                                         loadedPlugins = append(loadedPlugins, pluginName)
138                                 }
139                         }
140                 }
141         }
142
143         for _, pluginName := range preInstalledPlugins {
144                 alreadyExists := false
145                 for _, pl := range loadedPlugins {
146                         if pl == pluginName {
147                                 alreadyExists = true
148                                 break
149                         }
150                 }
151                 if !alreadyExists {
152                         plugin := "runtime/plugins/" + pluginName + "/" + pluginName + ".lua"
153                         data, err := Asset(plugin)
154                         if err != nil {
155                                 TermMessage("Error loading pre-installed plugin: " + pluginName)
156                                 continue
157                         }
158                         pluginDef := "\nlocal P = {}\n" + pluginName + " = P\nsetmetatable(" + pluginName + ", {__index = _G})\nsetfenv(1, P)\n"
159                         if err := L.DoString(pluginDef + string(data)); err != nil {
160                                 TermMessage(err)
161                                 continue
162                         }
163
164                         loadedPlugins = append(loadedPlugins, pluginName)
165                 }
166         }
167
168         if _, err := os.Stat(configDir + "/init.lua"); err == nil {
169                 pluginDef := "\nlocal P = {}\n" + "init" + " = P\nsetmetatable(" + "init" + ", {__index = _G})\nsetfenv(1, P)\n"
170                 data, _ := ioutil.ReadFile(configDir + "/init.lua")
171                 if err := L.DoString(pluginDef + string(data)); err != nil {
172                         TermMessage(err)
173                 }
174                 loadedPlugins = append(loadedPlugins, "init")
175         }
176 }