X-Git-Url: https://git.lizzy.rs/?a=blobdiff_plain;ds=inline;f=internal%2Faction%2Fcommand.go;h=3a445caa0a64e0aa76dadb118834147e2a838f18;hb=90304fb472fab5a9809feb0d9b23b0930619cf0d;hp=37ff0788925b086e5ace5ce211f3038208ecf8e0;hpb=b68461cf72cb913c40dd60a77b2cb2fcc27aec0f;p=micro.git diff --git a/internal/action/command.go b/internal/action/command.go index 37ff0788..3a445caa 100644 --- a/internal/action/command.go +++ b/internal/action/command.go @@ -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 {