]> git.lizzy.rs Git - micro.git/blob - cmd/micro/action/bufhandler.go
0ffa514fb906d315cc1531a42a6fbe195dca22a0
[micro.git] / cmd / micro / action / bufhandler.go
1 package action
2
3 import (
4         "time"
5
6         "github.com/zyedidia/micro/cmd/micro/buffer"
7         "github.com/zyedidia/micro/cmd/micro/display"
8         "github.com/zyedidia/micro/cmd/micro/util"
9         "github.com/zyedidia/tcell"
10 )
11
12 type BufKeyAction func(*BufHandler) bool
13 type BufMouseAction func(*BufHandler, *tcell.EventMouse) bool
14
15 var BufKeyBindings map[Event]BufKeyAction
16 var BufKeyStrings map[Event]string
17 var BufMouseBindings map[MouseEvent]BufMouseAction
18
19 func init() {
20         BufKeyBindings = make(map[Event]BufKeyAction)
21         BufKeyStrings = make(map[Event]string)
22         BufMouseBindings = make(map[MouseEvent]BufMouseAction)
23 }
24
25 // BufMapKey maps a key event to an action
26 func BufMapKey(k Event, action string) {
27         if f, ok := BufKeyActions[action]; ok {
28                 BufKeyStrings[k] = action
29                 BufKeyBindings[k] = f
30         } else {
31                 util.TermMessage("Error:", action, "does not exist")
32         }
33 }
34
35 // BufMapMouse maps a mouse event to an action
36 func BufMapMouse(k MouseEvent, action string) {
37         if f, ok := BufMouseActions[action]; ok {
38                 BufMouseBindings[k] = f
39         } else if f, ok := BufKeyActions[action]; ok {
40                 // allowed to map mouse buttons to key actions
41                 BufKeyStrings[k] = action
42                 BufKeyBindings[k] = f
43                 // ensure we don't double bind a key
44                 delete(BufMouseBindings, k)
45         } else {
46                 util.TermMessage("Error:", action, "does not exist")
47         }
48 }
49
50 // The BufHandler connects the buffer and the window
51 // It provides a cursor (or multiple) and defines a set of actions
52 // that can be taken on the buffer
53 // The ActionHandler can access the window for necessary info about
54 // visual positions for mouse clicks and scrolling
55 type BufHandler struct {
56         display.Window
57
58         Buf *buffer.Buffer
59
60         cursors []*buffer.Cursor
61         Cursor  *buffer.Cursor // the active cursor
62
63         StartLine int // Vertical scrolling
64         StartCol  int // Horizontal scrolling
65
66         // Since tcell doesn't differentiate between a mouse release event
67         // and a mouse move event with no keys pressed, we need to keep
68         // track of whether or not the mouse was pressed (or not released) last event to determine
69         // mouse release events
70         mouseReleased bool
71
72         // We need to keep track of insert key press toggle
73         isOverwriteMode bool
74         // This stores when the last click was
75         // This is useful for detecting double and triple clicks
76         lastClickTime time.Time
77         lastLoc       buffer.Loc
78
79         // lastCutTime stores when the last ctrl+k was issued.
80         // It is used for clearing the clipboard to replace it with fresh cut lines.
81         lastCutTime time.Time
82
83         // freshClip returns true if the clipboard has never been pasted.
84         freshClip bool
85
86         // Was the last mouse event actually a double click?
87         // Useful for detecting triple clicks -- if a double click is detected
88         // but the last mouse event was actually a double click, it's a triple click
89         doubleClick bool
90         // Same here, just to keep track for mouse move events
91         tripleClick bool
92
93         // Last search stores the last successful search for FindNext and FindPrev
94         lastSearch string
95         // Should the current multiple cursor selection search based on word or
96         // based on selection (false for selection, true for word)
97         multiWord bool
98
99         splitID uint64
100 }
101
102 func NewBufHandler(buf *buffer.Buffer, win display.Window) *BufHandler {
103         h := new(BufHandler)
104         h.Buf = buf
105         h.Window = win
106
107         h.cursors = []*buffer.Cursor{buffer.NewCursor(buf, buf.StartCursor)}
108         h.Cursor = h.cursors[0]
109         h.mouseReleased = true
110
111         buf.SetCursors(h.cursors)
112         return h
113 }
114
115 func (h *BufHandler) ID() uint64 {
116         return h.splitID
117 }
118
119 func (h *BufHandler) Name() string {
120         return h.Buf.GetName()
121 }
122
123 // HandleEvent executes the tcell event properly
124 // TODO: multiple actions bound to one key
125 func (h *BufHandler) HandleEvent(event tcell.Event) {
126         switch e := event.(type) {
127         case *tcell.EventRaw:
128                 re := RawEvent{
129                         esc: e.EscSeq(),
130                 }
131                 h.DoKeyEvent(re)
132         case *tcell.EventKey:
133                 ke := KeyEvent{
134                         code: e.Key(),
135                         mod:  e.Modifiers(),
136                         r:    e.Rune(),
137                 }
138
139                 done := h.DoKeyEvent(ke)
140                 if !done && e.Key() == tcell.KeyRune {
141                         h.DoRuneInsert(e.Rune())
142                 }
143         case *tcell.EventMouse:
144                 switch e.Buttons() {
145                 case tcell.ButtonNone:
146                         // Mouse event with no click
147                         if !h.mouseReleased {
148                                 // Mouse was just released
149
150                                 mx, my := e.Position()
151                                 mouseLoc := h.GetMouseLoc(buffer.Loc{X: mx, Y: my})
152
153                                 // Relocating here isn't really necessary because the cursor will
154                                 // be in the right place from the last mouse event
155                                 // However, if we are running in a terminal that doesn't support mouse motion
156                                 // events, this still allows the user to make selections, except only after they
157                                 // release the mouse
158
159                                 if !h.doubleClick && !h.tripleClick {
160                                         h.Cursor.Loc = mouseLoc
161                                         h.Cursor.SetSelectionEnd(h.Cursor.Loc)
162                                         h.Cursor.CopySelection("primary")
163                                 }
164                                 h.mouseReleased = true
165                         }
166                 }
167
168                 me := MouseEvent{
169                         btn: e.Buttons(),
170                         mod: e.Modifiers(),
171                 }
172                 h.DoMouseEvent(me, e)
173         }
174         h.Buf.MergeCursors()
175
176         // Display any gutter messages for this line
177         c := h.Buf.GetActiveCursor()
178         none := true
179         for _, m := range h.Buf.Messages {
180                 if c.Y == m.Start.Y || c.Y == m.End.Y {
181                         InfoBar.GutterMessage(m.Msg)
182                         none = false
183                         break
184                 }
185         }
186         if none && InfoBar.HasGutter {
187                 InfoBar.ClearGutter()
188         }
189 }
190
191 // DoKeyEvent executes a key event by finding the action it is bound
192 // to and executing it (possibly multiple times for multiple cursors)
193 func (h *BufHandler) DoKeyEvent(e Event) bool {
194         if action, ok := BufKeyBindings[e]; ok {
195                 estr := BufKeyStrings[e]
196                 for _, s := range MultiActions {
197                         if s == estr {
198                                 cursors := h.Buf.GetCursors()
199                                 for _, c := range cursors {
200                                         h.Buf.SetCurCursor(c.Num)
201                                         h.Cursor = c
202                                         if action(h) {
203                                                 h.Relocate()
204                                         }
205                                 }
206                                 return true
207                         }
208                 }
209                 if action(h) {
210                         h.Relocate()
211                 }
212                 return true
213         }
214         return false
215 }
216
217 func (h *BufHandler) HasKeyEvent(e Event) bool {
218         _, ok := BufKeyBindings[e]
219         return ok
220 }
221
222 // DoMouseEvent executes a mouse event by finding the action it is bound
223 // to and executing it
224 func (h *BufHandler) DoMouseEvent(e MouseEvent, te *tcell.EventMouse) bool {
225         if action, ok := BufMouseBindings[e]; ok {
226                 if action(h, te) {
227                         h.Relocate()
228                 }
229                 return true
230         } else if h.HasKeyEvent(e) {
231                 return h.DoKeyEvent(e)
232         }
233         return false
234 }
235
236 // DoRuneInsert inserts a given rune into the current buffer
237 // (possibly multiple times for multiple cursors)
238 func (h *BufHandler) DoRuneInsert(r rune) {
239         cursors := h.Buf.GetCursors()
240         for _, c := range cursors {
241                 // Insert a character
242                 if c.HasSelection() {
243                         c.DeleteSelection()
244                         c.ResetSelection()
245                 }
246
247                 if h.isOverwriteMode {
248                         next := c.Loc
249                         next.X++
250                         h.Buf.Replace(c.Loc, next, string(r))
251                 } else {
252                         h.Buf.Insert(c.Loc, string(r))
253                 }
254         }
255 }
256
257 func (h *BufHandler) VSplitBuf(buf *buffer.Buffer) {
258         e := NewBufEditPane(0, 0, 0, 0, buf)
259         e.splitID = MainTab().GetNode(h.splitID).VSplit(h.Buf.Settings["splitright"].(bool))
260         MainTab().Panes = append(MainTab().Panes, e)
261         MainTab().Resize()
262         MainTab().SetActive(len(MainTab().Panes) - 1)
263 }
264 func (h *BufHandler) HSplitBuf(buf *buffer.Buffer) {
265         e := NewBufEditPane(0, 0, 0, 0, buf)
266         e.splitID = MainTab().GetNode(h.splitID).HSplit(h.Buf.Settings["splitbottom"].(bool))
267         MainTab().Panes = append(MainTab().Panes, e)
268         MainTab().Resize()
269         MainTab().SetActive(len(MainTab().Panes) - 1)
270 }
271
272 // BufKeyActions contains the list of all possible key actions the bufhandler could execute
273 var BufKeyActions = map[string]BufKeyAction{
274         "CursorUp":               (*BufHandler).CursorUp,
275         "CursorDown":             (*BufHandler).CursorDown,
276         "CursorPageUp":           (*BufHandler).CursorPageUp,
277         "CursorPageDown":         (*BufHandler).CursorPageDown,
278         "CursorLeft":             (*BufHandler).CursorLeft,
279         "CursorRight":            (*BufHandler).CursorRight,
280         "CursorStart":            (*BufHandler).CursorStart,
281         "CursorEnd":              (*BufHandler).CursorEnd,
282         "SelectToStart":          (*BufHandler).SelectToStart,
283         "SelectToEnd":            (*BufHandler).SelectToEnd,
284         "SelectUp":               (*BufHandler).SelectUp,
285         "SelectDown":             (*BufHandler).SelectDown,
286         "SelectLeft":             (*BufHandler).SelectLeft,
287         "SelectRight":            (*BufHandler).SelectRight,
288         "WordRight":              (*BufHandler).WordRight,
289         "WordLeft":               (*BufHandler).WordLeft,
290         "SelectWordRight":        (*BufHandler).SelectWordRight,
291         "SelectWordLeft":         (*BufHandler).SelectWordLeft,
292         "DeleteWordRight":        (*BufHandler).DeleteWordRight,
293         "DeleteWordLeft":         (*BufHandler).DeleteWordLeft,
294         "SelectLine":             (*BufHandler).SelectLine,
295         "SelectToStartOfLine":    (*BufHandler).SelectToStartOfLine,
296         "SelectToEndOfLine":      (*BufHandler).SelectToEndOfLine,
297         "ParagraphPrevious":      (*BufHandler).ParagraphPrevious,
298         "ParagraphNext":          (*BufHandler).ParagraphNext,
299         "InsertNewline":          (*BufHandler).InsertNewline,
300         "Backspace":              (*BufHandler).Backspace,
301         "Delete":                 (*BufHandler).Delete,
302         "InsertTab":              (*BufHandler).InsertTab,
303         "Save":                   (*BufHandler).Save,
304         "SaveAll":                (*BufHandler).SaveAll,
305         "SaveAs":                 (*BufHandler).SaveAs,
306         "Find":                   (*BufHandler).Find,
307         "FindNext":               (*BufHandler).FindNext,
308         "FindPrevious":           (*BufHandler).FindPrevious,
309         "Center":                 (*BufHandler).Center,
310         "Undo":                   (*BufHandler).Undo,
311         "Redo":                   (*BufHandler).Redo,
312         "Copy":                   (*BufHandler).Copy,
313         "Cut":                    (*BufHandler).Cut,
314         "CutLine":                (*BufHandler).CutLine,
315         "DuplicateLine":          (*BufHandler).DuplicateLine,
316         "DeleteLine":             (*BufHandler).DeleteLine,
317         "MoveLinesUp":            (*BufHandler).MoveLinesUp,
318         "MoveLinesDown":          (*BufHandler).MoveLinesDown,
319         "IndentSelection":        (*BufHandler).IndentSelection,
320         "OutdentSelection":       (*BufHandler).OutdentSelection,
321         "OutdentLine":            (*BufHandler).OutdentLine,
322         "Paste":                  (*BufHandler).Paste,
323         "PastePrimary":           (*BufHandler).PastePrimary,
324         "SelectAll":              (*BufHandler).SelectAll,
325         "OpenFile":               (*BufHandler).OpenFile,
326         "Start":                  (*BufHandler).Start,
327         "End":                    (*BufHandler).End,
328         "PageUp":                 (*BufHandler).PageUp,
329         "PageDown":               (*BufHandler).PageDown,
330         "SelectPageUp":           (*BufHandler).SelectPageUp,
331         "SelectPageDown":         (*BufHandler).SelectPageDown,
332         "HalfPageUp":             (*BufHandler).HalfPageUp,
333         "HalfPageDown":           (*BufHandler).HalfPageDown,
334         "StartOfLine":            (*BufHandler).StartOfLine,
335         "EndOfLine":              (*BufHandler).EndOfLine,
336         "ToggleHelp":             (*BufHandler).ToggleHelp,
337         "ToggleKeyMenu":          (*BufHandler).ToggleKeyMenu,
338         "ToggleRuler":            (*BufHandler).ToggleRuler,
339         "JumpLine":               (*BufHandler).JumpLine,
340         "ClearStatus":            (*BufHandler).ClearStatus,
341         "ShellMode":              (*BufHandler).ShellMode,
342         "CommandMode":            (*BufHandler).CommandMode,
343         "ToggleOverwriteMode":    (*BufHandler).ToggleOverwriteMode,
344         "Escape":                 (*BufHandler).Escape,
345         "Quit":                   (*BufHandler).Quit,
346         "QuitAll":                (*BufHandler).QuitAll,
347         "AddTab":                 (*BufHandler).AddTab,
348         "PreviousTab":            (*BufHandler).PreviousTab,
349         "NextTab":                (*BufHandler).NextTab,
350         "NextSplit":              (*BufHandler).NextSplit,
351         "PreviousSplit":          (*BufHandler).PreviousSplit,
352         "Unsplit":                (*BufHandler).Unsplit,
353         "VSplit":                 (*BufHandler).VSplitAction,
354         "HSplit":                 (*BufHandler).HSplitAction,
355         "ToggleMacro":            (*BufHandler).ToggleMacro,
356         "PlayMacro":              (*BufHandler).PlayMacro,
357         "Suspend":                (*BufHandler).Suspend,
358         "ScrollUp":               (*BufHandler).ScrollUpAction,
359         "ScrollDown":             (*BufHandler).ScrollDownAction,
360         "SpawnMultiCursor":       (*BufHandler).SpawnMultiCursor,
361         "SpawnMultiCursorSelect": (*BufHandler).SpawnMultiCursorSelect,
362         "RemoveMultiCursor":      (*BufHandler).RemoveMultiCursor,
363         "RemoveAllMultiCursors":  (*BufHandler).RemoveAllMultiCursors,
364         "SkipMultiCursor":        (*BufHandler).SkipMultiCursor,
365         "JumpToMatchingBrace":    (*BufHandler).JumpToMatchingBrace,
366
367         // This was changed to InsertNewline but I don't want to break backwards compatibility
368         "InsertEnter": (*BufHandler).InsertNewline,
369 }
370
371 // BufMouseActions contains the list of all possible mouse actions the bufhandler could execute
372 var BufMouseActions = map[string]BufMouseAction{
373         "MousePress":       (*BufHandler).MousePress,
374         "MouseMultiCursor": (*BufHandler).MouseMultiCursor,
375 }
376
377 // MultiActions is a list of actions that should be executed multiple
378 // times if there are multiple cursors (one per cursor)
379 // Generally actions that modify global editor state like quitting or
380 // saving should not be included in this list
381 var MultiActions = []string{
382         "CursorUp",
383         "CursorDown",
384         "CursorPageUp",
385         "CursorPageDown",
386         "CursorLeft",
387         "CursorRight",
388         "CursorStart",
389         "CursorEnd",
390         "SelectToStart",
391         "SelectToEnd",
392         "SelectUp",
393         "SelectDown",
394         "SelectLeft",
395         "SelectRight",
396         "WordRight",
397         "WordLeft",
398         "SelectWordRight",
399         "SelectWordLeft",
400         "DeleteWordRight",
401         "DeleteWordLeft",
402         "SelectLine",
403         "SelectToStartOfLine",
404         "SelectToEndOfLine",
405         "ParagraphPrevious",
406         "ParagraphNext",
407         "InsertNewline",
408         "Backspace",
409         "Delete",
410         "InsertTab",
411         "FindNext",
412         "FindPrevious",
413         "Cut",
414         "CutLine",
415         "DuplicateLine",
416         "DeleteLine",
417         "MoveLinesUp",
418         "MoveLinesDown",
419         "IndentSelection",
420         "OutdentSelection",
421         "OutdentLine",
422         "Paste",
423         "PastePrimary",
424         "SelectPageUp",
425         "SelectPageDown",
426         "StartOfLine",
427         "EndOfLine",
428         "JumpToMatchingBrace",
429 }