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"
12 type BufKeyAction func(*BufHandler) bool
13 type BufMouseAction func(*BufHandler, *tcell.EventMouse) bool
15 var BufKeyBindings map[Event]BufKeyAction
16 var BufKeyStrings map[Event]string
17 var BufMouseBindings map[MouseEvent]BufMouseAction
20 BufKeyBindings = make(map[Event]BufKeyAction)
21 BufKeyStrings = make(map[Event]string)
22 BufMouseBindings = make(map[MouseEvent]BufMouseAction)
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
31 screen.TermMessage("Error:", action, "does not exist")
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
43 // ensure we don't double bind a key
44 delete(BufMouseBindings, k)
46 screen.TermMessage("Error:", action, "does not exist")
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 {
60 Cursor *buffer.Cursor // the active cursor
62 StartLine int // Vertical scrolling
63 StartCol int // Horizontal scrolling
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
71 // We need to keep track of insert key press toggle
73 // This stores when the last click was
74 // This is useful for detecting double and triple clicks
75 lastClickTime time.Time
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.
82 // freshClip returns true if the clipboard has never been pasted.
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
89 // Same here, just to keep track for mouse move events
92 // Last search stores the last successful search for FindNext and FindPrev
94 // Should the current multiple cursor selection search based on word or
95 // based on selection (false for selection, true for word)
101 func NewBufHandler(buf *buffer.Buffer, win display.BWindow) *BufHandler {
106 h.Cursor = h.Buf.GetActiveCursor()
107 h.mouseReleased = true
112 func (h *BufHandler) OpenBuffer(b *buffer.Buffer) {
115 h.BWindow.SetBuffer(b)
116 h.Cursor = b.GetActiveCursor()
117 v := new(display.View)
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
125 h.isOverwriteMode = false
126 h.lastClickTime = time.Time{}
129 func (h *BufHandler) ID() uint64 {
133 func (h *BufHandler) Name() string {
134 return h.Buf.GetName()
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:
146 case *tcell.EventKey:
153 done := h.DoKeyEvent(ke)
154 if !done && e.Key() == tcell.KeyRune {
155 h.DoRuneInsert(e.Rune())
157 case *tcell.EventMouse:
159 case tcell.ButtonNone:
160 // Mouse event with no click
161 if !h.mouseReleased {
162 // Mouse was just released
164 mx, my := e.Position()
165 mouseLoc := h.GetMouseLoc(buffer.Loc{X: mx, Y: my})
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
173 if !h.doubleClick && !h.tripleClick {
174 h.Cursor.Loc = mouseLoc
175 h.Cursor.SetSelectionEnd(h.Cursor.Loc)
176 h.Cursor.CopySelection("primary")
178 h.mouseReleased = true
186 h.DoMouseEvent(me, e)
190 // Display any gutter messages for this line
191 c := h.Buf.GetActiveCursor()
193 for _, m := range h.Buf.Messages {
194 if c.Y == m.Start.Y || c.Y == m.End.Y {
195 InfoBar.GutterMessage(m.Msg)
200 if none && InfoBar.HasGutter {
201 InfoBar.ClearGutter()
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 {
212 cursors := h.Buf.GetCursors()
213 for _, c := range cursors {
214 h.Buf.SetCurCursor(c.Num)
231 func (h *BufHandler) HasKeyEvent(e Event) bool {
232 _, ok := BufKeyBindings[e]
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 {
244 } else if h.HasKeyEvent(e) {
245 return h.DoKeyEvent(e)
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() {
262 if h.isOverwriteMode {
265 h.Buf.Replace(c.Loc, next, []byte{byte(r)})
267 h.Buf.Insert(c.Loc, []byte{byte(r)})
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)
277 MainTab().SetActive(len(MainTab().Panes) - 1)
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)
284 MainTab().SetActive(len(MainTab().Panes) - 1)
286 func (h *BufHandler) Close() {
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,
385 // This was changed to InsertNewline but I don't want to break backwards compatibility
386 "InsertEnter": (*BufHandler).InsertNewline,
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,
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{
421 "SelectToStartOfLine",
446 "JumpToMatchingBrace",