"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
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, ".")
- plName, plFn := luaFn[0], luaFn[1]
- pl := config.FindPlugin(plName)
- 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}
}
}
}
}
-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
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 {
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() {
// EvalCmd evaluates a lua expression
func (h *BufPane) EvalCmd(args []string) {
+ InfoBar.Error("Eval unsupported")
}
// NewTabCmd opens the given file in a new tab
}
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)
+ }
+ }
}
}
}
// 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
}
// 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 {
line = util.Clamp(line-1, 0, h.Buf.LinesNum()-1)
h.Cursor.GotoLoc(buffer.Loc{0, line})
}
+ h.Relocate()
}
}
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
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
// 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")
}
term := func(i int, newtab bool) {
-
t := new(shell.Terminal)
- t.Start(args, false, true)
+ t.Start(args, false, true, nil, nil)
id := h.ID()
if newtab {
}
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)
}
// 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 {