package action
import (
+ "bytes"
"errors"
"fmt"
- "log"
"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},
- "togglelog": Command{(*BufPane).ToggleLogCmd, nil},
- "plugin": Command{(*BufPane).PluginCmd, nil},
- "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{"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) < 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
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() {
// ToggleLogCmd toggles the log view
func (h *BufPane) ToggleLogCmd(args []string) {
+ if h.Buf.Type != buffer.BTLog {
+ OpenLogBuf(h)
+ } else {
+ h.Quit()
+ }
}
// ReloadCmd reloads all files (syntax files, colorschemes...)
screen.TermMessage(err)
}
- log.Println("RELOAD CONFIG", len(buffer.OpenBuffers))
for _, b := range buffer.OpenBuffers {
- log.Println("UPDATE RULES")
b.UpdateRules()
}
}
// 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()
+ }
+
+ 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 {
- screen.Screen.EnableMouse()
- }
- } 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)
+ }
+ }
}
}
}
option := args[0]
defaultGlobals := config.DefaultGlobalSettings()
- defaultLocals := config.DefaultLocalSettings()
+ defaultLocals := config.DefaultCommonSettings()
if _, ok := defaultGlobals[option]; ok {
SetGlobalOptionNative(option, defaultGlobals[option])
// 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()
}
}
all := false
noRegex := false
- if len(args) > 2 {
- for _, arg := range args[2:] {
- switch arg {
- case "-a":
- all = true
- case "-l":
- noRegex = true
- default:
+ foundSearch := false
+ foundReplace := false
+ var search string
+ var replaceStr string
+ for _, arg := range args {
+ switch arg {
+ case "-a":
+ all = true
+ case "-l":
+ noRegex = true
+ default:
+ if !foundSearch {
+ foundSearch = true
+ search = arg
+ } else if !foundReplace {
+ foundReplace = true
+ replaceStr = arg
+ } else {
InfoBar.Error("Invalid flag: " + arg)
return
}
}
}
- search := args[0]
-
if noRegex {
search = regexp.QuoteMeta(search)
}
- replace := []byte(args[1])
- replaceStr := args[1]
+ replace := []byte(replaceStr)
var regex *regexp.Regexp
var err error
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 {
InfoBar.Error("Unknown command ", inputCmd)
} else {
+ WriteLog("> " + input + "\n")
commands[inputCmd].action(h, args[1:])
+ WriteLog("\n")
}
}