Add support for plugin manager within micro

This commit is contained in:
Zachary Yedidia
2020-02-02 14:20:39 -05:00
parent 09ea82c97e
commit b0624cb66e
6 changed files with 190 additions and 129 deletions

View File

@@ -55,6 +55,12 @@ func InitFlags() {
fmt.Println(" \tRemove plugin(s)") fmt.Println(" \tRemove plugin(s)")
fmt.Println("-plugin update [PLUGIN]...") fmt.Println("-plugin update [PLUGIN]...")
fmt.Println(" \tUpdate plugin(s) (if no argument is given, updates all plugins)") fmt.Println(" \tUpdate plugin(s) (if no argument is given, updates all plugins)")
fmt.Println("-plugin search [PLUGIN]...")
fmt.Println(" \tSearch for a plugin")
fmt.Println("-plugin list")
fmt.Println(" \tList installed plugins")
fmt.Println("-plugin available")
fmt.Println(" \tList available plugins")
fmt.Print("\nMicro's options can also be set via command line arguments for quick\nadjustments. For real configuration, please use the settings.json\nfile (see 'help options').\n\n") fmt.Print("\nMicro's options can also be set via command line arguments for quick\nadjustments. For real configuration, please use the settings.json\nfile (see 'help options').\n\n")
fmt.Println("-option value") fmt.Println("-option value")
@@ -107,76 +113,8 @@ func DoPluginFlags() {
args := flag.Args() args := flag.Args()
switch *flagPlugin { config.PluginCommand(os.Stdout, *flagPlugin, args)
case "install":
installedVersions := config.GetInstalledVersions(false)
for _, plugin := range args {
pp := config.GetAllPluginPackages().Get(plugin)
if pp == nil {
fmt.Println("Unknown plugin \"" + plugin + "\"")
} else if err := pp.IsInstallable(); err != nil {
fmt.Println("Error installing ", plugin, ": ", err)
} else {
for _, installed := range installedVersions {
if pp.Name == installed.Pack().Name {
if pp.Versions[0].Version.Compare(installed.Version) == 1 {
fmt.Println(pp.Name, " is already installed but out-of-date: use 'plugin update ", pp.Name, "' to update")
} else {
fmt.Println(pp.Name, " is already installed")
}
}
}
pp.Install()
}
}
case "remove":
removed := ""
for _, plugin := range args {
// check if the plugin exists.
for _, p := range config.Plugins {
if p.Name == plugin && p.Default {
fmt.Println("Default plugins cannot be removed, but can be disabled via settings.")
continue
}
if p.Name == plugin {
config.UninstallPlugin(plugin)
removed += plugin + " "
continue
}
}
}
if removed != "" {
fmt.Println("Removed ", removed)
} else {
fmt.Println("No plugins removed")
}
case "update":
config.UpdatePlugins(args)
case "list":
plugins := config.GetInstalledVersions(false)
fmt.Println("The following plugins are currently installed:")
for _, p := range plugins {
fmt.Printf("%s (%s)\n", p.Pack().Name, p.Version)
}
case "search":
plugins := config.SearchPlugin(args)
fmt.Println(len(plugins), " plugins found")
for _, p := range plugins {
fmt.Println("----------------")
fmt.Println(p.String())
}
fmt.Println("----------------")
case "available":
packages := config.GetAllPluginPackages()
fmt.Println("Available Plugins:")
for _, pkg := range packages {
fmt.Println(pkg.Name)
}
default:
fmt.Println("Invalid plugin command")
os.Exit(1)
}
os.Exit(0) os.Exit(0)
} }
} }
@@ -290,15 +228,15 @@ func main() {
} }
}() }()
action.InitBindings() err = config.LoadAllPlugins()
action.InitCommands()
err = config.InitColorscheme()
if err != nil { if err != nil {
screen.TermMessage(err) screen.TermMessage(err)
} }
err = config.LoadAllPlugins() action.InitBindings()
action.InitCommands()
err = config.InitColorscheme()
if err != nil { if err != nil {
screen.TermMessage(err) screen.TermMessage(err)
} }

View File

@@ -120,11 +120,20 @@ func CommandAction(cmd string) BufKeyAction {
} }
} }
var PluginCmds = []string{"list", "info", "version"} var PluginCmds = []string{"install", "remove", "update", "available", "list", "search"}
// PluginCmd installs, removes, updates, lists, or searches for given plugins // PluginCmd installs, removes, updates, lists, or searches for given plugins
func (h *BufPane) PluginCmd(args []string) { func (h *BufPane) PluginCmd(args []string) {
InfoBar.Error("Plugin command disabled") if len(args) < 1 {
InfoBar.Error("Not enough arguments")
return
}
if h.Buf.Type != buffer.BTLog {
OpenLogBuf(h)
}
config.PluginCommand(buffer.LogBuf, args[0], args[1:])
} }
// RetabCmd changes all spaces to tabs or all tabs to spaces // RetabCmd changes all spaces to tabs or all tabs to spaces

View File

@@ -76,16 +76,24 @@ type SharedBuffer struct {
// Whether or not suggestions can be autocompleted must be shared because // Whether or not suggestions can be autocompleted must be shared because
// it changes based on how the buffer has changed // it changes based on how the buffer has changed
HasSuggestions bool HasSuggestions bool
// Modifications is the list of modified regions for syntax highlighting
Modifications []Loc
} }
func (b *SharedBuffer) insert(pos Loc, value []byte) { func (b *SharedBuffer) insert(pos Loc, value []byte) {
b.isModified = true b.isModified = true
b.HasSuggestions = false b.HasSuggestions = false
b.LineArray.insert(pos, value) b.LineArray.insert(pos, value)
// b.Modifications is cleared every screen redraw so it's
// ok to append duplicates
b.Modifications = append(b.Modifications, Loc{pos.Y, pos.Y + bytes.Count(value, []byte{'\n'})})
} }
func (b *SharedBuffer) remove(start, end Loc) []byte { func (b *SharedBuffer) remove(start, end Loc) []byte {
b.isModified = true b.isModified = true
b.HasSuggestions = false b.HasSuggestions = false
b.Modifications = append(b.Modifications, Loc{start.Y, start.Y})
return b.LineArray.remove(start, end) return b.LineArray.remove(start, end)
} }
@@ -115,9 +123,7 @@ type Buffer struct {
// This stores the highlighting rules and filetype detection info // This stores the highlighting rules and filetype detection info
SyntaxDef *highlight.Def SyntaxDef *highlight.Def
// The Highlighter struct actually performs the highlighting // The Highlighter struct actually performs the highlighting
Highlighter *highlight.Highlighter Highlighter *highlight.Highlighter
// Modifications is the list of modified regions for syntax highlighting
Modifications []Loc
HighlightLock sync.Mutex HighlightLock sync.Mutex
// Hash of the original buffer -- empty if fastdirty is on // Hash of the original buffer -- empty if fastdirty is on
@@ -333,10 +339,6 @@ func (b *Buffer) Insert(start Loc, text string) {
b.EventHandler.active = b.curCursor b.EventHandler.active = b.curCursor
b.EventHandler.Insert(start, text) b.EventHandler.Insert(start, text)
// b.Modifications is cleared every screen redraw so it's
// ok to append duplicates
b.Modifications = append(b.Modifications, Loc{start.Y, start.Y + strings.Count(text, "\n")})
go b.Backup(true) go b.Backup(true)
} }
} }
@@ -348,8 +350,6 @@ func (b *Buffer) Remove(start, end Loc) {
b.EventHandler.active = b.curCursor b.EventHandler.active = b.curCursor
b.EventHandler.Remove(start, end) b.EventHandler.Remove(start, end)
b.Modifications = append(b.Modifications, Loc{start.Y, start.Y})
go b.Backup(true) go b.Backup(true)
} }
} }
@@ -907,12 +907,12 @@ func ParseCursorLocation(cursorPositions []string) (Loc, error) {
} }
startpos.Y, err = strconv.Atoi(cursorPositions[0]) startpos.Y, err = strconv.Atoi(cursorPositions[0])
startpos.Y -= 1 startpos.Y--
if err == nil { if err == nil {
if len(cursorPositions) > 1 { if len(cursorPositions) > 1 {
startpos.X, err = strconv.Atoi(cursorPositions[1]) startpos.X, err = strconv.Atoi(cursorPositions[1])
if startpos.X > 0 { if startpos.X > 0 {
startpos.X -= 1 startpos.X--
} }
} }
} }
@@ -925,6 +925,11 @@ func (b *Buffer) Line(i int) string {
return string(b.LineBytes(i)) return string(b.LineBytes(i))
} }
func (b *Buffer) Write(bytes []byte) (n int, err error) {
b.EventHandler.InsertBytes(b.End(), bytes)
return len(bytes), nil
}
// WriteLog writes a string to the log buffer // WriteLog writes a string to the log buffer
func WriteLog(s string) { func WriteLog(s string) {
LogBuf.EventHandler.Insert(LogBuf.End(), s) LogBuf.EventHandler.Insert(LogBuf.End(), s)

View File

@@ -111,6 +111,11 @@ func (eh *EventHandler) ApplyDiff(new string) {
// Insert creates an insert text event and executes it // Insert creates an insert text event and executes it
func (eh *EventHandler) Insert(start Loc, textStr string) { func (eh *EventHandler) Insert(start Loc, textStr string) {
text := []byte(textStr) text := []byte(textStr)
eh.InsertBytes(start, text)
}
// InsertBytes creates an insert text event and executes it
func (eh *EventHandler) InsertBytes(start Loc, text []byte) {
e := &TextEvent{ e := &TextEvent{
C: *eh.cursors[eh.active], C: *eh.cursors[eh.active],
EventType: TextEventInsert, EventType: TextEventInsert,

View File

@@ -8,6 +8,7 @@ import (
ulua "github.com/zyedidia/micro/internal/lua" ulua "github.com/zyedidia/micro/internal/lua"
) )
// ErrNoSuchFunction is returned when Call is executed on a function that does not exist
var ErrNoSuchFunction = errors.New("No such function exists") var ErrNoSuchFunction = errors.New("No such function exists")
// LoadAllPlugins loads all detected plugins (in runtime/plugins and ConfigDir/plugins) // LoadAllPlugins loads all detected plugins (in runtime/plugins and ConfigDir/plugins)
@@ -65,6 +66,7 @@ func RunPluginFnBool(fn string, args ...lua.LValue) (bool, error) {
return retbool, reterr return retbool, reterr
} }
// Plugin stores information about the source files/info for a plugin
type Plugin struct { type Plugin struct {
DirName string // name of plugin folder DirName string // name of plugin folder
Name string // name of plugin Name string // name of plugin
@@ -74,6 +76,7 @@ type Plugin struct {
Default bool // pre-installed plugin Default bool // pre-installed plugin
} }
// IsEnabled returns if a plugin is enabled
func (p *Plugin) IsEnabled() bool { func (p *Plugin) IsEnabled() bool {
if v, ok := GlobalSettings[p.Name]; ok { if v, ok := GlobalSettings[p.Name]; ok {
return v.(bool) && p.Loaded return v.(bool) && p.Loaded
@@ -81,13 +84,15 @@ func (p *Plugin) IsEnabled() bool {
return true return true
} }
// Plugins is a list of all detected plugins (enabled or disabled)
var Plugins []*Plugin var Plugins []*Plugin
// Load creates an option for the plugin and runs all source files
func (p *Plugin) Load() error { func (p *Plugin) Load() error {
if v, ok := GlobalSettings[p.Name]; ok && !v.(bool) {
return nil
}
for _, f := range p.Srcs { for _, f := range p.Srcs {
if v, ok := GlobalSettings[p.Name]; ok && !v.(bool) {
return nil
}
dat, err := f.Data() dat, err := f.Data()
if err != nil { if err != nil {
return err return err
@@ -96,12 +101,13 @@ func (p *Plugin) Load() error {
if err != nil { if err != nil {
return err return err
} }
p.Loaded = true
RegisterGlobalOption(p.Name, true)
} }
p.Loaded = true
RegisterGlobalOption(p.Name, true)
return nil return nil
} }
// Call calls a given function in this plugin
func (p *Plugin) Call(fn string, args ...lua.LValue) (lua.LValue, error) { func (p *Plugin) Call(fn string, args ...lua.LValue) (lua.LValue, error) {
plug := ulua.L.GetGlobal(p.Name) plug := ulua.L.GetGlobal(p.Name)
if plug == lua.LNil { if plug == lua.LNil {
@@ -125,7 +131,23 @@ func (p *Plugin) Call(fn string, args ...lua.LValue) (lua.LValue, error) {
return ret, nil return ret, nil
} }
// FindPlugin returns the plugin with the given name
func FindPlugin(name string) *Plugin { func FindPlugin(name string) *Plugin {
var pl *Plugin
for _, p := range Plugins {
if !p.IsEnabled() {
continue
}
if p.Name == name {
pl = p
break
}
}
return pl
}
// FindAnyPlugin does not require the plugin to be enabled
func FindAnyPlugin(name string) *Plugin {
var pl *Plugin var pl *Plugin
for _, p := range Plugins { for _, p := range Plugins {
if p.Name == name { if p.Name == name {

View File

@@ -118,17 +118,17 @@ func fetchAllSources(count int, fetcher func(i int) PluginPackages) PluginPackag
} }
// Fetch retrieves all available PluginPackages from the given channels // Fetch retrieves all available PluginPackages from the given channels
func (pc PluginChannels) Fetch() PluginPackages { func (pc PluginChannels) Fetch(out io.Writer) PluginPackages {
return fetchAllSources(len(pc), func(i int) PluginPackages { return fetchAllSources(len(pc), func(i int) PluginPackages {
return pc[i].Fetch() return pc[i].Fetch(out)
}) })
} }
// Fetch retrieves all available PluginPackages from the given channel // Fetch retrieves all available PluginPackages from the given channel
func (pc PluginChannel) Fetch() PluginPackages { func (pc PluginChannel) Fetch(out io.Writer) PluginPackages {
resp, err := http.Get(string(pc)) resp, err := http.Get(string(pc))
if err != nil { if err != nil {
fmt.Println("Failed to query plugin channel:\n", err) fmt.Fprintln(out, "Failed to query plugin channel:\n", err)
return PluginPackages{} return PluginPackages{}
} }
defer resp.Body.Close() defer resp.Body.Close()
@@ -136,19 +136,19 @@ func (pc PluginChannel) Fetch() PluginPackages {
var repositories []PluginRepository var repositories []PluginRepository
if err := decoder.Decode(&repositories); err != nil { if err := decoder.Decode(&repositories); err != nil {
fmt.Println("Failed to decode channel data:\n", err) fmt.Fprintln(out, "Failed to decode channel data:\n", err)
return PluginPackages{} return PluginPackages{}
} }
return fetchAllSources(len(repositories), func(i int) PluginPackages { return fetchAllSources(len(repositories), func(i int) PluginPackages {
return repositories[i].Fetch() return repositories[i].Fetch(out)
}) })
} }
// Fetch retrieves all available PluginPackages from the given repository // Fetch retrieves all available PluginPackages from the given repository
func (pr PluginRepository) Fetch() PluginPackages { func (pr PluginRepository) Fetch(out io.Writer) PluginPackages {
resp, err := http.Get(string(pr)) resp, err := http.Get(string(pr))
if err != nil { if err != nil {
fmt.Println("Failed to query plugin repository:\n", err) fmt.Fprintln(out, "Failed to query plugin repository:\n", err)
return PluginPackages{} return PluginPackages{}
} }
defer resp.Body.Close() defer resp.Body.Close()
@@ -156,7 +156,7 @@ func (pr PluginRepository) Fetch() PluginPackages {
var plugins PluginPackages var plugins PluginPackages
if err := decoder.Decode(&plugins); err != nil { if err := decoder.Decode(&plugins); err != nil {
fmt.Println("Failed to decode repository data:\n", err) fmt.Fprintln(out, "Failed to decode repository data:\n", err)
return PluginPackages{} return PluginPackages{}
} }
if len(plugins) > 0 { if len(plugins) > 0 {
@@ -218,7 +218,7 @@ func (pp *PluginPackage) UnmarshalJSON(data []byte) error {
} }
// GetAllPluginPackages gets all PluginPackages which may be available. // GetAllPluginPackages gets all PluginPackages which may be available.
func GetAllPluginPackages() PluginPackages { func GetAllPluginPackages(out io.Writer) PluginPackages {
if allPluginPackages == nil { if allPluginPackages == nil {
getOption := func(name string) []string { getOption := func(name string) []string {
data := GetGlobalOption(name) data := GetGlobalOption(name)
@@ -249,9 +249,9 @@ func GetAllPluginPackages() PluginPackages {
} }
allPluginPackages = fetchAllSources(len(repos)+1, func(i int) PluginPackages { allPluginPackages = fetchAllSources(len(repos)+1, func(i int) PluginPackages {
if i == 0 { if i == 0 {
return channels.Fetch() return channels.Fetch(out)
} }
return repos[i-1].Fetch() return repos[i-1].Fetch(out)
}) })
} }
return allPluginPackages return allPluginPackages
@@ -301,8 +301,8 @@ func (pp PluginPackage) Match(text string) bool {
} }
// IsInstallable returns true if the package can be installed. // IsInstallable returns true if the package can be installed.
func (pp PluginPackage) IsInstallable() error { func (pp PluginPackage) IsInstallable(out io.Writer) error {
_, err := GetAllPluginPackages().Resolve(GetInstalledVersions(true), PluginDependencies{ _, err := GetAllPluginPackages(out).Resolve(GetInstalledVersions(true), PluginDependencies{
&PluginDependency{ &PluginDependency{
Name: pp.Name, Name: pp.Name,
Range: semver.Range(func(v semver.Version) bool { return true }), Range: semver.Range(func(v semver.Version) bool { return true }),
@@ -312,18 +312,18 @@ func (pp PluginPackage) IsInstallable() error {
// SearchPlugin retrieves a list of all PluginPackages which match the given search text and // SearchPlugin retrieves a list of all PluginPackages which match the given search text and
// could be or are already installed // could be or are already installed
func SearchPlugin(texts []string) (plugins PluginPackages) { func SearchPlugin(out io.Writer, texts []string) (plugins PluginPackages) {
plugins = make(PluginPackages, 0) plugins = make(PluginPackages, 0)
pluginLoop: pluginLoop:
for _, pp := range GetAllPluginPackages() { for _, pp := range GetAllPluginPackages(out) {
for _, text := range texts { for _, text := range texts {
if !pp.Match(text) { if !pp.Match(text) {
continue pluginLoop continue pluginLoop
} }
} }
if err := pp.IsInstallable(); err == nil { if err := pp.IsInstallable(out); err == nil {
plugins = append(plugins, pp) plugins = append(plugins, pp)
} }
} }
@@ -363,6 +363,9 @@ func GetInstalledVersions(withCore bool) PluginVersions {
} }
for _, p := range Plugins { for _, p := range Plugins {
if !p.IsEnabled() {
continue
}
version := GetInstalledPluginVersion(p.Name) version := GetInstalledPluginVersion(p.Name)
if pv := newStaticPluginVersion(p.Name, version); pv != nil { if pv := newStaticPluginVersion(p.Name, version); pv != nil {
result = append(result, pv) result = append(result, pv)
@@ -386,8 +389,8 @@ func GetInstalledPluginVersion(name string) string {
} }
// DownloadAndInstall downloads and installs the given plugin and version // DownloadAndInstall downloads and installs the given plugin and version
func (pv *PluginVersion) DownloadAndInstall() error { func (pv *PluginVersion) DownloadAndInstall(out io.Writer) error {
fmt.Printf("Downloading %q (%s) from %q\n", pv.pack.Name, pv.Version, pv.Url) fmt.Fprintf(out, "Downloading %q (%s) from %q\n", pv.pack.Name, pv.Version, pv.Url)
resp, err := http.Get(pv.Url) resp, err := http.Get(pv.Url)
if err != nil { if err != nil {
return err return err
@@ -536,7 +539,7 @@ func (all PluginPackages) Resolve(selectedVersions PluginVersions, open PluginDe
return selectedVersions, nil return selectedVersions, nil
} }
func (pv PluginVersions) install() { func (pv PluginVersions) install(out io.Writer) {
anyInstalled := false anyInstalled := false
currentlyInstalled := GetInstalledVersions(true) currentlyInstalled := GetInstalledVersions(true)
@@ -545,16 +548,16 @@ func (pv PluginVersions) install() {
shouldInstall := true shouldInstall := true
if pv := currentlyInstalled.find(sel.pack.Name); pv != nil { if pv := currentlyInstalled.find(sel.pack.Name); pv != nil {
if pv.Version.NE(sel.Version) { if pv.Version.NE(sel.Version) {
fmt.Println("Uninstalling", sel.pack.Name) fmt.Fprintln(out, "Uninstalling", sel.pack.Name)
UninstallPlugin(sel.pack.Name) UninstallPlugin(out, sel.pack.Name)
} else { } else {
shouldInstall = false shouldInstall = false
} }
} }
if shouldInstall { if shouldInstall {
if err := sel.DownloadAndInstall(); err != nil { if err := sel.DownloadAndInstall(out); err != nil {
fmt.Println(err) fmt.Fprintln(out, err)
return return
} }
anyInstalled = true anyInstalled = true
@@ -562,49 +565,56 @@ func (pv PluginVersions) install() {
} }
} }
if anyInstalled { if anyInstalled {
fmt.Println("One or more plugins installed.") fmt.Fprintln(out, "One or more plugins installed.")
} else { } else {
fmt.Println("Nothing to install / update") fmt.Fprintln(out, "Nothing to install / update")
} }
} }
// UninstallPlugin deletes the plugin folder of the given plugin // UninstallPlugin deletes the plugin folder of the given plugin
func UninstallPlugin(name string) { func UninstallPlugin(out io.Writer, name string) {
for _, p := range Plugins { for _, p := range Plugins {
if !p.IsEnabled() {
continue
}
if p.Name == name { if p.Name == name {
p.Loaded = false p.Loaded = false
if err := os.RemoveAll(filepath.Join(ConfigDir, "plug", p.DirName)); err != nil { if err := os.RemoveAll(filepath.Join(ConfigDir, "plug", p.DirName)); err != nil {
fmt.Println(err) fmt.Fprintln(out, err)
return return
} }
break
} }
} }
} }
// Install installs the plugin // Install installs the plugin
func (pl PluginPackage) Install() { func (pl PluginPackage) Install(out io.Writer) {
selected, err := GetAllPluginPackages().Resolve(GetInstalledVersions(true), PluginDependencies{ selected, err := GetAllPluginPackages(out).Resolve(GetInstalledVersions(true), PluginDependencies{
&PluginDependency{ &PluginDependency{
Name: pl.Name, Name: pl.Name,
Range: semver.Range(func(v semver.Version) bool { return true }), Range: semver.Range(func(v semver.Version) bool { return true }),
}}) }})
if err != nil { if err != nil {
fmt.Println(err) fmt.Fprintln(out, err)
return return
} }
selected.install() selected.install(out)
} }
// UpdatePlugins updates the given plugins // UpdatePlugins updates the given plugins
func UpdatePlugins(plugins []string) { func UpdatePlugins(out io.Writer, plugins []string) {
// if no plugins are specified, update all installed plugins. // if no plugins are specified, update all installed plugins.
if len(plugins) == 0 { if len(plugins) == 0 {
for _, p := range Plugins { for _, p := range Plugins {
if !p.IsEnabled() {
continue
}
plugins = append(plugins, p.Name) plugins = append(plugins, p.Name)
} }
} }
fmt.Println("Checking for plugin updates") fmt.Fprintln(out, "Checking for plugin updates")
microVersion := PluginVersions{ microVersion := PluginVersions{
newStaticPluginVersion(CorePluginName, util.Version), newStaticPluginVersion(CorePluginName, util.Version),
} }
@@ -621,10 +631,82 @@ func UpdatePlugins(plugins []string) {
} }
} }
selected, err := GetAllPluginPackages().Resolve(microVersion, updates) selected, err := GetAllPluginPackages(out).Resolve(microVersion, updates)
if err != nil { if err != nil {
fmt.Println(err) fmt.Fprintln(out, err)
return return
} }
selected.install() selected.install(out)
}
func PluginCommand(out io.Writer, cmd string, args []string) {
switch cmd {
case "install":
installedVersions := GetInstalledVersions(false)
for _, plugin := range args {
pp := GetAllPluginPackages(out).Get(plugin)
if pp == nil {
fmt.Fprintln(out, "Unknown plugin \""+plugin+"\"")
} else if err := pp.IsInstallable(out); err != nil {
fmt.Fprintln(out, "Error installing ", plugin, ": ", err)
} else {
for _, installed := range installedVersions {
if pp.Name == installed.Pack().Name {
if pp.Versions[0].Version.Compare(installed.Version) == 1 {
fmt.Fprintln(out, pp.Name, " is already installed but out-of-date: use 'plugin update ", pp.Name, "' to update")
} else {
fmt.Fprintln(out, pp.Name, " is already installed")
}
}
}
pp.Install(out)
}
}
case "remove":
removed := ""
for _, plugin := range args {
// check if the plugin exists.
for _, p := range Plugins {
if p.Name == plugin && p.Default {
fmt.Fprintln(out, "Default plugins cannot be removed, but can be disabled via settings.")
continue
}
if p.Name == plugin {
UninstallPlugin(out, plugin)
removed += plugin + " "
continue
}
}
}
if removed != "" {
fmt.Fprintln(out, "Removed ", removed)
} else {
fmt.Fprintln(out, "No plugins removed")
}
case "update":
UpdatePlugins(out, args)
case "list":
plugins := GetInstalledVersions(false)
fmt.Fprintln(out, "The following plugins are currently installed:")
for _, p := range plugins {
fmt.Fprintf(out, "%s (%s)\n", p.Pack().Name, p.Version)
}
case "search":
plugins := SearchPlugin(out, args)
fmt.Fprintln(out, len(plugins), " plugins found")
for _, p := range plugins {
fmt.Fprintln(out, "----------------")
fmt.Fprintln(out, p.String())
}
fmt.Fprintln(out, "----------------")
case "available":
packages := GetAllPluginPackages(out)
fmt.Fprintln(out, "Available Plugins:")
for _, pkg := range packages {
fmt.Fprintln(out, pkg.Name)
}
default:
fmt.Fprintln(out, "Invalid plugin command")
}
} }