]> git.lizzy.rs Git - micro.git/blob - internal/action/bufpane.go
4ecf25335ad2d446789147b77e7e5f7a5b4289c8
[micro.git] / internal / action / bufpane.go
1 package action
2
3 import (
4         "strings"
5         "time"
6
7         luar "layeh.com/gopher-luar"
8
9         lua "github.com/yuin/gopher-lua"
10         "github.com/zyedidia/micro/v2/internal/buffer"
11         "github.com/zyedidia/micro/v2/internal/clipboard"
12         "github.com/zyedidia/micro/v2/internal/config"
13         "github.com/zyedidia/micro/v2/internal/display"
14         ulua "github.com/zyedidia/micro/v2/internal/lua"
15         "github.com/zyedidia/micro/v2/internal/screen"
16         "github.com/zyedidia/tcell/v2"
17 )
18
19 type BufKeyAction func(*BufPane) bool
20 type BufMouseAction func(*BufPane, *tcell.EventMouse) bool
21
22 var BufBindings *KeyTree
23
24 func BufKeyActionGeneral(a BufKeyAction) PaneKeyAction {
25         return func(p Pane) bool {
26                 return a(p.(*BufPane))
27         }
28 }
29
30 func BufMouseActionGeneral(a BufMouseAction) PaneMouseAction {
31         return func(p Pane, me *tcell.EventMouse) bool {
32                 return a(p.(*BufPane), me)
33         }
34 }
35
36 func init() {
37         BufBindings = NewKeyTree()
38 }
39
40 func LuaAction(fn string) func(*BufPane) bool {
41         luaFn := strings.Split(fn, ".")
42         if len(luaFn) <= 1 {
43                 return nil
44         }
45         plName, plFn := luaFn[0], luaFn[1]
46         pl := config.FindPlugin(plName)
47         if pl == nil {
48                 return nil
49         }
50         return func(h *BufPane) bool {
51                 val, err := pl.Call(plFn, luar.New(ulua.L, h))
52                 if err != nil {
53                         screen.TermMessage(err)
54                 }
55                 if v, ok := val.(lua.LBool); !ok {
56                         return false
57                 } else {
58                         return bool(v)
59                 }
60         }
61 }
62
63 // BufMapKey maps an event to an action
64 func BufMapEvent(k Event, action string) {
65         config.Bindings["buffer"][k.Name()] = action
66
67         switch e := k.(type) {
68         case KeyEvent, KeySequenceEvent, RawEvent:
69                 bufMapKey(e, action)
70         case MouseEvent:
71                 bufMapMouse(e, action)
72         }
73 }
74
75 func bufMapKey(k Event, action string) {
76         var actionfns []func(*BufPane) bool
77         var names []string
78         var types []byte
79         for i := 0; ; i++ {
80                 if action == "" {
81                         break
82                 }
83
84                 // TODO: fix problem when complex bindings have these
85                 // characters (escape them?)
86                 idx := strings.IndexAny(action, "&|,")
87                 a := action
88                 if idx >= 0 {
89                         a = action[:idx]
90                         types = append(types, action[idx])
91                         action = action[idx+1:]
92                 } else {
93                         types = append(types, ' ')
94                         action = ""
95                 }
96
97                 var afn func(*BufPane) bool
98                 if strings.HasPrefix(a, "command:") {
99                         a = strings.SplitN(a, ":", 2)[1]
100                         afn = CommandAction(a)
101                         names = append(names, "")
102                 } else if strings.HasPrefix(a, "command-edit:") {
103                         a = strings.SplitN(a, ":", 2)[1]
104                         afn = CommandEditAction(a)
105                         names = append(names, "")
106                 } else if strings.HasPrefix(a, "lua:") {
107                         a = strings.SplitN(a, ":", 2)[1]
108                         afn = LuaAction(a)
109                         if afn == nil {
110                                 screen.TermMessage("Lua Error:", a, "does not exist")
111                                 continue
112                         }
113                         split := strings.SplitN(a, ".", 2)
114                         if len(split) > 1 {
115                                 a = strings.Title(split[0]) + strings.Title(split[1])
116                         } else {
117                                 a = strings.Title(a)
118                         }
119
120                         names = append(names, a)
121                 } else if f, ok := BufKeyActions[a]; ok {
122                         afn = f
123                         names = append(names, a)
124                 } else {
125                         screen.TermMessage("Error in bindings: action", a, "does not exist")
126                         continue
127                 }
128                 actionfns = append(actionfns, afn)
129         }
130         bufAction := func(h *BufPane) bool {
131                 cursors := h.Buf.GetCursors()
132                 success := true
133                 for i, a := range actionfns {
134                         innerSuccess := true
135                         for j, c := range cursors {
136                                 if c == nil {
137                                         continue
138                                 }
139                                 h.Buf.SetCurCursor(c.Num)
140                                 h.Cursor = c
141                                 if i == 0 || (success && types[i-1] == '&') || (!success && types[i-1] == '|') || (types[i-1] == ',') {
142                                         innerSuccess = innerSuccess && h.execAction(a, names[i], j)
143                                 } else {
144                                         break
145                                 }
146                         }
147                         // if the action changed the current pane, update the reference
148                         h = MainTab().CurPane()
149                         success = innerSuccess
150                 }
151                 return true
152         }
153
154         BufBindings.RegisterKeyBinding(k, BufKeyActionGeneral(bufAction))
155 }
156
157 // BufMapMouse maps a mouse event to an action
158 func bufMapMouse(k MouseEvent, action string) {
159         if f, ok := BufMouseActions[action]; ok {
160                 BufBindings.RegisterMouseBinding(k, BufMouseActionGeneral(f))
161         } else {
162                 // TODO
163                 // delete(BufMouseBindings, k)
164                 bufMapKey(k, action)
165         }
166 }
167
168 // BufUnmap unmaps a key or mouse event from any action
169 func BufUnmap(k Event) {
170         // TODO
171         // delete(BufKeyBindings, k)
172         //
173         // switch e := k.(type) {
174         // case MouseEvent:
175         //      delete(BufMouseBindings, e)
176         // }
177 }
178
179 // The BufPane connects the buffer and the window
180 // It provides a cursor (or multiple) and defines a set of actions
181 // that can be taken on the buffer
182 // The ActionHandler can access the window for necessary info about
183 // visual positions for mouse clicks and scrolling
184 type BufPane struct {
185         display.BWindow
186
187         // Buf is the buffer this BufPane views
188         Buf *buffer.Buffer
189         // Bindings stores the association of key events and actions
190         bindings *KeyTree
191
192         // Cursor is the currently active buffer cursor
193         Cursor *buffer.Cursor
194
195         // Since tcell doesn't differentiate between a mouse release event
196         // and a mouse move event with no keys pressed, we need to keep
197         // track of whether or not the mouse was pressed (or not released) last event to determine
198         // mouse release events
199         mouseReleased bool
200
201         // We need to keep track of insert key press toggle
202         isOverwriteMode bool
203         // This stores when the last click was
204         // This is useful for detecting double and triple clicks
205         lastClickTime time.Time
206         lastLoc       buffer.Loc
207
208         // lastCutTime stores when the last ctrl+k was issued.
209         // It is used for clearing the clipboard to replace it with fresh cut lines.
210         lastCutTime time.Time
211
212         // freshClip returns true if the clipboard has never been pasted.
213         freshClip bool
214
215         // Was the last mouse event actually a double click?
216         // Useful for detecting triple clicks -- if a double click is detected
217         // but the last mouse event was actually a double click, it's a triple click
218         doubleClick bool
219         // Same here, just to keep track for mouse move events
220         tripleClick bool
221
222         // Last search stores the last successful search for FindNext and FindPrev
223         lastSearch      string
224         lastSearchRegex bool
225         // Should the current multiple cursor selection search based on word or
226         // based on selection (false for selection, true for word)
227         multiWord bool
228
229         splitID uint64
230         tab     *Tab
231
232         // remember original location of a search in case the search is canceled
233         searchOrig buffer.Loc
234 }
235
236 func NewBufPane(buf *buffer.Buffer, win display.BWindow, tab *Tab) *BufPane {
237         h := new(BufPane)
238         h.Buf = buf
239         h.BWindow = win
240         h.tab = tab
241
242         h.Cursor = h.Buf.GetActiveCursor()
243         h.mouseReleased = true
244
245         config.RunPluginFn("onBufPaneOpen", luar.New(ulua.L, h))
246
247         return h
248 }
249
250 func NewBufPaneFromBuf(buf *buffer.Buffer, tab *Tab) *BufPane {
251         w := display.NewBufWindow(0, 0, 0, 0, buf)
252         return NewBufPane(buf, w, tab)
253 }
254
255 func (h *BufPane) SetTab(t *Tab) {
256         h.tab = t
257 }
258
259 func (h *BufPane) Tab() *Tab {
260         return h.tab
261 }
262
263 func (h *BufPane) ResizePane(size int) {
264         n := h.tab.GetNode(h.splitID)
265         n.ResizeSplit(size)
266         h.tab.Resize()
267 }
268
269 // PluginCB calls all plugin callbacks with a certain name and
270 // displays an error if there is one and returns the aggregrate
271 // boolean response
272 func (h *BufPane) PluginCB(cb string) bool {
273         b, err := config.RunPluginFnBool(cb, luar.New(ulua.L, h))
274         if err != nil {
275                 screen.TermMessage(err)
276         }
277         return b
278 }
279
280 // PluginCBRune is the same as PluginCB but also passes a rune to
281 // the plugins
282 func (h *BufPane) PluginCBRune(cb string, r rune) bool {
283         b, err := config.RunPluginFnBool(cb, luar.New(ulua.L, h), luar.New(ulua.L, string(r)))
284         if err != nil {
285                 screen.TermMessage(err)
286         }
287         return b
288 }
289
290 func (h *BufPane) OpenBuffer(b *buffer.Buffer) {
291         h.Buf.Close()
292         h.Buf = b
293         h.BWindow.SetBuffer(b)
294         h.Cursor = b.GetActiveCursor()
295         h.Resize(h.GetView().Width, h.GetView().Height)
296         h.Relocate()
297         // Set mouseReleased to true because we assume the mouse is not being pressed when
298         // the editor is opened
299         h.mouseReleased = true
300         // Set isOverwriteMode to false, because we assume we are in the default mode when editor
301         // is opened
302         h.isOverwriteMode = false
303         h.lastClickTime = time.Time{}
304 }
305
306 func (h *BufPane) ID() uint64 {
307         return h.splitID
308 }
309
310 func (h *BufPane) SetID(i uint64) {
311         h.splitID = i
312 }
313
314 func (h *BufPane) Name() string {
315         n := h.Buf.GetName()
316         if h.Buf.Modified() {
317                 n += " +"
318         }
319         return n
320 }
321
322 // HandleEvent executes the tcell event properly
323 func (h *BufPane) HandleEvent(event tcell.Event) {
324         if h.Buf.ExternallyModified() && !h.Buf.ReloadDisabled {
325                 InfoBar.YNPrompt("The file on disk has changed. Reload file? (y,n,esc)", func(yes, canceled bool) {
326                         if canceled {
327                                 h.Buf.DisableReload()
328                         }
329                         if !yes || canceled {
330                                 h.Buf.UpdateModTime()
331                         } else {
332                                 h.Buf.ReOpen()
333                         }
334                 })
335
336         }
337
338         switch e := event.(type) {
339         case *tcell.EventRaw:
340                 re := RawEvent{
341                         esc: e.EscSeq(),
342                 }
343                 h.DoKeyEvent(re)
344         case *tcell.EventPaste:
345                 h.paste(e.Text())
346                 h.Relocate()
347         case *tcell.EventKey:
348                 ke := KeyEvent{
349                         code: e.Key(),
350                         mod:  metaToAlt(e.Modifiers()),
351                         r:    e.Rune(),
352                 }
353
354                 done := h.DoKeyEvent(ke)
355                 if !done && e.Key() == tcell.KeyRune {
356                         h.DoRuneInsert(e.Rune())
357                 }
358         case *tcell.EventMouse:
359                 cancel := false
360                 switch e.Buttons() {
361                 case tcell.Button1:
362                         _, my := e.Position()
363                         if h.Buf.Type.Kind != buffer.BTInfo.Kind && h.Buf.Settings["statusline"].(bool) && my >= h.GetView().Y+h.GetView().Height-1 {
364                                 cancel = true
365                         }
366                 case tcell.ButtonNone:
367                         // Mouse event with no click
368                         if !h.mouseReleased {
369                                 // Mouse was just released
370
371                                 // mx, my := e.Position()
372                                 // mouseLoc := h.LocFromVisual(buffer.Loc{X: mx, Y: my})
373
374                                 // we could finish the selection based on the release location as described
375                                 // below but when the mouse click is within the scroll margin this will
376                                 // cause a scroll and selection even for a simple mouse click which is
377                                 // not good
378                                 // for terminals that don't support mouse motion events, selection via
379                                 // the mouse won't work but this is ok
380
381                                 // Relocating here isn't really necessary because the cursor will
382                                 // be in the right place from the last mouse event
383                                 // However, if we are running in a terminal that doesn't support mouse motion
384                                 // events, this still allows the user to make selections, except only after they
385                                 // release the mouse
386
387                                 // if !h.doubleClick && !h.tripleClick {
388                                 //      h.Cursor.SetSelectionEnd(h.Cursor.Loc)
389                                 // }
390                                 if h.Cursor.HasSelection() {
391                                         h.Cursor.CopySelection(clipboard.PrimaryReg)
392                                 }
393                                 h.mouseReleased = true
394                         }
395                 }
396
397                 if !cancel {
398                         me := MouseEvent{
399                                 btn: e.Buttons(),
400                                 mod: metaToAlt(e.Modifiers()),
401                         }
402                         h.DoMouseEvent(me, e)
403                 }
404         }
405         h.Buf.MergeCursors()
406
407         if h.IsActive() {
408                 // Display any gutter messages for this line
409                 c := h.Buf.GetActiveCursor()
410                 none := true
411                 for _, m := range h.Buf.Messages {
412                         if c.Y == m.Start.Y || c.Y == m.End.Y {
413                                 InfoBar.GutterMessage(m.Msg)
414                                 none = false
415                                 break
416                         }
417                 }
418                 if none && InfoBar.HasGutter {
419                         InfoBar.ClearGutter()
420                 }
421         }
422 }
423
424 func (h *BufPane) Bindings() *KeyTree {
425         if h.bindings != nil {
426                 return h.bindings
427         }
428         return BufBindings
429 }
430
431 // DoKeyEvent executes a key event by finding the action it is bound
432 // to and executing it (possibly multiple times for multiple cursors)
433 func (h *BufPane) DoKeyEvent(e Event) bool {
434         binds := h.Bindings()
435         action, more := binds.NextEvent(e, nil)
436         if action != nil && !more {
437                 action(h)
438                 binds.ResetEvents()
439                 return true
440         } else if action == nil && !more {
441                 binds.ResetEvents()
442         }
443         return more
444 }
445
446 func (h *BufPane) execAction(action func(*BufPane) bool, name string, cursor int) bool {
447         if name != "Autocomplete" && name != "CycleAutocompleteBack" {
448                 h.Buf.HasSuggestions = false
449         }
450
451         _, isMulti := MultiActions[name]
452         if (!isMulti && cursor == 0) || isMulti {
453                 if h.PluginCB("pre" + name) {
454                         success := action(h)
455                         success = success && h.PluginCB("on"+name)
456
457                         if isMulti {
458                                 if recording_macro {
459                                         if name != "ToggleMacro" && name != "PlayMacro" {
460                                                 curmacro = append(curmacro, action)
461                                         }
462                                 }
463                         }
464
465                         return success
466                 }
467         }
468
469         return false
470 }
471
472 func (h *BufPane) completeAction(action string) {
473         h.PluginCB("on" + action)
474 }
475
476 func (h *BufPane) HasKeyEvent(e Event) bool {
477         // TODO
478         return true
479         // _, ok := BufKeyBindings[e]
480         // return ok
481 }
482
483 // DoMouseEvent executes a mouse event by finding the action it is bound
484 // to and executing it
485 func (h *BufPane) DoMouseEvent(e MouseEvent, te *tcell.EventMouse) bool {
486         binds := h.Bindings()
487         action, _ := binds.NextEvent(e, te)
488         if action != nil {
489                 action(h)
490                 binds.ResetEvents()
491                 return true
492         }
493         // TODO
494         return false
495
496         // if action, ok := BufMouseBindings[e]; ok {
497         //      if action(h, te) {
498         //              h.Relocate()
499         //      }
500         //      return true
501         // } else if h.HasKeyEvent(e) {
502         //      return h.DoKeyEvent(e)
503         // }
504         // return false
505 }
506
507 // DoRuneInsert inserts a given rune into the current buffer
508 // (possibly multiple times for multiple cursors)
509 func (h *BufPane) DoRuneInsert(r rune) {
510         cursors := h.Buf.GetCursors()
511         for _, c := range cursors {
512                 // Insert a character
513                 h.Buf.SetCurCursor(c.Num)
514                 h.Cursor = c
515                 if !h.PluginCBRune("preRune", r) {
516                         continue
517                 }
518                 if c.HasSelection() {
519                         c.DeleteSelection()
520                         c.ResetSelection()
521                 }
522
523                 if h.isOverwriteMode {
524                         next := c.Loc
525                         next.X++
526                         h.Buf.Replace(c.Loc, next, string(r))
527                 } else {
528                         h.Buf.Insert(c.Loc, string(r))
529                 }
530                 if recording_macro {
531                         curmacro = append(curmacro, r)
532                 }
533                 h.Relocate()
534                 h.PluginCBRune("onRune", r)
535         }
536 }
537
538 func (h *BufPane) VSplitIndex(buf *buffer.Buffer, right bool) *BufPane {
539         e := NewBufPaneFromBuf(buf, h.tab)
540         e.splitID = MainTab().GetNode(h.splitID).VSplit(right)
541         MainTab().Panes = append(MainTab().Panes, e)
542         MainTab().Resize()
543         MainTab().SetActive(len(MainTab().Panes) - 1)
544         return e
545 }
546 func (h *BufPane) HSplitIndex(buf *buffer.Buffer, bottom bool) *BufPane {
547         e := NewBufPaneFromBuf(buf, h.tab)
548         e.splitID = MainTab().GetNode(h.splitID).HSplit(bottom)
549         MainTab().Panes = append(MainTab().Panes, e)
550         MainTab().Resize()
551         MainTab().SetActive(len(MainTab().Panes) - 1)
552         return e
553 }
554
555 func (h *BufPane) VSplitBuf(buf *buffer.Buffer) *BufPane {
556         return h.VSplitIndex(buf, h.Buf.Settings["splitright"].(bool))
557 }
558 func (h *BufPane) HSplitBuf(buf *buffer.Buffer) *BufPane {
559         return h.HSplitIndex(buf, h.Buf.Settings["splitbottom"].(bool))
560 }
561 func (h *BufPane) Close() {
562         h.Buf.Close()
563 }
564
565 func (h *BufPane) SetActive(b bool) {
566         h.BWindow.SetActive(b)
567         if b {
568                 // Display any gutter messages for this line
569                 c := h.Buf.GetActiveCursor()
570                 none := true
571                 for _, m := range h.Buf.Messages {
572                         if c.Y == m.Start.Y || c.Y == m.End.Y {
573                                 InfoBar.GutterMessage(m.Msg)
574                                 none = false
575                                 break
576                         }
577                 }
578                 if none && InfoBar.HasGutter {
579                         InfoBar.ClearGutter()
580                 }
581         }
582
583 }
584
585 // BufKeyActions contains the list of all possible key actions the bufhandler could execute
586 var BufKeyActions = map[string]BufKeyAction{
587         "CursorUp":                  (*BufPane).CursorUp,
588         "CursorDown":                (*BufPane).CursorDown,
589         "CursorPageUp":              (*BufPane).CursorPageUp,
590         "CursorPageDown":            (*BufPane).CursorPageDown,
591         "CursorLeft":                (*BufPane).CursorLeft,
592         "CursorRight":               (*BufPane).CursorRight,
593         "CursorStart":               (*BufPane).CursorStart,
594         "CursorEnd":                 (*BufPane).CursorEnd,
595         "SelectToStart":             (*BufPane).SelectToStart,
596         "SelectToEnd":               (*BufPane).SelectToEnd,
597         "SelectUp":                  (*BufPane).SelectUp,
598         "SelectDown":                (*BufPane).SelectDown,
599         "SelectLeft":                (*BufPane).SelectLeft,
600         "SelectRight":               (*BufPane).SelectRight,
601         "WordRight":                 (*BufPane).WordRight,
602         "WordLeft":                  (*BufPane).WordLeft,
603         "SelectWordRight":           (*BufPane).SelectWordRight,
604         "SelectWordLeft":            (*BufPane).SelectWordLeft,
605         "DeleteWordRight":           (*BufPane).DeleteWordRight,
606         "DeleteWordLeft":            (*BufPane).DeleteWordLeft,
607         "SelectLine":                (*BufPane).SelectLine,
608         "SelectToStartOfLine":       (*BufPane).SelectToStartOfLine,
609         "SelectToStartOfText":       (*BufPane).SelectToStartOfText,
610         "SelectToStartOfTextToggle": (*BufPane).SelectToStartOfTextToggle,
611         "SelectToEndOfLine":         (*BufPane).SelectToEndOfLine,
612         "ParagraphPrevious":         (*BufPane).ParagraphPrevious,
613         "ParagraphNext":             (*BufPane).ParagraphNext,
614         "InsertNewline":             (*BufPane).InsertNewline,
615         "Backspace":                 (*BufPane).Backspace,
616         "Delete":                    (*BufPane).Delete,
617         "InsertTab":                 (*BufPane).InsertTab,
618         "Save":                      (*BufPane).Save,
619         "SaveAll":                   (*BufPane).SaveAll,
620         "SaveAs":                    (*BufPane).SaveAs,
621         "Find":                      (*BufPane).Find,
622         "FindLiteral":               (*BufPane).FindLiteral,
623         "FindNext":                  (*BufPane).FindNext,
624         "FindPrevious":              (*BufPane).FindPrevious,
625         "Center":                    (*BufPane).Center,
626         "Undo":                      (*BufPane).Undo,
627         "Redo":                      (*BufPane).Redo,
628         "Copy":                      (*BufPane).Copy,
629         "CopyLine":                  (*BufPane).CopyLine,
630         "Cut":                       (*BufPane).Cut,
631         "CutLine":                   (*BufPane).CutLine,
632         "DuplicateLine":             (*BufPane).DuplicateLine,
633         "DeleteLine":                (*BufPane).DeleteLine,
634         "MoveLinesUp":               (*BufPane).MoveLinesUp,
635         "MoveLinesDown":             (*BufPane).MoveLinesDown,
636         "IndentSelection":           (*BufPane).IndentSelection,
637         "OutdentSelection":          (*BufPane).OutdentSelection,
638         "Autocomplete":              (*BufPane).Autocomplete,
639         "CycleAutocompleteBack":     (*BufPane).CycleAutocompleteBack,
640         "OutdentLine":               (*BufPane).OutdentLine,
641         "IndentLine":                (*BufPane).IndentLine,
642         "Paste":                     (*BufPane).Paste,
643         "PastePrimary":              (*BufPane).PastePrimary,
644         "SelectAll":                 (*BufPane).SelectAll,
645         "OpenFile":                  (*BufPane).OpenFile,
646         "Start":                     (*BufPane).Start,
647         "End":                       (*BufPane).End,
648         "PageUp":                    (*BufPane).PageUp,
649         "PageDown":                  (*BufPane).PageDown,
650         "SelectPageUp":              (*BufPane).SelectPageUp,
651         "SelectPageDown":            (*BufPane).SelectPageDown,
652         "HalfPageUp":                (*BufPane).HalfPageUp,
653         "HalfPageDown":              (*BufPane).HalfPageDown,
654         "StartOfText":               (*BufPane).StartOfText,
655         "StartOfTextToggle":         (*BufPane).StartOfTextToggle,
656         "StartOfLine":               (*BufPane).StartOfLine,
657         "EndOfLine":                 (*BufPane).EndOfLine,
658         "ToggleHelp":                (*BufPane).ToggleHelp,
659         "ToggleKeyMenu":             (*BufPane).ToggleKeyMenu,
660         "ToggleDiffGutter":          (*BufPane).ToggleDiffGutter,
661         "ToggleRuler":               (*BufPane).ToggleRuler,
662         "ClearStatus":               (*BufPane).ClearStatus,
663         "ShellMode":                 (*BufPane).ShellMode,
664         "CommandMode":               (*BufPane).CommandMode,
665         "ToggleOverwriteMode":       (*BufPane).ToggleOverwriteMode,
666         "Escape":                    (*BufPane).Escape,
667         "Quit":                      (*BufPane).Quit,
668         "QuitAll":                   (*BufPane).QuitAll,
669         "ForceQuit":                 (*BufPane).ForceQuit,
670         "AddTab":                    (*BufPane).AddTab,
671         "PreviousTab":               (*BufPane).PreviousTab,
672         "NextTab":                   (*BufPane).NextTab,
673         "NextSplit":                 (*BufPane).NextSplit,
674         "PreviousSplit":             (*BufPane).PreviousSplit,
675         "Unsplit":                   (*BufPane).Unsplit,
676         "VSplit":                    (*BufPane).VSplitAction,
677         "HSplit":                    (*BufPane).HSplitAction,
678         "ToggleMacro":               (*BufPane).ToggleMacro,
679         "PlayMacro":                 (*BufPane).PlayMacro,
680         "Suspend":                   (*BufPane).Suspend,
681         "ScrollUp":                  (*BufPane).ScrollUpAction,
682         "ScrollDown":                (*BufPane).ScrollDownAction,
683         "SpawnMultiCursor":          (*BufPane).SpawnMultiCursor,
684         "SpawnMultiCursorUp":        (*BufPane).SpawnMultiCursorUp,
685         "SpawnMultiCursorDown":      (*BufPane).SpawnMultiCursorDown,
686         "SpawnMultiCursorSelect":    (*BufPane).SpawnMultiCursorSelect,
687         "RemoveMultiCursor":         (*BufPane).RemoveMultiCursor,
688         "RemoveAllMultiCursors":     (*BufPane).RemoveAllMultiCursors,
689         "SkipMultiCursor":           (*BufPane).SkipMultiCursor,
690         "JumpToMatchingBrace":       (*BufPane).JumpToMatchingBrace,
691         "JumpLine":                  (*BufPane).JumpLine,
692         "Deselect":                  (*BufPane).Deselect,
693         "ClearInfo":                 (*BufPane).ClearInfo,
694         "None":                      (*BufPane).None,
695
696         // This was changed to InsertNewline but I don't want to break backwards compatibility
697         "InsertEnter": (*BufPane).InsertNewline,
698 }
699
700 // BufMouseActions contains the list of all possible mouse actions the bufhandler could execute
701 var BufMouseActions = map[string]BufMouseAction{
702         "MousePress":       (*BufPane).MousePress,
703         "MouseMultiCursor": (*BufPane).MouseMultiCursor,
704 }
705
706 // MultiActions is a list of actions that should be executed multiple
707 // times if there are multiple cursors (one per cursor)
708 // Generally actions that modify global editor state like quitting or
709 // saving should not be included in this list
710 var MultiActions = map[string]bool{
711         "CursorUp":                  true,
712         "CursorDown":                true,
713         "CursorPageUp":              true,
714         "CursorPageDown":            true,
715         "CursorLeft":                true,
716         "CursorRight":               true,
717         "CursorStart":               true,
718         "CursorEnd":                 true,
719         "SelectToStart":             true,
720         "SelectToEnd":               true,
721         "SelectUp":                  true,
722         "SelectDown":                true,
723         "SelectLeft":                true,
724         "SelectRight":               true,
725         "WordRight":                 true,
726         "WordLeft":                  true,
727         "SelectWordRight":           true,
728         "SelectWordLeft":            true,
729         "DeleteWordRight":           true,
730         "DeleteWordLeft":            true,
731         "SelectLine":                true,
732         "SelectToStartOfLine":       true,
733         "SelectToStartOfText":       true,
734         "SelectToStartOfTextToggle": true,
735         "SelectToEndOfLine":         true,
736         "ParagraphPrevious":         true,
737         "ParagraphNext":             true,
738         "InsertNewline":             true,
739         "Backspace":                 true,
740         "Delete":                    true,
741         "InsertTab":                 true,
742         "FindNext":                  true,
743         "FindPrevious":              true,
744         "CopyLine":                  true,
745         "Copy":                      true,
746         "Cut":                       true,
747         "CutLine":                   true,
748         "DuplicateLine":             true,
749         "DeleteLine":                true,
750         "MoveLinesUp":               true,
751         "MoveLinesDown":             true,
752         "IndentSelection":           true,
753         "OutdentSelection":          true,
754         "OutdentLine":               true,
755         "IndentLine":                true,
756         "Paste":                     true,
757         "PastePrimary":              true,
758         "SelectPageUp":              true,
759         "SelectPageDown":            true,
760         "StartOfLine":               true,
761         "StartOfText":               true,
762         "StartOfTextToggle":         true,
763         "EndOfLine":                 true,
764         "JumpToMatchingBrace":       true,
765 }