Files
zyedidia.micro/cmd/micro/action/command.go
2019-12-25 17:05:10 -05:00

362 lines
10 KiB
Go

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
if len(args) == 0 {
sh := os.Getenv("SHELL")
if sh == "" {
InfoBar.Error("Shell environment not found")
return
}
args = []string{sh}
}
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:])
}
}