package action import ( "os" "github.com/zyedidia/micro/cmd/micro/buffer" "github.com/zyedidia/micro/cmd/micro/config" "github.com/zyedidia/micro/cmd/micro/screen" "github.com/zyedidia/micro/cmd/micro/shell" "github.com/zyedidia/micro/cmd/micro/shellwords" "github.com/zyedidia/micro/cmd/micro/util" ) // A Command contains an action (a function to call) as well as information about how to autocomplete the command type Command struct { action func(*BufHandler, []string) completions []Completion } // A StrCommand is similar to a command but keeps the name of the action type StrCommand struct { action string completions []Completion } var commands map[string]Command var commandActions = map[string]func(*BufHandler, []string){ "Set": (*BufHandler).SetCmd, "SetLocal": (*BufHandler).SetLocalCmd, "Show": (*BufHandler).ShowCmd, "ShowKey": (*BufHandler).ShowKeyCmd, "Run": (*BufHandler).RunCmd, "Bind": (*BufHandler).BindCmd, "Quit": (*BufHandler).QuitCmd, "Save": (*BufHandler).SaveCmd, "Replace": (*BufHandler).ReplaceCmd, "ReplaceAll": (*BufHandler).ReplaceAllCmd, "VSplit": (*BufHandler).VSplitCmd, "HSplit": (*BufHandler).HSplitCmd, "Tab": (*BufHandler).NewTabCmd, "Help": (*BufHandler).HelpCmd, "Eval": (*BufHandler).EvalCmd, "ToggleLog": (*BufHandler).ToggleLogCmd, "Plugin": (*BufHandler).PluginCmd, "Reload": (*BufHandler).ReloadCmd, "Cd": (*BufHandler).CdCmd, "Pwd": (*BufHandler).PwdCmd, "Open": (*BufHandler).OpenCmd, "TabSwitch": (*BufHandler).TabSwitchCmd, "Term": (*BufHandler).TermCmd, "MemUsage": (*BufHandler).MemUsageCmd, "Retab": (*BufHandler).RetabCmd, "Raw": (*BufHandler).RawCmd, } // InitCommands initializes the default commands func InitCommands() { commands = make(map[string]Command) defaults := DefaultCommands() parseCommands(defaults) } func parseCommands(userCommands map[string]StrCommand) { for k, v := range userCommands { MakeCommand(k, v.action, v.completions...) } } // 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 MakeCommand(name, function string, completions ...Completion) { action := commandActions[function] // if _, ok := commandActions[function]; !ok { // If the user seems to be binding a function that doesn't exist // We hope that it's a lua function that exists and bind it to that // action = LuaFunctionCommand(function) // } commands[name] = Command{action, completions} } // DefaultCommands returns a map containing micro's default commands func DefaultCommands() map[string]StrCommand { return map[string]StrCommand{ "set": {"Set", []Completion{OptionCompletion, OptionValueCompletion}}, "setlocal": {"SetLocal", []Completion{OptionCompletion, OptionValueCompletion}}, "show": {"Show", []Completion{OptionCompletion, NoCompletion}}, "showkey": {"ShowKey", []Completion{NoCompletion}}, "bind": {"Bind", []Completion{NoCompletion}}, "run": {"Run", []Completion{NoCompletion}}, "quit": {"Quit", []Completion{NoCompletion}}, "save": {"Save", []Completion{NoCompletion}}, "replace": {"Replace", []Completion{NoCompletion}}, "replaceall": {"ReplaceAll", []Completion{NoCompletion}}, "vsplit": {"VSplit", []Completion{FileCompletion, NoCompletion}}, "hsplit": {"HSplit", []Completion{FileCompletion, NoCompletion}}, "tab": {"Tab", []Completion{FileCompletion, NoCompletion}}, "help": {"Help", []Completion{HelpCompletion, NoCompletion}}, "eval": {"Eval", []Completion{NoCompletion}}, "log": {"ToggleLog", []Completion{NoCompletion}}, "plugin": {"Plugin", []Completion{PluginCmdCompletion, PluginNameCompletion}}, "reload": {"Reload", []Completion{NoCompletion}}, "cd": {"Cd", []Completion{FileCompletion}}, "pwd": {"Pwd", []Completion{NoCompletion}}, "open": {"Open", []Completion{FileCompletion}}, "tabswitch": {"TabSwitch", []Completion{NoCompletion}}, "term": {"Term", []Completion{NoCompletion}}, "memusage": {"MemUsage", []Completion{NoCompletion}}, "retab": {"Retab", []Completion{NoCompletion}}, "raw": {"Raw", []Completion{NoCompletion}}, } } // CommandEditAction returns a bindable function that opens a prompt with // the given string and executes the command when the user presses // enter func CommandEditAction(prompt string) BufKeyAction { return func(h *BufHandler) bool { InfoBar.Prompt("> ", prompt, "Command", nil, func(resp string, canceled bool) { if !canceled { MainTab().CurPane().HandleCommand(resp) } }) return false } } // CommandAction returns a bindable function which executes the // given command func CommandAction(cmd string) BufKeyAction { return func(h *BufHandler) bool { MainTab().CurPane().HandleCommand(cmd) return false } } // PluginCmd installs, removes, updates, lists, or searches for given plugins func (h *BufHandler) PluginCmd(args []string) { } // RetabCmd changes all spaces to tabs or all tabs to spaces // depending on the user's settings func (h *BufHandler) RetabCmd(args []string) { } // RawCmd opens a new raw view which displays the escape sequences micro // is receiving in real-time func (h *BufHandler) RawCmd(args []string) { } // TabSwitchCmd switches to a given tab either by name or by number func (h *BufHandler) TabSwitchCmd(args []string) { } // CdCmd changes the current working directory func (h *BufHandler) CdCmd(args []string) { } // MemUsageCmd prints micro's memory usage // Alloc shows how many bytes are currently in use // Sys shows how many bytes have been requested from the operating system // NumGC shows how many times the GC has been run // Note that Go commonly reserves more memory from the OS than is currently in-use/required // Additionally, even if Go returns memory to the OS, the OS does not always claim it because // there may be plenty of memory to spare func (h *BufHandler) MemUsageCmd(args []string) { InfoBar.Message(util.GetMemStats()) } // PwdCmd prints the current working directory func (h *BufHandler) PwdCmd(args []string) { wd, err := os.Getwd() if err != nil { InfoBar.Message(err.Error()) } else { InfoBar.Message(wd) } } // OpenCmd opens a new buffer with a given filename func (h *BufHandler) OpenCmd(args []string) { } // ToggleLogCmd toggles the log view func (h *BufHandler) ToggleLogCmd(args []string) { } // ReloadCmd reloads all files (syntax files, colorschemes...) func (h *BufHandler) ReloadCmd(args []string) { } // HelpCmd tries to open the given help page in a horizontal split func (h *BufHandler) HelpCmd(args []string) { } // VSplitCmd opens a vertical split with file given in the first argument // If no file is given, it opens an empty buffer in a new split func (h *BufHandler) VSplitCmd(args []string) { buf, err := buffer.NewBufferFromFile(args[0], buffer.BTDefault) if err != nil { InfoBar.Error(err) return } h.vsplit(buf) } // HSplitCmd opens a horizontal split with file given in the first argument // If no file is given, it opens an empty buffer in a new split func (h *BufHandler) HSplitCmd(args []string) { buf, err := buffer.NewBufferFromFile(args[0], buffer.BTDefault) if err != nil { InfoBar.Error(err) return } h.hsplit(buf) } // EvalCmd evaluates a lua expression func (h *BufHandler) EvalCmd(args []string) { } // NewTabCmd opens the given file in a new tab func (h *BufHandler) NewTabCmd(args []string) { width, height := screen.Screen.Size() if len(args) > 0 { for _, a := range args { b, err := buffer.NewBufferFromFile(a, buffer.BTDefault) if err != nil { InfoBar.Error(err) return } tp := NewTabFromBuffer(0, 0, width, height-1, b) Tabs.AddTab(tp) Tabs.SetActive(len(Tabs.List) - 1) } } else { b := buffer.NewBufferFromString("", "", buffer.BTDefault) tp := NewTabFromBuffer(0, 0, width, height-1, b) Tabs.AddTab(tp) Tabs.SetActive(len(Tabs.List) - 1) } } // SetCmd sets an option func (h *BufHandler) SetCmd(args []string) { } // SetLocalCmd sets an option local to the buffer func (h *BufHandler) SetLocalCmd(args []string) { } // ShowCmd shows the value of the given option func (h *BufHandler) ShowCmd(args []string) { } // ShowKeyCmd displays the action that a key is bound to func (h *BufHandler) ShowKeyCmd(args []string) { if len(args) < 1 { InfoBar.Error("Please provide a key to show") return } if action, ok := config.Bindings[args[0]]; ok { InfoBar.Message(action) } else { InfoBar.Message(args[0], " has no binding") } } // BindCmd creates a new keybinding func (h *BufHandler) BindCmd(args []string) { } // RunCmd runs a shell command in the background func (h *BufHandler) RunCmd(args []string) { runf, err := shell.RunBackgroundShell(shellwords.Join(args...)) if err != nil { InfoBar.Error(err) } else { go func() { InfoBar.Message(runf()) screen.Redraw() }() } } // QuitCmd closes the main view func (h *BufHandler) QuitCmd(args []string) { } // SaveCmd saves the buffer in the main view func (h *BufHandler) SaveCmd(args []string) { } // ReplaceCmd runs search and replace func (h *BufHandler) ReplaceCmd(args []string) { } // ReplaceAllCmd replaces search term all at once func (h *BufHandler) ReplaceAllCmd(args []string) { } // TermCmd opens a terminal in the current view func (h *BufHandler) TermCmd(args []string) { ps := MainTab().Panes term := func(i int) { v := h.GetView() t := new(shell.Terminal) t.Start(args, false, true) MainTab().Panes[i] = NewTermHandler(v.X, v.Y, v.Width, v.Height, t, h.ID()) MainTab().SetActive(i) } for i, p := range ps { if p.ID() == h.ID() { if h.Buf.Modified() { InfoBar.YNPrompt("Save changes to "+h.Buf.GetName()+" before closing? (y,n,esc)", func(yes, canceled bool) { if !canceled && !yes { term(i) } else if !canceled && yes { h.Save() term(i) } }) } else { term(i) } } } } // HandleCommand handles input from the user func (h *BufHandler) HandleCommand(input string) { args, err := shellwords.Split(input) if err != nil { InfoBar.Error("Error parsing args ", err) return } inputCmd := args[0] if _, ok := commands[inputCmd]; !ok { InfoBar.Error("Unknown command ", inputCmd) } else { commands[inputCmd].action(h, args[1:]) } }