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