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