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