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