X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;f=cmd%2Fmicro%2Fplugin.go;h=b335078070ba02a4bb7471efe5b276c9d0061f16;hb=5b26702d5e7df5397e10b0e158d99afb4e7e946d;hp=6401970b20ab6193012ebe7036e190e2ffdf1d36;hpb=68189fd4066ea7b41455535c2b0810b09bc3b479;p=micro.git diff --git a/cmd/micro/plugin.go b/cmd/micro/plugin.go index 6401970b..b3350780 100644 --- a/cmd/micro/plugin.go +++ b/cmd/micro/plugin.go @@ -3,40 +3,71 @@ package main import ( "errors" "io/ioutil" + "os" + "strings" "github.com/yuin/gopher-lua" + "github.com/zyedidia/tcell" + "layeh.com/gopher-luar" ) -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) error { - luaFunc := L.GetGlobal(function) +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 nil, errors.New("function does not exist: " + function) + } + luaFunc = L.GetField(plugin, strings.Split(function, ".")[1]) + } else { + luaFunc = L.GetGlobal(function) + } + 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 { + luaArgs = append(luaArgs, luar.New(L, v)) } err := L.CallByParam(lua.P{ Fn: luaFunc, - NRet: 0, + NRet: 1, Protect: true, - }) - return err + }, luaArgs...) + 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) +func LuaFunctionBinding(function string) func(*View, bool) bool { + return func(v *View, _ bool) bool { + _, err := Call(function, nil) + if err != nil { + TermMessage(err) + } + return false + } +} + +// LuaFunctionMouseBinding 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 mouse binding because this is used +// to bind mouse actions to lua functions +func LuaFunctionMouseBinding(function string) func(*View, bool, *tcell.EventMouse) bool { + return func(v *View, _ bool, e *tcell.EventMouse) bool { + _, err := Call(function, e) if err != nil { TermMessage(err) } @@ -44,47 +75,110 @@ 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() { - return func() { - err := Call(function) +func LuaFunctionCommand(function string) func([]string) { + return func(args []string) { + _, err := Call(function, unpack(args)...) if err != nil { TermMessage(err) } } } -// 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" { - if err := L.DoFile(configDir + "/plugins/" + pluginName + "/" + f.Name()); err != nil { - TermMessage(err) - continue - } - loadedPlugins = append(loadedPlugins, pluginName) +// 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 + } +} + +// LuaFunctionJob returns a function that will call the given lua function +// structured as a job call i.e. the job output and arguments are provided +// to the lua function +func LuaFunctionJob(function string) func(string, ...string) { + return func(output string, args ...string) { + _, err := Call(function, unpack(append([]string{output}, args...))...) + if err != nil && !strings.HasPrefix(err.Error(), "function does not exist") { + TermMessage(err) + } } +} - for _, pluginName := range preInstalledPlugins { - plugin := "runtime/plugins/" + pluginName + "/" + pluginName + ".lua" - data, err := Asset(plugin) +// 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() { + loadedPlugins = make(map[string]string) + + for _, plugin := range ListRuntimeFiles(RTPlugin) { + pluginName := plugin.Name() + if _, ok := loadedPlugins[pluginName]; ok { + continue + } + + data, err := plugin.Data() if err != nil { - TermMessage("Error loading pre-installed plugin: " + pluginName) + TermMessage("Error loading plugin: " + pluginName) + continue + } + + pluginLuaName := luaPluginName(pluginName) + + if err := LoadFile(pluginLuaName, pluginLuaName, string(data)); err != nil { + TermMessage(err) continue } - if err := L.DoString(string(data)); err != nil { + + loadedPlugins[pluginName] = pluginLuaName + + } + + if _, err := os.Stat(configDir + "/init.lua"); err == nil { + data, _ := ioutil.ReadFile(configDir + "/init.lua") + if err := LoadFile("init", configDir+"init.lua", string(data)); err != nil { + TermMessage(err) + } + loadedPlugins["init"] = "init" + } +} + +// GlobalCall makes a call to a function in every plugin that is currently +// loaded +func GlobalPluginCall(function string, args ...interface{}) { + for pl := range loadedPlugins { + _, err := Call(pl+"."+function, args...) + if err != nil && !strings.HasPrefix(err.Error(), "function does not exist") { TermMessage(err) continue } - loadedPlugins = append(loadedPlugins, pluginName) } }