]> git.lizzy.rs Git - micro.git/blobdiff - cmd/micro/plugin.go
refactor(plugin): Enable human-friendly plugin name
[micro.git] / cmd / micro / plugin.go
index d60f9510db5b0e10a6cadf5f5fa74a52cddc1b76..4b42f5bdd7a620a4c6563d724eecd100bccb4f3c 100644 (file)
@@ -3,28 +3,24 @@ package main
 import (
        "errors"
        "io/ioutil"
+       "os"
        "strings"
 
        "github.com/layeh/gopher-luar"
        "github.com/yuin/gopher-lua"
 )
 
-var loadedPlugins []string
-
-var preInstalledPlugins = []string{
-       "go",
-       "linter",
-}
+var loadedPlugins map[string]string
 
 // Call calls the lua function 'function'
 // If it does not exist nothing happens, if there is an error,
 // the error is returned
-func Call(function string, args []string) error {
+func Call(function string, args ...interface{}) (lua.LValue, error) {
        var luaFunc lua.LValue
        if strings.Contains(function, ".") {
                plugin := L.GetGlobal(strings.Split(function, ".")[0])
                if plugin.String() == "nil" {
-                       return errors.New("function does not exist: " + function)
+                       return nil, errors.New("function does not exist: " + function)
                }
                luaFunc = L.GetField(plugin, strings.Split(function, ".")[1])
        } else {
@@ -32,7 +28,7 @@ func Call(function string, args []string) error {
        }
 
        if luaFunc.String() == "nil" {
-               return errors.New("function does not exist: " + function)
+               return nil, errors.New("function does not exist: " + function)
        }
        var luaArgs []lua.LValue
        for _, v := range args {
@@ -40,19 +36,23 @@ func Call(function string, args []string) error {
        }
        err := L.CallByParam(lua.P{
                Fn:      luaFunc,
-               NRet:    0,
+               NRet:    1,
                Protect: true,
        }, luaArgs...)
-       return err
+       ret := L.Get(-1) // returned value
+       if ret.String() != "nil" {
+               L.Pop(1) // remove received value
+       }
+       return ret, err
 }
 
 // LuaFunctionBinding is a function generator which takes the name of a lua function
 // and creates a function that will call that lua function
 // Specifically it creates a function that can be called as a binding because this is used
 // to bind keys to lua functions
-func LuaFunctionBinding(function string) func(*View) bool {
-       return func(v *View) bool {
-               err := Call(function, nil)
+func LuaFunctionBinding(function string) func(*View, bool) bool {
+       return func(v *View, _ bool) bool {
+               _, err := Call(function, nil)
                if err != nil {
                        TermMessage(err)
                }
@@ -60,60 +60,99 @@ func LuaFunctionBinding(function string) func(*View) bool {
        }
 }
 
+func unpack(old []string) []interface{} {
+       new := make([]interface{}, len(old))
+       for i, v := range old {
+               new[i] = v
+       }
+       return new
+}
+
 // LuaFunctionCommand is the same as LuaFunctionBinding except it returns a normal function
 // so that a command can be bound to a lua function
 func LuaFunctionCommand(function string) func([]string) {
        return func(args []string) {
-               err := Call(function, args)
+               _, err := Call(function, unpack(args)...)
                if err != nil {
                        TermMessage(err)
                }
        }
 }
 
+// LuaFunctionComplete returns a function which can be used for autocomplete in plugins
+func LuaFunctionComplete(function string) func(string) []string {
+       return func(input string) (result []string) {
+
+               res, err := Call(function, input)
+               if err != nil {
+                       TermMessage(err)
+               }
+               if tbl, ok := res.(*lua.LTable); !ok {
+                       TermMessage(function, "should return a table of strings")
+               } else {
+                       for i := 1; i <= tbl.Len(); i++ {
+                               val := tbl.RawGetInt(i)
+                               if v, ok := val.(lua.LString); !ok {
+                                       TermMessage(function, "should return a table of strings")
+                               } else {
+                                       result = append(result, string(v))
+                               }
+                       }
+               }
+               return result
+       }
+}
+
 func LuaFunctionJob(function string) func(string, ...string) {
        return func(output string, args ...string) {
-               err := Call(function, append([]string{output}, args...))
+               _, err := Call(function, unpack(append([]string{output}, args...))...)
                if err != nil {
                        TermMessage(err)
                }
        }
 }
 
+// luaPluginName convert a human-friendly plugin name into a valid lua variable name.
+func luaPluginName(name string) string {
+       return strings.Replace(name, "-", "_", -1)
+}
+
 // LoadPlugins loads the pre-installed plugins and the plugins located in ~/.config/micro/plugins
 func LoadPlugins() {
-       files, _ := ioutil.ReadDir(configDir + "/plugins")
-       for _, plugin := range files {
-               if plugin.IsDir() {
-                       pluginName := plugin.Name()
-                       files, _ := ioutil.ReadDir(configDir + "/plugins/" + pluginName)
-                       for _, f := range files {
-                               if f.Name() == pluginName+".lua" {
-                                       data, _ := ioutil.ReadFile(configDir + "/plugins/" + pluginName + "/" + f.Name())
-                                       pluginDef := "\nlocal P = {}\n" + pluginName + " = P\nsetmetatable(" + pluginName + ", {__index = _G})\nsetfenv(1, P)\n"
-
-                                       if err := L.DoString(pluginDef + string(data)); err != nil {
-                                               TermMessage(err)
-                                               continue
-                                       }
-                                       loadedPlugins = append(loadedPlugins, pluginName)
-                               }
-                       }
+
+       loadedPlugins = make(map[string]string)
+
+       for _, plugin := range ListRuntimeFiles(RTPlugin) {
+
+               pluginName := plugin.Name()
+               if _, ok := loadedPlugins[pluginName]; ok {
+                       continue
                }
-       }
 
-       for _, pluginName := range preInstalledPlugins {
-               plugin := "runtime/plugins/" + pluginName + "/" + pluginName + ".lua"
-               data, err := Asset(plugin)
+               data, err := plugin.Data()
                if err != nil {
-                       TermMessage("Error loading pre-installed plugin: " + pluginName)
+                       TermMessage("Error loading plugin: " + pluginName)
                        continue
                }
-               pluginDef := "\nlocal P = {}\n" + pluginName + " = P\nsetmetatable(" + pluginName + ", {__index = _G})\nsetfenv(1, P)\n"
+
+               pluginLuaName := luaPluginName(pluginName)
+               pluginDef := "\nlocal P = {}\n" + pluginLuaName + " = P\nsetmetatable(" + pluginLuaName + ", {__index = _G})\nsetfenv(1, P)\n"
+
                if err := L.DoString(pluginDef + string(data)); err != nil {
                        TermMessage(err)
                        continue
                }
-               loadedPlugins = append(loadedPlugins, pluginName)
+
+               loadedPlugins[pluginName] = pluginLuaName
+
+       }
+
+       if _, err := os.Stat(configDir + "/init.lua"); err == nil {
+               pluginDef := "\nlocal P = {}\n" + "init" + " = P\nsetmetatable(" + "init" + ", {__index = _G})\nsetfenv(1, P)\n"
+               data, _ := ioutil.ReadFile(configDir + "/init.lua")
+               if err := L.DoString(pluginDef + string(data)); err != nil {
+                       TermMessage(err)
+               }
+               loadedPlugins["init"] = "init"
        }
 }