]> git.lizzy.rs Git - micro.git/blob - internal/config/plugin.go
Add support for plugin manager within micro
[micro.git] / internal / config / plugin.go
1 package config
2
3 import (
4         "errors"
5         "log"
6
7         lua "github.com/yuin/gopher-lua"
8         ulua "github.com/zyedidia/micro/internal/lua"
9 )
10
11 // ErrNoSuchFunction is returned when Call is executed on a function that does not exist
12 var ErrNoSuchFunction = errors.New("No such function exists")
13
14 // LoadAllPlugins loads all detected plugins (in runtime/plugins and ConfigDir/plugins)
15 func LoadAllPlugins() error {
16         var reterr error
17         for _, p := range Plugins {
18                 err := p.Load()
19                 if err != nil {
20                         reterr = err
21                 }
22         }
23         return reterr
24 }
25
26 // RunPluginFn runs a given function in all plugins
27 // returns an error if any of the plugins had an error
28 func RunPluginFn(fn string, args ...lua.LValue) error {
29         var reterr error
30         for _, p := range Plugins {
31                 if !p.IsEnabled() {
32                         continue
33                 }
34                 _, err := p.Call(fn, args...)
35                 if err != nil && err != ErrNoSuchFunction {
36                         reterr = errors.New("Plugin " + p.Name + ": " + err.Error())
37                 }
38         }
39         return reterr
40 }
41
42 // RunPluginFnBool runs a function in all plugins and returns
43 // false if any one of them returned false
44 // also returns an error if any of the plugins had an error
45 func RunPluginFnBool(fn string, args ...lua.LValue) (bool, error) {
46         var reterr error
47         retbool := true
48         for _, p := range Plugins {
49                 if !p.IsEnabled() {
50                         continue
51                 }
52                 val, err := p.Call(fn, args...)
53                 if err == ErrNoSuchFunction {
54                         continue
55                 }
56                 if err != nil {
57                         reterr = errors.New("Plugin " + p.Name + ": " + err.Error())
58                         continue
59                 }
60                 if v, ok := val.(lua.LBool); !ok {
61                         reterr = errors.New(p.Name + "." + fn + " should return a boolean")
62                 } else {
63                         retbool = retbool && bool(v)
64                 }
65         }
66         return retbool, reterr
67 }
68
69 // Plugin stores information about the source files/info for a plugin
70 type Plugin struct {
71         DirName string        // name of plugin folder
72         Name    string        // name of plugin
73         Info    *PluginInfo   // json file containing info
74         Srcs    []RuntimeFile // lua files
75         Loaded  bool
76         Default bool // pre-installed plugin
77 }
78
79 // IsEnabled returns if a plugin is enabled
80 func (p *Plugin) IsEnabled() bool {
81         if v, ok := GlobalSettings[p.Name]; ok {
82                 return v.(bool) && p.Loaded
83         }
84         return true
85 }
86
87 // Plugins is a list of all detected plugins (enabled or disabled)
88 var Plugins []*Plugin
89
90 // Load creates an option for the plugin and runs all source files
91 func (p *Plugin) Load() error {
92         if v, ok := GlobalSettings[p.Name]; ok && !v.(bool) {
93                 return nil
94         }
95         for _, f := range p.Srcs {
96                 dat, err := f.Data()
97                 if err != nil {
98                         return err
99                 }
100                 err = ulua.LoadFile(p.Name, f.Name(), dat)
101                 if err != nil {
102                         return err
103                 }
104         }
105         p.Loaded = true
106         RegisterGlobalOption(p.Name, true)
107         return nil
108 }
109
110 // Call calls a given function in this plugin
111 func (p *Plugin) Call(fn string, args ...lua.LValue) (lua.LValue, error) {
112         plug := ulua.L.GetGlobal(p.Name)
113         if plug == lua.LNil {
114                 log.Println("Plugin does not exist:", p.Name, "at", p.DirName, ":", p)
115                 return nil, nil
116         }
117         luafn := ulua.L.GetField(plug, fn)
118         if luafn == lua.LNil {
119                 return nil, ErrNoSuchFunction
120         }
121         err := ulua.L.CallByParam(lua.P{
122                 Fn:      luafn,
123                 NRet:    1,
124                 Protect: true,
125         }, args...)
126         if err != nil {
127                 return nil, err
128         }
129         ret := ulua.L.Get(-1)
130         ulua.L.Pop(1)
131         return ret, nil
132 }
133
134 // FindPlugin returns the plugin with the given name
135 func FindPlugin(name string) *Plugin {
136         var pl *Plugin
137         for _, p := range Plugins {
138                 if !p.IsEnabled() {
139                         continue
140                 }
141                 if p.Name == name {
142                         pl = p
143                         break
144                 }
145         }
146         return pl
147 }
148
149 // FindAnyPlugin does not require the plugin to be enabled
150 func FindAnyPlugin(name string) *Plugin {
151         var pl *Plugin
152         for _, p := range Plugins {
153                 if p.Name == name {
154                         pl = p
155                         break
156                 }
157         }
158         return pl
159 }