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