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"
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 util.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 util.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 cursors []*buffer.Cursor
61 Cursor *buffer.Cursor // the active cursor
63 StartLine int // Vertical scrolling
64 StartCol int // Horizontal scrolling
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
72 // We need to keep track of insert key press toggle
74 // This stores when the last click was
75 // This is useful for detecting double and triple clicks
76 lastClickTime time.Time
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.
83 // freshClip returns true if the clipboard has never been pasted.
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
90 // Same here, just to keep track for mouse move events
93 // Last search stores the last successful search for FindNext and FindPrev
95 // Should the current multiple cursor selection search based on word or
96 // based on selection (false for selection, true for word)
102 func NewBufHandler(buf *buffer.Buffer, win display.Window) *BufHandler {
107 h.cursors = []*buffer.Cursor{buffer.NewCursor(buf, buf.StartCursor)}
108 h.Cursor = h.cursors[0]
109 h.mouseReleased = true
111 buf.SetCursors(h.cursors)
115 func (h *BufHandler) ID() uint64 {
119 func (h *BufHandler) Name() string {
120 return h.Buf.GetName()
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:
132 case *tcell.EventKey:
139 done := h.DoKeyEvent(ke)
140 if !done && e.Key() == tcell.KeyRune {
141 h.DoRuneInsert(e.Rune())
143 case *tcell.EventMouse:
145 case tcell.ButtonNone:
146 // Mouse event with no click
147 if !h.mouseReleased {
148 // Mouse was just released
150 mx, my := e.Position()
151 mouseLoc := h.GetMouseLoc(buffer.Loc{X: mx, Y: my})
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
159 if !h.doubleClick && !h.tripleClick {
160 h.Cursor.Loc = mouseLoc
161 h.Cursor.SetSelectionEnd(h.Cursor.Loc)
162 h.Cursor.CopySelection("primary")
164 h.mouseReleased = true
172 h.DoMouseEvent(me, e)
176 // Display any gutter messages for this line
177 c := h.Buf.GetActiveCursor()
179 for _, m := range h.Buf.Messages {
180 if c.Y == m.Start.Y || c.Y == m.End.Y {
181 InfoBar.GutterMessage(m.Msg)
186 if none && InfoBar.HasGutter {
187 InfoBar.ClearGutter()
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 {
198 cursors := h.Buf.GetCursors()
199 for _, c := range cursors {
200 h.Buf.SetCurCursor(c.Num)
217 func (h *BufHandler) HasKeyEvent(e Event) bool {
218 _, ok := BufKeyBindings[e]
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 {
230 } else if h.HasKeyEvent(e) {
231 return h.DoKeyEvent(e)
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() {
247 if h.isOverwriteMode {
250 h.Buf.Replace(c.Loc, next, string(r))
252 h.Buf.Insert(c.Loc, string(r))
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)
262 MainTab().SetActive(len(MainTab().Panes) - 1)
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)
269 MainTab().SetActive(len(MainTab().Panes) - 1)
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,
367 // This was changed to InsertNewline but I don't want to break backwards compatibility
368 "InsertEnter": (*BufHandler).InsertNewline,
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,
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{
403 "SelectToStartOfLine",
428 "JumpToMatchingBrace",