Add plugin info.json support

This commit is contained in:
Zachary Yedidia 2019-08-06 22:24:03 -07:00
parent 26c545267d
commit adaddba696
11 changed files with 381 additions and 9 deletions

View file

@ -1,6 +1,7 @@
package action
import (
"bytes"
"errors"
"fmt"
"os"
@ -52,7 +53,7 @@ func InitCommands() {
"help": Command{(*BufPane).HelpCmd, HelpComplete},
"eval": Command{(*BufPane).EvalCmd, nil},
"log": Command{(*BufPane).ToggleLogCmd, nil},
"plugin": Command{(*BufPane).PluginCmd, nil},
"plugin": Command{(*BufPane).PluginCmd, PluginComplete},
"reload": Command{(*BufPane).ReloadCmd, nil},
"reopen": Command{(*BufPane).ReopenCmd, nil},
"cd": Command{(*BufPane).CdCmd, buffer.FileComplete},
@ -115,6 +116,8 @@ func CommandAction(cmd string) BufKeyAction {
}
}
var PluginCmds = []string{"list", "info", "version"}
// PluginCmd installs, removes, updates, lists, or searches for given plugins
func (h *BufPane) PluginCmd(args []string) {
if len(args) <= 0 {
@ -132,10 +135,82 @@ func (h *BufPane) PluginCmd(args []string) {
} else {
en = "disabled"
}
WriteLog(fmt.Sprintf("%s: %s\n", pl.Name, en))
WriteLog(fmt.Sprintf("%s: %s", pl.Name, en))
if pl.Default {
WriteLog(" (default)\n")
} else {
WriteLog("\n")
}
}
WriteLog("Default plugins come pre-installed with micro.")
case "version":
if len(args) <= 1 {
InfoBar.Error("No plugin provided to give info for")
return
}
found := false
for _, pl := range config.Plugins {
if pl.Name == args[1] {
found = true
if pl.Info == nil {
InfoBar.Message("Sorry no version for", pl.Name)
return
}
WriteLog("Version: " + pl.Info.Vstr + "\n")
}
}
if !found {
InfoBar.Message(args[1], "is not installed")
}
case "info":
if len(args) <= 1 {
InfoBar.Error("No plugin provided to give info for")
return
}
found := false
for _, pl := range config.Plugins {
if pl.Name == args[1] {
found = true
if pl.Info == nil {
InfoBar.Message("Sorry no info for ", pl.Name)
return
}
var buffer bytes.Buffer
buffer.WriteString("Name: ")
buffer.WriteString(pl.Info.Name)
buffer.WriteString("\n")
buffer.WriteString("Description: ")
buffer.WriteString(pl.Info.Desc)
buffer.WriteString("\n")
buffer.WriteString("Website: ")
buffer.WriteString(pl.Info.Site)
buffer.WriteString("\n")
buffer.WriteString("Installation link: ")
buffer.WriteString(pl.Info.Install)
buffer.WriteString("\n")
buffer.WriteString("Version: ")
buffer.WriteString(pl.Info.Vstr)
buffer.WriteString("\n")
buffer.WriteString("Requirements:")
buffer.WriteString("\n")
for _, r := range pl.Info.Require {
buffer.WriteString(" - ")
buffer.WriteString(r)
buffer.WriteString("\n")
}
WriteLog(buffer.String())
}
}
if !found {
InfoBar.Message(args[1], "is not installed")
return
}
default:
valid = false
InfoBar.Error("Not a valid plugin command")
return
}
if valid && h.Buf.Type != buffer.BTLog {

View file

@ -197,6 +197,62 @@ func OptionValueComplete(b *buffer.Buffer) ([]string, []string) {
return completions, suggestions
}
// OptionComplete autocompletes options
func PluginCmdComplete(b *buffer.Buffer) ([]string, []string) {
c := b.GetActiveCursor()
input, argstart := buffer.GetArg(b)
var suggestions []string
for _, cmd := range PluginCmds {
if strings.HasPrefix(cmd, input) {
suggestions = append(suggestions, cmd)
}
}
sort.Strings(suggestions)
completions := make([]string, len(suggestions))
for i := range suggestions {
completions[i] = util.SliceEndStr(suggestions[i], c.X-argstart)
}
return completions, suggestions
}
// PluginComplete completes values for the plugin command
func PluginComplete(b *buffer.Buffer) ([]string, []string) {
c := b.GetActiveCursor()
l := b.LineBytes(c.Y)
l = util.SliceStart(l, c.X)
input, argstart := buffer.GetArg(b)
completeValue := false
args := bytes.Split(l, []byte{' '})
if len(args) >= 2 {
for _, cmd := range PluginCmds {
if cmd == string(args[len(args)-2]) {
completeValue = true
break
}
}
}
if !completeValue {
return PluginCmdComplete(b)
}
var suggestions []string
for _, pl := range config.Plugins {
if strings.HasPrefix(pl.Name, input) {
suggestions = append(suggestions, pl.Name)
}
}
sort.Strings(suggestions)
completions := make([]string, len(suggestions))
for i := range suggestions {
completions[i] = util.SliceEndStr(suggestions[i], c.X-argstart)
}
return completions, suggestions
}
// // MakeCompletion registers a function from a plugin for autocomplete commands
// func MakeCompletion(function string) Completion {
// pluginCompletions = append(pluginCompletions, LuaFunctionComplete(function))

View file

@ -66,9 +66,10 @@ func RunPluginFnBool(fn string, args ...lua.LValue) (bool, error) {
type Plugin struct {
Name string // name of plugin
Info RuntimeFile // json file containing info
Info *PluginInfo // json file containing info
Srcs []RuntimeFile // lua files
Loaded bool
Default bool // pre-installed plugin
}
func (p *Plugin) IsEnabled() bool {

View file

@ -0,0 +1,66 @@
package config
import (
"bytes"
"encoding/json"
"errors"
)
var (
ErrMissingName = errors.New("Missing or empty name field")
ErrMissingDesc = errors.New("Missing or empty description field")
ErrMissingSite = errors.New("Missing or empty website field")
ErrMissingInstall = errors.New("Missing or empty install field")
ErrMissingVstr = errors.New("Missing or empty versions field")
ErrMissingRequire = errors.New("Missing or empty require field")
)
// PluginInfo contains all the needed info about a plugin
// The info is just strings and are not used beyond that (except
// the Site and Install fields should be valid URLs). This means
// that the requirements for example can be formatted however the
// plugin maker decides, the fields will only be parsed by humans
// Name: name of plugin
// Desc: description of plugin
// Site: home website of plugin
// Install: install link for plugin (can be link to repo or zip file)
// Vstr: version
// Require: list of dependencies and requirements
type PluginInfo struct {
Name string `json:"name"`
Desc string `json:"description"`
Site string `json:"website"`
Install string `json:"install"`
Vstr string `json:"version"`
Require []string `json:"require"`
}
// NewPluginInfo parses a JSON input into a valid PluginInfo struct
// Returns an error if there are any missing fields or any invalid fields
// There are no optional fields in a plugin info json file
func NewPluginInfo(data []byte) (*PluginInfo, error) {
var info PluginInfo
dec := json.NewDecoder(bytes.NewReader(data))
// dec.DisallowUnknownFields() // Force errors
if err := dec.Decode(&info); err != nil {
return nil, err
}
// if len(info.Name) == 0 {
// return nil, ErrMissingName
// } else if len(info.Desc) == 0 {
// return nil, ErrMissingDesc
// } else if len(info.Site) == 0 {
// return nil, ErrMissingSite
// } else if len(info.Install) == 0 {
// return nil, ErrMissingInstall
// } else if len(info.Vstr) == 0 {
// return nil, ErrMissingVstr
// } else if len(info.Require) == 0 {
// return nil, ErrMissingRequire
// }
return &info, nil
}

View file

@ -154,7 +154,11 @@ func InitRuntimeFiles() {
if strings.HasSuffix(f.Name(), ".lua") {
p.Srcs = append(p.Srcs, realFile(filepath.Join(plugdir, d.Name(), f.Name())))
} else if f.Name() == "info.json" {
p.Info = realFile(filepath.Join(plugdir, d.Name(), "info.json"))
data, err := ioutil.ReadFile(filepath.Join(plugdir, d.Name(), "info.json"))
if err != nil {
continue
}
p.Info, _ = NewPluginInfo(data)
}
}
Plugins = append(Plugins, p)
@ -167,11 +171,16 @@ func InitRuntimeFiles() {
if srcs, err := AssetDir(filepath.Join(plugdir, d)); err == nil {
p := new(Plugin)
p.Name = d
p.Default = true
for _, f := range srcs {
if strings.HasSuffix(f, ".lua") {
p.Srcs = append(p.Srcs, assetFile(filepath.Join(plugdir, d, f)))
} else if f == "info.json" {
p.Info = assetFile(filepath.Join(plugdir, d, "info.json"))
data, err := Asset(filepath.Join(plugdir, d, "info.json"))
if err != nil {
continue
}
p.Info, _ = NewPluginInfo(data)
}
}
Plugins = append(Plugins, p)

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,10 @@
{
"name": "linter",
"description": "Automatically places closing characters for quotes, parentheses, brackets, etc...",
"website": "https://github.com/zyedidia/micro",
"install": "https://github.com/zyedidia/micro",
"version": "1.0.0",
"require": [
"micro >= 2.0.0"
]
}

View file

@ -0,0 +1,10 @@
{
"name": "comment",
"description": "Support for automatically commenting blocks of code. Extensible and multiple languages supported.",
"website": "https://github.com/zyedidia/micro",
"install": "https://github.com/zyedidia/micro",
"version": "1.0.0",
"require": [
"micro >= 2.0.0"
]
}

View file

@ -0,0 +1,10 @@
{
"name": "ftoptions",
"description": "Sets basic options based on the filetype (for example Makefiles require tabs).",
"website": "https://github.com/zyedidia/micro",
"install": "https://github.com/zyedidia/micro",
"version": "1.0.0",
"require": [
"micro >= 2.0.0"
]
}

View file

@ -0,0 +1,10 @@
{
"name": "linter",
"description": "Automatic code linting for a variety of languages.",
"website": "https://github.com/zyedidia/micro",
"install": "https://github.com/zyedidia/micro",
"version": "1.0.0",
"require": [
"micro >= 2.0.0"
]
}

View file

@ -0,0 +1,10 @@
{
"name": "linter",
"description": "Highlighting and language support for the Literate programming tool.",
"website": "https://github.com/zyedidia/Literate",
"install": "https://github.com/zyedidia/micro",
"version": "1.0.0",
"require": [
"micro >= 2.0.0"
]
}