7 "github.com/zyedidia/micro/internal/buffer"
8 "github.com/zyedidia/micro/internal/display"
9 "github.com/zyedidia/micro/internal/screen"
10 "github.com/zyedidia/tcell"
13 type BufKeyAction func(*BufPane) bool
14 type BufMouseAction func(*BufPane, *tcell.EventMouse) bool
16 var BufKeyBindings map[Event]BufKeyAction
17 var BufKeyStrings map[Event]string
18 var BufMouseBindings map[MouseEvent]BufMouseAction
21 BufKeyBindings = make(map[Event]BufKeyAction)
22 BufKeyStrings = make(map[Event]string)
23 BufMouseBindings = make(map[MouseEvent]BufMouseAction)
26 // BufMapKey maps a key event to an action
27 func BufMapKey(k Event, action string) {
28 if strings.HasPrefix(action, "command:") {
29 action = strings.SplitN(action, ":", 2)[1]
30 BufKeyStrings[k] = action
31 BufKeyBindings[k] = CommandAction(action)
32 } else if strings.HasPrefix(action, "command-edit:") {
33 action = strings.SplitN(action, ":", 2)[1]
34 BufKeyStrings[k] = action
35 BufKeyBindings[k] = CommandEditAction(action)
36 } else if f, ok := BufKeyActions[action]; ok {
37 BufKeyStrings[k] = action
40 screen.TermMessage("Error:", action, "does not exist")
44 // BufMapMouse maps a mouse event to an action
45 func BufMapMouse(k MouseEvent, action string) {
46 if f, ok := BufMouseActions[action]; ok {
47 BufMouseBindings[k] = f
49 delete(BufMouseBindings, k)
54 // The BufPane connects the buffer and the window
55 // It provides a cursor (or multiple) and defines a set of actions
56 // that can be taken on the buffer
57 // The ActionHandler can access the window for necessary info about
58 // visual positions for mouse clicks and scrolling
64 Cursor *buffer.Cursor // the active cursor
66 StartLine int // Vertical scrolling
67 StartCol int // Horizontal scrolling
69 // Since tcell doesn't differentiate between a mouse release event
70 // and a mouse move event with no keys pressed, we need to keep
71 // track of whether or not the mouse was pressed (or not released) last event to determine
72 // mouse release events
75 // We need to keep track of insert key press toggle
77 // This stores when the last click was
78 // This is useful for detecting double and triple clicks
79 lastClickTime time.Time
82 // lastCutTime stores when the last ctrl+k was issued.
83 // It is used for clearing the clipboard to replace it with fresh cut lines.
86 // freshClip returns true if the clipboard has never been pasted.
89 // Was the last mouse event actually a double click?
90 // Useful for detecting triple clicks -- if a double click is detected
91 // but the last mouse event was actually a double click, it's a triple click
93 // Same here, just to keep track for mouse move events
96 // Last search stores the last successful search for FindNext and FindPrev
98 // Should the current multiple cursor selection search based on word or
99 // based on selection (false for selection, true for word)
105 func NewBufPane(buf *buffer.Buffer, win display.BWindow) *BufPane {
110 h.Cursor = h.Buf.GetActiveCursor()
111 h.mouseReleased = true
116 func NewBufPaneFromBuf(buf *buffer.Buffer) *BufPane {
117 w := display.NewBufWindow(0, 0, 0, 0, buf)
118 return NewBufPane(buf, w)
121 func (h *BufPane) OpenBuffer(b *buffer.Buffer) {
124 h.BWindow.SetBuffer(b)
125 h.Cursor = b.GetActiveCursor()
126 v := new(display.View)
129 // Set mouseReleased to true because we assume the mouse is not being pressed when
130 // the editor is opened
131 h.mouseReleased = true
132 // Set isOverwriteMode to false, because we assume we are in the default mode when editor
134 h.isOverwriteMode = false
135 h.lastClickTime = time.Time{}
138 func (h *BufPane) ID() uint64 {
142 func (h *BufPane) SetID(i uint64) {
146 func (h *BufPane) Name() string {
147 return h.Buf.GetName()
150 // HandleEvent executes the tcell event properly
151 // TODO: multiple actions bound to one key
152 func (h *BufPane) HandleEvent(event tcell.Event) {
153 switch e := event.(type) {
154 case *tcell.EventRaw:
159 case *tcell.EventKey:
166 done := h.DoKeyEvent(ke)
167 if !done && e.Key() == tcell.KeyRune {
168 h.DoRuneInsert(e.Rune())
170 case *tcell.EventMouse:
172 case tcell.ButtonNone:
173 // Mouse event with no click
174 if !h.mouseReleased {
175 // Mouse was just released
177 mx, my := e.Position()
178 mouseLoc := h.GetMouseLoc(buffer.Loc{X: mx, Y: my})
180 // Relocating here isn't really necessary because the cursor will
181 // be in the right place from the last mouse event
182 // However, if we are running in a terminal that doesn't support mouse motion
183 // events, this still allows the user to make selections, except only after they
186 if !h.doubleClick && !h.tripleClick {
187 h.Cursor.Loc = mouseLoc
188 h.Cursor.SetSelectionEnd(h.Cursor.Loc)
189 h.Cursor.CopySelection("primary")
191 h.mouseReleased = true
199 h.DoMouseEvent(me, e)
203 // Display any gutter messages for this line
204 c := h.Buf.GetActiveCursor()
206 for _, m := range h.Buf.Messages {
207 if c.Y == m.Start.Y || c.Y == m.End.Y {
208 InfoBar.GutterMessage(m.Msg)
213 if none && InfoBar.HasGutter {
214 InfoBar.ClearGutter()
218 // DoKeyEvent executes a key event by finding the action it is bound
219 // to and executing it (possibly multiple times for multiple cursors)
220 func (h *BufPane) DoKeyEvent(e Event) bool {
221 if action, ok := BufKeyBindings[e]; ok {
222 estr := BufKeyStrings[e]
223 for _, s := range MultiActions {
225 cursors := h.Buf.GetCursors()
226 for _, c := range cursors {
227 h.Buf.SetCurCursor(c.Num)
244 func (h *BufPane) HasKeyEvent(e Event) bool {
245 _, ok := BufKeyBindings[e]
249 // DoMouseEvent executes a mouse event by finding the action it is bound
250 // to and executing it
251 func (h *BufPane) DoMouseEvent(e MouseEvent, te *tcell.EventMouse) bool {
252 if action, ok := BufMouseBindings[e]; ok {
257 } else if h.HasKeyEvent(e) {
258 return h.DoKeyEvent(e)
263 // DoRuneInsert inserts a given rune into the current buffer
264 // (possibly multiple times for multiple cursors)
265 func (h *BufPane) DoRuneInsert(r rune) {
266 cursors := h.Buf.GetCursors()
267 for _, c := range cursors {
268 // Insert a character
269 h.Buf.SetCurCursor(c.Num)
270 if c.HasSelection() {
275 if h.isOverwriteMode {
278 h.Buf.Replace(c.Loc, next, string(r))
280 h.Buf.Insert(c.Loc, string(r))
285 func (h *BufPane) VSplitBuf(buf *buffer.Buffer) {
286 e := NewBufPaneFromBuf(buf)
287 e.splitID = MainTab().GetNode(h.splitID).VSplit(h.Buf.Settings["splitright"].(bool))
288 MainTab().Panes = append(MainTab().Panes, e)
290 MainTab().SetActive(len(MainTab().Panes) - 1)
292 func (h *BufPane) HSplitBuf(buf *buffer.Buffer) {
293 e := NewBufPaneFromBuf(buf)
294 e.splitID = MainTab().GetNode(h.splitID).HSplit(h.Buf.Settings["splitbottom"].(bool))
295 MainTab().Panes = append(MainTab().Panes, e)
297 MainTab().SetActive(len(MainTab().Panes) - 1)
299 func (h *BufPane) Close() {
303 // BufKeyActions contains the list of all possible key actions the bufhandler could execute
304 var BufKeyActions = map[string]BufKeyAction{
305 "CursorUp": (*BufPane).CursorUp,
306 "CursorDown": (*BufPane).CursorDown,
307 "CursorPageUp": (*BufPane).CursorPageUp,
308 "CursorPageDown": (*BufPane).CursorPageDown,
309 "CursorLeft": (*BufPane).CursorLeft,
310 "CursorRight": (*BufPane).CursorRight,
311 "CursorStart": (*BufPane).CursorStart,
312 "CursorEnd": (*BufPane).CursorEnd,
313 "SelectToStart": (*BufPane).SelectToStart,
314 "SelectToEnd": (*BufPane).SelectToEnd,
315 "SelectUp": (*BufPane).SelectUp,
316 "SelectDown": (*BufPane).SelectDown,
317 "SelectLeft": (*BufPane).SelectLeft,
318 "SelectRight": (*BufPane).SelectRight,
319 "WordRight": (*BufPane).WordRight,
320 "WordLeft": (*BufPane).WordLeft,
321 "SelectWordRight": (*BufPane).SelectWordRight,
322 "SelectWordLeft": (*BufPane).SelectWordLeft,
323 "DeleteWordRight": (*BufPane).DeleteWordRight,
324 "DeleteWordLeft": (*BufPane).DeleteWordLeft,
325 "SelectLine": (*BufPane).SelectLine,
326 "SelectToStartOfLine": (*BufPane).SelectToStartOfLine,
327 "SelectToEndOfLine": (*BufPane).SelectToEndOfLine,
328 "ParagraphPrevious": (*BufPane).ParagraphPrevious,
329 "ParagraphNext": (*BufPane).ParagraphNext,
330 "InsertNewline": (*BufPane).InsertNewline,
331 "Backspace": (*BufPane).Backspace,
332 "Delete": (*BufPane).Delete,
333 "InsertTab": (*BufPane).InsertTab,
334 "Save": (*BufPane).Save,
335 "SaveAll": (*BufPane).SaveAll,
336 "SaveAs": (*BufPane).SaveAs,
337 "Find": (*BufPane).Find,
338 "FindNext": (*BufPane).FindNext,
339 "FindPrevious": (*BufPane).FindPrevious,
340 "Center": (*BufPane).Center,
341 "Undo": (*BufPane).Undo,
342 "Redo": (*BufPane).Redo,
343 "Copy": (*BufPane).Copy,
344 "Cut": (*BufPane).Cut,
345 "CutLine": (*BufPane).CutLine,
346 "DuplicateLine": (*BufPane).DuplicateLine,
347 "DeleteLine": (*BufPane).DeleteLine,
348 "MoveLinesUp": (*BufPane).MoveLinesUp,
349 "MoveLinesDown": (*BufPane).MoveLinesDown,
350 "IndentSelection": (*BufPane).IndentSelection,
351 "OutdentSelection": (*BufPane).OutdentSelection,
352 "OutdentLine": (*BufPane).OutdentLine,
353 "Paste": (*BufPane).Paste,
354 "PastePrimary": (*BufPane).PastePrimary,
355 "SelectAll": (*BufPane).SelectAll,
356 "OpenFile": (*BufPane).OpenFile,
357 "Start": (*BufPane).Start,
358 "End": (*BufPane).End,
359 "PageUp": (*BufPane).PageUp,
360 "PageDown": (*BufPane).PageDown,
361 "SelectPageUp": (*BufPane).SelectPageUp,
362 "SelectPageDown": (*BufPane).SelectPageDown,
363 "HalfPageUp": (*BufPane).HalfPageUp,
364 "HalfPageDown": (*BufPane).HalfPageDown,
365 "StartOfLine": (*BufPane).StartOfLine,
366 "EndOfLine": (*BufPane).EndOfLine,
367 "ToggleHelp": (*BufPane).ToggleHelp,
368 "ToggleKeyMenu": (*BufPane).ToggleKeyMenu,
369 "ToggleRuler": (*BufPane).ToggleRuler,
370 "JumpLine": (*BufPane).JumpLine,
371 "ClearStatus": (*BufPane).ClearStatus,
372 "ShellMode": (*BufPane).ShellMode,
373 "CommandMode": (*BufPane).CommandMode,
374 "ToggleOverwriteMode": (*BufPane).ToggleOverwriteMode,
375 "Escape": (*BufPane).Escape,
376 "Quit": (*BufPane).Quit,
377 "QuitAll": (*BufPane).QuitAll,
378 "AddTab": (*BufPane).AddTab,
379 "PreviousTab": (*BufPane).PreviousTab,
380 "NextTab": (*BufPane).NextTab,
381 "NextSplit": (*BufPane).NextSplit,
382 "PreviousSplit": (*BufPane).PreviousSplit,
383 "Unsplit": (*BufPane).Unsplit,
384 "VSplit": (*BufPane).VSplitAction,
385 "HSplit": (*BufPane).HSplitAction,
386 "ToggleMacro": (*BufPane).ToggleMacro,
387 "PlayMacro": (*BufPane).PlayMacro,
388 "Suspend": (*BufPane).Suspend,
389 "ScrollUp": (*BufPane).ScrollUpAction,
390 "ScrollDown": (*BufPane).ScrollDownAction,
391 "SpawnMultiCursor": (*BufPane).SpawnMultiCursor,
392 "SpawnMultiCursorSelect": (*BufPane).SpawnMultiCursorSelect,
393 "RemoveMultiCursor": (*BufPane).RemoveMultiCursor,
394 "RemoveAllMultiCursors": (*BufPane).RemoveAllMultiCursors,
395 "SkipMultiCursor": (*BufPane).SkipMultiCursor,
396 "JumpToMatchingBrace": (*BufPane).JumpToMatchingBrace,
398 // This was changed to InsertNewline but I don't want to break backwards compatibility
399 "InsertEnter": (*BufPane).InsertNewline,
402 // BufMouseActions contains the list of all possible mouse actions the bufhandler could execute
403 var BufMouseActions = map[string]BufMouseAction{
404 "MousePress": (*BufPane).MousePress,
405 "MouseMultiCursor": (*BufPane).MouseMultiCursor,
408 // MultiActions is a list of actions that should be executed multiple
409 // times if there are multiple cursors (one per cursor)
410 // Generally actions that modify global editor state like quitting or
411 // saving should not be included in this list
412 var MultiActions = []string{
434 "SelectToStartOfLine",
459 "JumpToMatchingBrace",