]> git.lizzy.rs Git - micro.git/blobdiff - internal/action/command.go
better top
[micro.git] / internal / action / command.go
index 37ff0788925b086e5ace5ce211f3038208ecf8e0..3a445caa0a64e0aa76dadb118834147e2a838f18 100644 (file)
@@ -5,22 +5,19 @@ import (
        "errors"
        "fmt"
        "os"
+       "os/exec"
        "path/filepath"
        "regexp"
        "strconv"
        "strings"
        "unicode/utf8"
 
-       luar "layeh.com/gopher-luar"
-
-       lua "github.com/yuin/gopher-lua"
+       shellquote "github.com/kballard/go-shellquote"
        "github.com/zyedidia/micro/internal/buffer"
        "github.com/zyedidia/micro/internal/config"
-       ulua "github.com/zyedidia/micro/internal/lua"
        "github.com/zyedidia/micro/internal/screen"
        "github.com/zyedidia/micro/internal/shell"
        "github.com/zyedidia/micro/internal/util"
-       "github.com/zyedidia/micro/pkg/shellwords"
 )
 
 // A Command contains information about how to execute a command
@@ -34,68 +31,45 @@ var commands map[string]Command
 
 func InitCommands() {
        commands = map[string]Command{
-               "set":        Command{(*BufPane).SetCmd, OptionValueComplete},
-               "reset":      Command{(*BufPane).ResetCmd, OptionValueComplete},
-               "setlocal":   Command{(*BufPane).SetLocalCmd, OptionValueComplete},
-               "show":       Command{(*BufPane).ShowCmd, OptionComplete},
-               "showkey":    Command{(*BufPane).ShowKeyCmd, nil},
-               "run":        Command{(*BufPane).RunCmd, nil},
-               "bind":       Command{(*BufPane).BindCmd, nil},
-               "unbind":     Command{(*BufPane).UnbindCmd, nil},
-               "quit":       Command{(*BufPane).QuitCmd, nil},
-               "goto":       Command{(*BufPane).GotoCmd, nil},
-               "save":       Command{(*BufPane).SaveCmd, nil},
-               "replace":    Command{(*BufPane).ReplaceCmd, nil},
-               "replaceall": Command{(*BufPane).ReplaceAllCmd, nil},
-               "vsplit":     Command{(*BufPane).VSplitCmd, buffer.FileComplete},
-               "hsplit":     Command{(*BufPane).HSplitCmd, buffer.FileComplete},
-               "tab":        Command{(*BufPane).NewTabCmd, buffer.FileComplete},
-               "help":       Command{(*BufPane).HelpCmd, HelpComplete},
-               "eval":       Command{(*BufPane).EvalCmd, nil},
-               "log":        Command{(*BufPane).ToggleLogCmd, nil},
-               "plugin":     Command{(*BufPane).PluginCmd, PluginComplete},
-               "reload":     Command{(*BufPane).ReloadCmd, nil},
-               "reopen":     Command{(*BufPane).ReopenCmd, nil},
-               "cd":         Command{(*BufPane).CdCmd, buffer.FileComplete},
-               "pwd":        Command{(*BufPane).PwdCmd, nil},
-               "open":       Command{(*BufPane).OpenCmd, buffer.FileComplete},
-               "tabswitch":  Command{(*BufPane).TabSwitchCmd, nil},
-               "term":       Command{(*BufPane).TermCmd, nil},
-               "memusage":   Command{(*BufPane).MemUsageCmd, nil},
-               "retab":      Command{(*BufPane).RetabCmd, nil},
-               "raw":        Command{(*BufPane).RawCmd, nil},
+               "set":        {(*BufPane).SetCmd, OptionValueComplete},
+               "reset":      {(*BufPane).ResetCmd, OptionValueComplete},
+               "setlocal":   {(*BufPane).SetLocalCmd, OptionValueComplete},
+               "show":       {(*BufPane).ShowCmd, OptionComplete},
+               "showkey":    {(*BufPane).ShowKeyCmd, nil},
+               "run":        {(*BufPane).RunCmd, nil},
+               "bind":       {(*BufPane).BindCmd, nil},
+               "unbind":     {(*BufPane).UnbindCmd, nil},
+               "quit":       {(*BufPane).QuitCmd, nil},
+               "goto":       {(*BufPane).GotoCmd, nil},
+               "save":       {(*BufPane).SaveCmd, nil},
+               "replace":    {(*BufPane).ReplaceCmd, nil},
+               "replaceall": {(*BufPane).ReplaceAllCmd, nil},
+               "vsplit":     {(*BufPane).VSplitCmd, buffer.FileComplete},
+               "hsplit":     {(*BufPane).HSplitCmd, buffer.FileComplete},
+               "tab":        {(*BufPane).NewTabCmd, buffer.FileComplete},
+               "help":       {(*BufPane).HelpCmd, HelpComplete},
+               "eval":       {(*BufPane).EvalCmd, nil},
+               "log":        {(*BufPane).ToggleLogCmd, nil},
+               "plugin":     {(*BufPane).PluginCmd, PluginComplete},
+               "reload":     {(*BufPane).ReloadCmd, nil},
+               "reopen":     {(*BufPane).ReopenCmd, nil},
+               "cd":         {(*BufPane).CdCmd, buffer.FileComplete},
+               "pwd":        {(*BufPane).PwdCmd, nil},
+               "open":       {(*BufPane).OpenCmd, buffer.FileComplete},
+               "tabswitch":  {(*BufPane).TabSwitchCmd, nil},
+               "term":       {(*BufPane).TermCmd, nil},
+               "memusage":   {(*BufPane).MemUsageCmd, nil},
+               "retab":      {(*BufPane).RetabCmd, nil},
+               "raw":        {(*BufPane).RawCmd, nil},
+               "textfilter": {(*BufPane).TextFilterCmd, nil},
        }
 }
 
 // MakeCommand is a function to easily create new commands
 // This can be called by plugins in Lua so that plugins can define their own commands
-func LuaMakeCommand(name, function string, completer buffer.Completer) {
-       action := LuaFunctionCommand(function)
-       commands[name] = Command{action, completer}
-}
-
-// LuaFunctionCommand returns a normal function
-// so that a command can be bound to a lua function
-func LuaFunctionCommand(fn string) func(*BufPane, []string) {
-       luaFn := strings.Split(fn, ".")
-       if len(luaFn) <= 1 {
-               return nil
-       }
-       plName, plFn := luaFn[0], luaFn[1]
-       pl := config.FindPlugin(plName)
-       if pl == nil {
-               return nil
-       }
-       return func(bp *BufPane, args []string) {
-               var luaArgs []lua.LValue
-               luaArgs = append(luaArgs, luar.New(ulua.L, bp))
-               for _, v := range args {
-                       luaArgs = append(luaArgs, luar.New(ulua.L, v))
-               }
-               _, err := pl.Call(plFn, luaArgs...)
-               if err != nil {
-                       screen.TermMessage(err)
-               }
+func MakeCommand(name string, action func(bp *BufPane, args []string), completer buffer.Completer) {
+       if action != nil {
+               commands[name] = Command{action, completer}
        }
 }
 
@@ -122,106 +96,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
 func (h *BufPane) PluginCmd(args []string) {
-       if len(args) <= 0 {
-               InfoBar.Error("Not enough arguments, see 'help commands'")
-               return
-       }
-
-       valid := true
-       switch args[0] {
-       case "list":
-               for _, pl := range config.Plugins {
-                       var en string
-                       if pl.IsEnabled() {
-                               en = "enabled"
-                       } else {
-                               en = "disabled"
-                       }
-                       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:
-               InfoBar.Error("Not a valid plugin command")
+       if len(args) < 1 {
+               InfoBar.Error("Not enough arguments")
                return
        }
 
-       if valid && h.Buf.Type != buffer.BTLog {
+       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
@@ -235,11 +123,38 @@ func (h *BufPane) RetabCmd(args []string) {
 func (h *BufPane) RawCmd(args []string) {
        width, height := screen.Screen.Size()
        iOffset := config.GetInfoBarOffset()
-       tp := NewTabFromPane(0, 0, width, height-iOffset, NewRawPane())
+       tp := NewTabFromPane(0, 0, width, height-iOffset, NewRawPane(nil))
        Tabs.AddTab(tp)
        Tabs.SetActive(len(Tabs.List) - 1)
 }
 
+// TextFilterCmd filters the selection through the command.
+// Selection goes to the command input.
+// On successful run command output replaces the current selection.
+func (h *BufPane) TextFilterCmd(args []string) {
+       if len(args) == 0 {
+               InfoBar.Error("usage: textfilter arguments")
+               return
+       }
+       sel := h.Cursor.GetSelection()
+       if len(sel) == 0 {
+               h.Cursor.SelectWord()
+               sel = h.Cursor.GetSelection()
+       }
+       var bout, berr bytes.Buffer
+       cmd := exec.Command(args[0], args[1:]...)
+       cmd.Stdin = strings.NewReader(string(sel))
+       cmd.Stderr = &berr
+       cmd.Stdout = &bout
+       err := cmd.Run()
+       if err != nil {
+               InfoBar.Error(err.Error() + " " + berr.String())
+               return
+       }
+       h.Cursor.DeleteSelection()
+       h.Buf.Insert(h.Cursor.Loc, bout.String())
+}
+
 // TabSwitchCmd switches to a given tab either by name or by number
 func (h *BufPane) TabSwitchCmd(args []string) {
        if len(args) > 0 {
@@ -319,11 +234,14 @@ func (h *BufPane) OpenCmd(args []string) {
        if len(args) > 0 {
                filename := args[0]
                // the filename might or might not be quoted, so unquote first then join the strings.
-               args, err := shellwords.Split(filename)
+               args, err := shellquote.Split(filename)
                if err != nil {
                        InfoBar.Error("Error parsing args ", err)
                        return
                }
+               if len(args) == 0 {
+                       return
+               }
                filename = strings.Join(args, " ")
 
                open := func() {
@@ -472,6 +390,7 @@ func (h *BufPane) HSplitCmd(args []string) {
 
 // EvalCmd evaluates a lua expression
 func (h *BufPane) EvalCmd(args []string) {
+       InfoBar.Error("Eval unsupported")
 }
 
 // NewTabCmd opens the given file in a new tab
@@ -498,37 +417,55 @@ func (h *BufPane) NewTabCmd(args []string) {
 }
 
 func SetGlobalOptionNative(option string, nativeValue interface{}) error {
-       config.GlobalSettings[option] = nativeValue
-
-       if option == "colorscheme" {
-               // LoadSyntaxFiles()
-               config.InitColorscheme()
-               for _, b := range buffer.OpenBuffers {
-                       b.UpdateRules()
+       local := false
+       for _, s := range config.LocalSettings {
+               if s == option {
+                       local = true
+                       break
                }
-       } else if option == "infobar" || option == "keymenu" {
-               Tabs.Resize()
-       } else if option == "mouse" {
-               if !nativeValue.(bool) {
-                       screen.Screen.DisableMouse()
-               } else {
-                       screen.Screen.EnableMouse()
-               }
-       } else if option == "autosave" {
-               if nativeValue.(float64) > 0 {
-                       config.SetAutoTime(int(nativeValue.(float64)))
-                       config.StartAutoSave()
+       }
+
+       if !local {
+               config.GlobalSettings[option] = nativeValue
+
+               if option == "colorscheme" {
+                       // LoadSyntaxFiles()
+                       config.InitColorscheme()
+                       for _, b := range buffer.OpenBuffers {
+                               b.UpdateRules()
+                       }
+               } else if option == "infobar" || option == "keymenu" {
+                       Tabs.Resize()
+               } else if option == "mouse" {
+                       if !nativeValue.(bool) {
+                               screen.Screen.DisableMouse()
+                       } else {
+                               screen.Screen.EnableMouse()
+                       }
+               } else if option == "autosave" {
+                       if nativeValue.(float64) > 0 {
+                               config.SetAutoTime(int(nativeValue.(float64)))
+                               config.StartAutoSave()
+                       } else {
+                               config.SetAutoTime(0)
+                       }
+               } else if option == "paste" {
+                       screen.Screen.SetPaste(nativeValue.(bool))
                } else {
-                       config.SetAutoTime(0)
-               }
-       } else {
-               for _, pl := range config.Plugins {
-                       if option == pl.Name {
-                               if nativeValue.(bool) && !pl.Loaded {
-                                       pl.Load()
-                                       pl.Call("init")
-                               } else if !nativeValue.(bool) && pl.Loaded {
-                                       pl.Call("deinit")
+                       for _, pl := range config.Plugins {
+                               if option == pl.Name {
+                                       if nativeValue.(bool) && !pl.Loaded {
+                                               pl.Load()
+                                               _, err := pl.Call("init")
+                                               if err != nil && err != config.ErrNoSuchFunction {
+                                                       screen.TermMessage(err)
+                                               }
+                                       } else if !nativeValue.(bool) && pl.Loaded {
+                                               _, err := pl.Call("deinit")
+                                               if err != nil && err != config.ErrNoSuchFunction {
+                                                       screen.TermMessage(err)
+                                               }
+                                       }
                                }
                        }
                }
@@ -666,7 +603,7 @@ func (h *BufPane) BindCmd(args []string) {
 // UnbindCmd binds a key to its default action
 func (h *BufPane) UnbindCmd(args []string) {
        if len(args) < 1 {
-               InfoBar.Error("Not enough arguements")
+               InfoBar.Error("Not enough arguments")
                return
        }
 
@@ -678,7 +615,7 @@ func (h *BufPane) UnbindCmd(args []string) {
 
 // RunCmd runs a shell command in the background
 func (h *BufPane) RunCmd(args []string) {
-       runf, err := shell.RunBackgroundShell(shellwords.Join(args...))
+       runf, err := shell.RunBackgroundShell(shellquote.Join(args...))
        if err != nil {
                InfoBar.Error(err)
        } else {
@@ -726,6 +663,7 @@ func (h *BufPane) GotoCmd(args []string) {
                        line = util.Clamp(line-1, 0, h.Buf.LinesNum()-1)
                        h.Cursor.GotoLoc(buffer.Loc{0, line})
                }
+               h.Relocate()
        }
 }
 
@@ -794,23 +732,23 @@ func (h *BufPane) ReplaceCmd(args []string) {
 
        nreplaced := 0
        start := h.Buf.Start()
-       end := h.Buf.End()
-       if h.Cursor.HasSelection() {
-               start = h.Cursor.CurSelection[0]
-               end = h.Cursor.CurSelection[1]
-       }
+       // end := h.Buf.End()
+       // if h.Cursor.HasSelection() {
+       //      start = h.Cursor.CurSelection[0]
+       //      end = h.Cursor.CurSelection[1]
+       // }
        if all {
-               nreplaced = h.Buf.ReplaceRegex(start, end, regex, replace)
+               nreplaced = h.Buf.ReplaceRegex(start, h.Buf.End(), regex, replace)
        } else {
                inRange := func(l buffer.Loc) bool {
-                       return l.GreaterEqual(start) && l.LessThan(end)
+                       return l.GreaterEqual(start) && l.LessEqual(h.Buf.End())
                }
 
                searchLoc := start
                searching := true
                var doReplacement func()
                doReplacement = func() {
-                       locs, found, err := h.Buf.FindNext(search, start, end, searchLoc, true, !noRegex)
+                       locs, found, err := h.Buf.FindNext(search, start, h.Buf.End(), searchLoc, true, !noRegex)
                        if err != nil {
                                InfoBar.Error(err)
                                return
@@ -827,6 +765,7 @@ func (h *BufPane) ReplaceCmd(args []string) {
                        InfoBar.YNPrompt("Perform replacement (y,n,esc)", func(yes, canceled bool) {
                                if !canceled && yes {
                                        h.Buf.Replace(locs[0], locs[1], replaceStr)
+
                                        searchLoc = locs[0]
                                        searchLoc.X += utf8.RuneCount(replace)
                                        h.Cursor.Loc = searchLoc
@@ -866,7 +805,12 @@ func (h *BufPane) ReplaceAllCmd(args []string) {
 
 // TermCmd opens a terminal in the current view
 func (h *BufPane) TermCmd(args []string) {
-       ps := MainTab().Panes
+       ps := h.tab.Panes
+
+       if !TermEmuSupported {
+               InfoBar.Error("Terminal emulator not supported on this system")
+               return
+       }
 
        if len(args) == 0 {
                sh := os.Getenv("SHELL")
@@ -879,7 +823,7 @@ func (h *BufPane) TermCmd(args []string) {
 
        term := func(i int, newtab bool) {
                t := new(shell.Terminal)
-               t.Start(args, false, true, "", nil)
+               t.Start(args, false, true, nil, nil)
 
                id := h.ID()
                if newtab {
@@ -891,7 +835,12 @@ func (h *BufPane) TermCmd(args []string) {
                }
 
                v := h.GetView()
-               MainTab().Panes[i] = NewTermPane(v.X, v.Y, v.Width, v.Height, t, id)
+               tp, err := NewTermPane(v.X, v.Y, v.Width, v.Height, t, id, MainTab())
+               if err != nil {
+                       InfoBar.Error(err)
+                       return
+               }
+               MainTab().Panes[i] = tp
                MainTab().SetActive(i)
        }
 
@@ -923,12 +872,16 @@ func (h *BufPane) TermCmd(args []string) {
 
 // HandleCommand handles input from the user
 func (h *BufPane) HandleCommand(input string) {
-       args, err := shellwords.Split(input)
+       args, err := shellquote.Split(input)
        if err != nil {
                InfoBar.Error("Error parsing args ", err)
                return
        }
 
+       if len(args) == 0 {
+               return
+       }
+
        inputCmd := args[0]
 
        if _, ok := commands[inputCmd]; !ok {