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