]> git.lizzy.rs Git - micro.git/blob - internal/action/bufpane.go
Support for highlighting all search matches (hlsearch) (#1762)
[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         // Should the current multiple cursor selection search based on word or
230         // based on selection (false for selection, true for word)
231         multiWord bool
232
233         splitID uint64
234         tab     *Tab
235
236         // remember original location of a search in case the search is canceled
237         searchOrig buffer.Loc
238 }
239
240 // NewBufPane creates a new buffer pane with the given window.
241 func NewBufPane(buf *buffer.Buffer, win display.BWindow, tab *Tab) *BufPane {
242         h := new(BufPane)
243         h.Buf = buf
244         h.BWindow = win
245         h.tab = tab
246
247         h.Cursor = h.Buf.GetActiveCursor()
248         h.mouseReleased = true
249
250         config.RunPluginFn("onBufPaneOpen", luar.New(ulua.L, h))
251
252         return h
253 }
254
255 // NewBufPaneFromBuf constructs a new pane from the given buffer and automatically
256 // creates a buf window.
257 func NewBufPaneFromBuf(buf *buffer.Buffer, tab *Tab) *BufPane {
258         w := display.NewBufWindow(0, 0, 0, 0, buf)
259         return NewBufPane(buf, w, tab)
260 }
261
262 // SetTab sets this pane's tab.
263 func (h *BufPane) SetTab(t *Tab) {
264         h.tab = t
265 }
266
267 // Tab returns this pane's tab.
268 func (h *BufPane) Tab() *Tab {
269         return h.tab
270 }
271
272 func (h *BufPane) ResizePane(size int) {
273         n := h.tab.GetNode(h.splitID)
274         n.ResizeSplit(size)
275         h.tab.Resize()
276 }
277
278 // PluginCB calls all plugin callbacks with a certain name and displays an
279 // error if there is one and returns the aggregrate boolean response
280 func (h *BufPane) PluginCB(cb string) bool {
281         b, err := config.RunPluginFnBool(cb, luar.New(ulua.L, h))
282         if err != nil {
283                 screen.TermMessage(err)
284         }
285         return b
286 }
287
288 // PluginCBRune is the same as PluginCB but also passes a rune to the plugins
289 func (h *BufPane) PluginCBRune(cb string, r rune) bool {
290         b, err := config.RunPluginFnBool(cb, luar.New(ulua.L, h), luar.New(ulua.L, string(r)))
291         if err != nil {
292                 screen.TermMessage(err)
293         }
294         return b
295 }
296
297 // OpenBuffer opens the given buffer in this pane.
298 func (h *BufPane) OpenBuffer(b *buffer.Buffer) {
299         h.Buf.Close()
300         h.Buf = b
301         h.BWindow.SetBuffer(b)
302         h.Cursor = b.GetActiveCursor()
303         h.Resize(h.GetView().Width, h.GetView().Height)
304         h.Relocate()
305         // Set mouseReleased to true because we assume the mouse is not being
306         // pressed when the editor is opened
307         h.mouseReleased = true
308         // Set isOverwriteMode to false, because we assume we are in the default
309         // mode when editor is opened
310         h.isOverwriteMode = false
311         h.lastClickTime = time.Time{}
312 }
313
314 // ID returns this pane's split id.
315 func (h *BufPane) ID() uint64 {
316         return h.splitID
317 }
318
319 // SetID sets the split ID of this pane.
320 func (h *BufPane) SetID(i uint64) {
321         h.splitID = i
322 }
323
324 // Name returns the BufPane's name.
325 func (h *BufPane) Name() string {
326         n := h.Buf.GetName()
327         if h.Buf.Modified() {
328                 n += " +"
329         }
330         return n
331 }
332
333 // HandleEvent executes the tcell event properly
334 func (h *BufPane) HandleEvent(event tcell.Event) {
335         if h.Buf.ExternallyModified() && !h.Buf.ReloadDisabled {
336                 InfoBar.YNPrompt("The file on disk has changed. Reload file? (y,n,esc)", func(yes, canceled bool) {
337                         if canceled {
338                                 h.Buf.DisableReload()
339                         }
340                         if !yes || canceled {
341                                 h.Buf.UpdateModTime()
342                         } else {
343                                 h.Buf.ReOpen()
344                         }
345                 })
346
347         }
348
349         switch e := event.(type) {
350         case *tcell.EventRaw:
351                 re := RawEvent{
352                         esc: e.EscSeq(),
353                 }
354                 h.DoKeyEvent(re)
355         case *tcell.EventPaste:
356                 h.paste(e.Text())
357                 h.Relocate()
358         case *tcell.EventKey:
359                 ke := KeyEvent{
360                         code: e.Key(),
361                         mod:  metaToAlt(e.Modifiers()),
362                         r:    e.Rune(),
363                 }
364
365                 done := h.DoKeyEvent(ke)
366                 if !done && e.Key() == tcell.KeyRune {
367                         h.DoRuneInsert(e.Rune())
368                 }
369         case *tcell.EventMouse:
370                 cancel := false
371                 switch e.Buttons() {
372                 case tcell.Button1:
373                         _, my := e.Position()
374                         if h.Buf.Type.Kind != buffer.BTInfo.Kind && h.Buf.Settings["statusline"].(bool) && my >= h.GetView().Y+h.GetView().Height-1 {
375                                 cancel = true
376                         }
377                 case tcell.ButtonNone:
378                         // Mouse event with no click
379                         if !h.mouseReleased {
380                                 // Mouse was just released
381
382                                 // mx, my := e.Position()
383                                 // mouseLoc := h.LocFromVisual(buffer.Loc{X: mx, Y: my})
384
385                                 // we could finish the selection based on the release location as described
386                                 // below but when the mouse click is within the scroll margin this will
387                                 // cause a scroll and selection even for a simple mouse click which is
388                                 // not good
389                                 // for terminals that don't support mouse motion events, selection via
390                                 // the mouse won't work but this is ok
391
392                                 // Relocating here isn't really necessary because the cursor will
393                                 // be in the right place from the last mouse event
394                                 // However, if we are running in a terminal that doesn't support mouse motion
395                                 // events, this still allows the user to make selections, except only after they
396                                 // release the mouse
397
398                                 // if !h.doubleClick && !h.tripleClick {
399                                 //      h.Cursor.SetSelectionEnd(h.Cursor.Loc)
400                                 // }
401                                 if h.Cursor.HasSelection() {
402                                         h.Cursor.CopySelection(clipboard.PrimaryReg)
403                                 }
404                                 h.mouseReleased = true
405                         }
406                 }
407
408                 if !cancel {
409                         me := MouseEvent{
410                                 btn: e.Buttons(),
411                                 mod: metaToAlt(e.Modifiers()),
412                         }
413                         h.DoMouseEvent(me, e)
414                 }
415         }
416         h.Buf.MergeCursors()
417
418         if h.IsActive() {
419                 // Display any gutter messages for this line
420                 c := h.Buf.GetActiveCursor()
421                 none := true
422                 for _, m := range h.Buf.Messages {
423                         if c.Y == m.Start.Y || c.Y == m.End.Y {
424                                 InfoBar.GutterMessage(m.Msg)
425                                 none = false
426                                 break
427                         }
428                 }
429                 if none && InfoBar.HasGutter {
430                         InfoBar.ClearGutter()
431                 }
432         }
433 }
434
435 // Bindings returns the current bindings tree for this buffer.
436 func (h *BufPane) Bindings() *KeyTree {
437         if h.bindings != nil {
438                 return h.bindings
439         }
440         return BufBindings
441 }
442
443 // DoKeyEvent executes a key event by finding the action it is bound
444 // to and executing it (possibly multiple times for multiple cursors)
445 func (h *BufPane) DoKeyEvent(e Event) bool {
446         binds := h.Bindings()
447         action, more := binds.NextEvent(e, nil)
448         if action != nil && !more {
449                 action(h)
450                 binds.ResetEvents()
451                 return true
452         } else if action == nil && !more {
453                 binds.ResetEvents()
454         }
455         return more
456 }
457
458 func (h *BufPane) execAction(action func(*BufPane) bool, name string, cursor int) bool {
459         if name != "Autocomplete" && name != "CycleAutocompleteBack" {
460                 h.Buf.HasSuggestions = false
461         }
462
463         _, isMulti := MultiActions[name]
464         if (!isMulti && cursor == 0) || isMulti {
465                 if h.PluginCB("pre" + name) {
466                         success := action(h)
467                         success = success && h.PluginCB("on"+name)
468
469                         if isMulti {
470                                 if recordingMacro {
471                                         if name != "ToggleMacro" && name != "PlayMacro" {
472                                                 curmacro = append(curmacro, action)
473                                         }
474                                 }
475                         }
476
477                         return success
478                 }
479         }
480
481         return false
482 }
483
484 func (h *BufPane) completeAction(action string) {
485         h.PluginCB("on" + action)
486 }
487
488 func (h *BufPane) HasKeyEvent(e Event) bool {
489         // TODO
490         return true
491         // _, ok := BufKeyBindings[e]
492         // return ok
493 }
494
495 // DoMouseEvent executes a mouse event by finding the action it is bound
496 // to and executing it
497 func (h *BufPane) DoMouseEvent(e MouseEvent, te *tcell.EventMouse) bool {
498         binds := h.Bindings()
499         action, _ := binds.NextEvent(e, te)
500         if action != nil {
501                 action(h)
502                 binds.ResetEvents()
503                 return true
504         }
505         // TODO
506         return false
507
508         // if action, ok := BufMouseBindings[e]; ok {
509         //      if action(h, te) {
510         //              h.Relocate()
511         //      }
512         //      return true
513         // } else if h.HasKeyEvent(e) {
514         //      return h.DoKeyEvent(e)
515         // }
516         // return false
517 }
518
519 // DoRuneInsert inserts a given rune into the current buffer
520 // (possibly multiple times for multiple cursors)
521 func (h *BufPane) DoRuneInsert(r rune) {
522         cursors := h.Buf.GetCursors()
523         for _, c := range cursors {
524                 // Insert a character
525                 h.Buf.SetCurCursor(c.Num)
526                 h.Cursor = c
527                 if !h.PluginCBRune("preRune", r) {
528                         continue
529                 }
530                 if c.HasSelection() {
531                         c.DeleteSelection()
532                         c.ResetSelection()
533                 }
534
535                 if h.isOverwriteMode {
536                         next := c.Loc
537                         next.X++
538                         h.Buf.Replace(c.Loc, next, string(r))
539                 } else {
540                         h.Buf.Insert(c.Loc, string(r))
541                 }
542                 if recordingMacro {
543                         curmacro = append(curmacro, r)
544                 }
545                 h.Relocate()
546                 h.PluginCBRune("onRune", r)
547         }
548 }
549
550 // VSplitIndex opens the given buffer in a vertical split on the given side.
551 func (h *BufPane) VSplitIndex(buf *buffer.Buffer, right bool) *BufPane {
552         e := NewBufPaneFromBuf(buf, h.tab)
553         e.splitID = MainTab().GetNode(h.splitID).VSplit(right)
554         MainTab().Panes = append(MainTab().Panes, e)
555         MainTab().Resize()
556         MainTab().SetActive(len(MainTab().Panes) - 1)
557         return e
558 }
559
560 // HSplitIndex opens the given buffer in a horizontal split on the given side.
561 func (h *BufPane) HSplitIndex(buf *buffer.Buffer, bottom bool) *BufPane {
562         e := NewBufPaneFromBuf(buf, h.tab)
563         e.splitID = MainTab().GetNode(h.splitID).HSplit(bottom)
564         MainTab().Panes = append(MainTab().Panes, e)
565         MainTab().Resize()
566         MainTab().SetActive(len(MainTab().Panes) - 1)
567         return e
568 }
569
570 // VSplitBuf opens the given buffer in a new vertical split.
571 func (h *BufPane) VSplitBuf(buf *buffer.Buffer) *BufPane {
572         return h.VSplitIndex(buf, h.Buf.Settings["splitright"].(bool))
573 }
574
575 // HSplitBuf opens the given buffer in a new horizontal split.
576 func (h *BufPane) HSplitBuf(buf *buffer.Buffer) *BufPane {
577         return h.HSplitIndex(buf, h.Buf.Settings["splitbottom"].(bool))
578 }
579
580 // Close this pane.
581 func (h *BufPane) Close() {
582         h.Buf.Close()
583 }
584
585 // SetActive marks this pane as active.
586 func (h *BufPane) SetActive(b bool) {
587         h.BWindow.SetActive(b)
588         if b {
589                 // Display any gutter messages for this line
590                 c := h.Buf.GetActiveCursor()
591                 none := true
592                 for _, m := range h.Buf.Messages {
593                         if c.Y == m.Start.Y || c.Y == m.End.Y {
594                                 InfoBar.GutterMessage(m.Msg)
595                                 none = false
596                                 break
597                         }
598                 }
599                 if none && InfoBar.HasGutter {
600                         InfoBar.ClearGutter()
601                 }
602         }
603
604 }
605
606 // BufKeyActions contains the list of all possible key actions the bufhandler could execute
607 var BufKeyActions = map[string]BufKeyAction{
608         "CursorUp":                  (*BufPane).CursorUp,
609         "CursorDown":                (*BufPane).CursorDown,
610         "CursorPageUp":              (*BufPane).CursorPageUp,
611         "CursorPageDown":            (*BufPane).CursorPageDown,
612         "CursorLeft":                (*BufPane).CursorLeft,
613         "CursorRight":               (*BufPane).CursorRight,
614         "CursorStart":               (*BufPane).CursorStart,
615         "CursorEnd":                 (*BufPane).CursorEnd,
616         "SelectToStart":             (*BufPane).SelectToStart,
617         "SelectToEnd":               (*BufPane).SelectToEnd,
618         "SelectUp":                  (*BufPane).SelectUp,
619         "SelectDown":                (*BufPane).SelectDown,
620         "SelectLeft":                (*BufPane).SelectLeft,
621         "SelectRight":               (*BufPane).SelectRight,
622         "WordRight":                 (*BufPane).WordRight,
623         "WordLeft":                  (*BufPane).WordLeft,
624         "SelectWordRight":           (*BufPane).SelectWordRight,
625         "SelectWordLeft":            (*BufPane).SelectWordLeft,
626         "DeleteWordRight":           (*BufPane).DeleteWordRight,
627         "DeleteWordLeft":            (*BufPane).DeleteWordLeft,
628         "SelectLine":                (*BufPane).SelectLine,
629         "SelectToStartOfLine":       (*BufPane).SelectToStartOfLine,
630         "SelectToStartOfText":       (*BufPane).SelectToStartOfText,
631         "SelectToStartOfTextToggle": (*BufPane).SelectToStartOfTextToggle,
632         "SelectToEndOfLine":         (*BufPane).SelectToEndOfLine,
633         "ParagraphPrevious":         (*BufPane).ParagraphPrevious,
634         "ParagraphNext":             (*BufPane).ParagraphNext,
635         "InsertNewline":             (*BufPane).InsertNewline,
636         "Backspace":                 (*BufPane).Backspace,
637         "Delete":                    (*BufPane).Delete,
638         "InsertTab":                 (*BufPane).InsertTab,
639         "Save":                      (*BufPane).Save,
640         "SaveAll":                   (*BufPane).SaveAll,
641         "SaveAs":                    (*BufPane).SaveAs,
642         "Find":                      (*BufPane).Find,
643         "FindLiteral":               (*BufPane).FindLiteral,
644         "FindNext":                  (*BufPane).FindNext,
645         "FindPrevious":              (*BufPane).FindPrevious,
646         "Center":                    (*BufPane).Center,
647         "Undo":                      (*BufPane).Undo,
648         "Redo":                      (*BufPane).Redo,
649         "Copy":                      (*BufPane).Copy,
650         "CopyLine":                  (*BufPane).CopyLine,
651         "Cut":                       (*BufPane).Cut,
652         "CutLine":                   (*BufPane).CutLine,
653         "DuplicateLine":             (*BufPane).DuplicateLine,
654         "DeleteLine":                (*BufPane).DeleteLine,
655         "MoveLinesUp":               (*BufPane).MoveLinesUp,
656         "MoveLinesDown":             (*BufPane).MoveLinesDown,
657         "IndentSelection":           (*BufPane).IndentSelection,
658         "OutdentSelection":          (*BufPane).OutdentSelection,
659         "Autocomplete":              (*BufPane).Autocomplete,
660         "CycleAutocompleteBack":     (*BufPane).CycleAutocompleteBack,
661         "OutdentLine":               (*BufPane).OutdentLine,
662         "IndentLine":                (*BufPane).IndentLine,
663         "Paste":                     (*BufPane).Paste,
664         "PastePrimary":              (*BufPane).PastePrimary,
665         "SelectAll":                 (*BufPane).SelectAll,
666         "OpenFile":                  (*BufPane).OpenFile,
667         "Start":                     (*BufPane).Start,
668         "End":                       (*BufPane).End,
669         "PageUp":                    (*BufPane).PageUp,
670         "PageDown":                  (*BufPane).PageDown,
671         "SelectPageUp":              (*BufPane).SelectPageUp,
672         "SelectPageDown":            (*BufPane).SelectPageDown,
673         "HalfPageUp":                (*BufPane).HalfPageUp,
674         "HalfPageDown":              (*BufPane).HalfPageDown,
675         "StartOfText":               (*BufPane).StartOfText,
676         "StartOfTextToggle":         (*BufPane).StartOfTextToggle,
677         "StartOfLine":               (*BufPane).StartOfLine,
678         "EndOfLine":                 (*BufPane).EndOfLine,
679         "ToggleHelp":                (*BufPane).ToggleHelp,
680         "ToggleKeyMenu":             (*BufPane).ToggleKeyMenu,
681         "ToggleDiffGutter":          (*BufPane).ToggleDiffGutter,
682         "ToggleRuler":               (*BufPane).ToggleRuler,
683         "ToggleHighlightSearch":     (*BufPane).ToggleHighlightSearch,
684         "UnhighlightSearch":         (*BufPane).UnhighlightSearch,
685         "ClearStatus":               (*BufPane).ClearStatus,
686         "ShellMode":                 (*BufPane).ShellMode,
687         "CommandMode":               (*BufPane).CommandMode,
688         "ToggleOverwriteMode":       (*BufPane).ToggleOverwriteMode,
689         "Escape":                    (*BufPane).Escape,
690         "Quit":                      (*BufPane).Quit,
691         "QuitAll":                   (*BufPane).QuitAll,
692         "ForceQuit":                 (*BufPane).ForceQuit,
693         "AddTab":                    (*BufPane).AddTab,
694         "PreviousTab":               (*BufPane).PreviousTab,
695         "NextTab":                   (*BufPane).NextTab,
696         "NextSplit":                 (*BufPane).NextSplit,
697         "PreviousSplit":             (*BufPane).PreviousSplit,
698         "Unsplit":                   (*BufPane).Unsplit,
699         "VSplit":                    (*BufPane).VSplitAction,
700         "HSplit":                    (*BufPane).HSplitAction,
701         "ToggleMacro":               (*BufPane).ToggleMacro,
702         "PlayMacro":                 (*BufPane).PlayMacro,
703         "Suspend":                   (*BufPane).Suspend,
704         "ScrollUp":                  (*BufPane).ScrollUpAction,
705         "ScrollDown":                (*BufPane).ScrollDownAction,
706         "SpawnMultiCursor":          (*BufPane).SpawnMultiCursor,
707         "SpawnMultiCursorUp":        (*BufPane).SpawnMultiCursorUp,
708         "SpawnMultiCursorDown":      (*BufPane).SpawnMultiCursorDown,
709         "SpawnMultiCursorSelect":    (*BufPane).SpawnMultiCursorSelect,
710         "RemoveMultiCursor":         (*BufPane).RemoveMultiCursor,
711         "RemoveAllMultiCursors":     (*BufPane).RemoveAllMultiCursors,
712         "SkipMultiCursor":           (*BufPane).SkipMultiCursor,
713         "JumpToMatchingBrace":       (*BufPane).JumpToMatchingBrace,
714         "JumpLine":                  (*BufPane).JumpLine,
715         "Deselect":                  (*BufPane).Deselect,
716         "ClearInfo":                 (*BufPane).ClearInfo,
717         "None":                      (*BufPane).None,
718
719         // This was changed to InsertNewline but I don't want to break backwards compatibility
720         "InsertEnter": (*BufPane).InsertNewline,
721 }
722
723 // BufMouseActions contains the list of all possible mouse actions the bufhandler could execute
724 var BufMouseActions = map[string]BufMouseAction{
725         "MousePress":       (*BufPane).MousePress,
726         "MouseMultiCursor": (*BufPane).MouseMultiCursor,
727 }
728
729 // MultiActions is a list of actions that should be executed multiple
730 // times if there are multiple cursors (one per cursor)
731 // Generally actions that modify global editor state like quitting or
732 // saving should not be included in this list
733 var MultiActions = map[string]bool{
734         "CursorUp":                  true,
735         "CursorDown":                true,
736         "CursorPageUp":              true,
737         "CursorPageDown":            true,
738         "CursorLeft":                true,
739         "CursorRight":               true,
740         "CursorStart":               true,
741         "CursorEnd":                 true,
742         "SelectToStart":             true,
743         "SelectToEnd":               true,
744         "SelectUp":                  true,
745         "SelectDown":                true,
746         "SelectLeft":                true,
747         "SelectRight":               true,
748         "WordRight":                 true,
749         "WordLeft":                  true,
750         "SelectWordRight":           true,
751         "SelectWordLeft":            true,
752         "DeleteWordRight":           true,
753         "DeleteWordLeft":            true,
754         "SelectLine":                true,
755         "SelectToStartOfLine":       true,
756         "SelectToStartOfText":       true,
757         "SelectToStartOfTextToggle": true,
758         "SelectToEndOfLine":         true,
759         "ParagraphPrevious":         true,
760         "ParagraphNext":             true,
761         "InsertNewline":             true,
762         "Backspace":                 true,
763         "Delete":                    true,
764         "InsertTab":                 true,
765         "FindNext":                  true,
766         "FindPrevious":              true,
767         "CopyLine":                  true,
768         "Copy":                      true,
769         "Cut":                       true,
770         "CutLine":                   true,
771         "DuplicateLine":             true,
772         "DeleteLine":                true,
773         "MoveLinesUp":               true,
774         "MoveLinesDown":             true,
775         "IndentSelection":           true,
776         "OutdentSelection":          true,
777         "OutdentLine":               true,
778         "IndentLine":                true,
779         "Paste":                     true,
780         "PastePrimary":              true,
781         "SelectPageUp":              true,
782         "SelectPageDown":            true,
783         "StartOfLine":               true,
784         "StartOfText":               true,
785         "StartOfTextToggle":         true,
786         "EndOfLine":                 true,
787         "JumpToMatchingBrace":       true,
788 }