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[KeyEvent]BufKeyAction
16 var BufKeyStrings map[KeyEvent]string
17 var BufMouseBindings map[MouseEvent]BufMouseAction
20 BufKeyBindings = make(map[KeyEvent]BufKeyAction)
21 BufKeyStrings = make(map[KeyEvent]string)
22 BufMouseBindings = make(map[MouseEvent]BufMouseAction)
25 // BufMapKey maps a key event to an action
26 func BufMapKey(k KeyEvent, 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 BufMouseBindings[k] = func(h *BufHandler, e *tcell.EventMouse) bool {
45 util.TermMessage("Error:", action, "does not exist")
49 // The BufHandler connects the buffer and the window
50 // It provides a cursor (or multiple) and defines a set of actions
51 // that can be taken on the buffer
52 // The ActionHandler can access the window for necessary info about
53 // visual positions for mouse clicks and scrolling
54 type BufHandler struct {
58 cursors []*buffer.Cursor
59 Cursor *buffer.Cursor // the active cursor
61 StartLine int // Vertical scrolling
62 StartCol int // Horizontal scrolling
64 // Since tcell doesn't differentiate between a mouse release event
65 // and a mouse move event with no keys pressed, we need to keep
66 // track of whether or not the mouse was pressed (or not released) last event to determine
67 // mouse release events
70 // We need to keep track of insert key press toggle
72 // This stores when the last click was
73 // This is useful for detecting double and triple clicks
74 lastClickTime time.Time
77 // lastCutTime stores when the last ctrl+k was issued.
78 // It is used for clearing the clipboard to replace it with fresh cut lines.
81 // freshClip returns true if the clipboard has never been pasted.
84 // Was the last mouse event actually a double click?
85 // Useful for detecting triple clicks -- if a double click is detected
86 // but the last mouse event was actually a double click, it's a triple click
88 // Same here, just to keep track for mouse move events
91 // Last search stores the last successful search for FindNext and FindPrev
93 // Should the current multiple cursor selection search based on word or
94 // based on selection (false for selection, true for word)
100 func NewBufHandler(buf *buffer.Buffer, win display.Window) *BufHandler {
105 h.cursors = []*buffer.Cursor{buffer.NewCursor(buf, buf.StartCursor)}
106 h.Cursor = h.cursors[0]
107 h.mouseReleased = true
109 buf.SetCursors(h.cursors)
113 // HandleEvent executes the tcell event properly
114 // TODO: multiple actions bound to one key
115 func (h *BufHandler) HandleEvent(event tcell.Event) {
116 switch e := event.(type) {
117 case *tcell.EventKey:
124 done := h.DoKeyEvent(ke)
125 if !done && e.Key() == tcell.KeyRune {
126 h.DoRuneInsert(e.Rune())
128 case *tcell.EventMouse:
130 case tcell.ButtonNone:
131 // Mouse event with no click
132 if !h.mouseReleased {
133 // Mouse was just released
135 mx, my := e.Position()
136 mouseLoc := h.Win.GetMouseLoc(buffer.Loc{X: mx, Y: my})
138 // Relocating here isn't really necessary because the cursor will
139 // be in the right place from the last mouse event
140 // However, if we are running in a terminal that doesn't support mouse motion
141 // events, this still allows the user to make selections, except only after they
144 if !h.doubleClick && !h.tripleClick {
145 h.Cursor.Loc = mouseLoc
146 h.Cursor.SetSelectionEnd(h.Cursor.Loc)
147 h.Cursor.CopySelection("primary")
149 h.mouseReleased = true
157 h.DoMouseEvent(me, e)
162 // DoKeyEvent executes a key event by finding the action it is bound
163 // to and executing it (possibly multiple times for multiple cursors)
164 func (h *BufHandler) DoKeyEvent(e KeyEvent) bool {
165 if action, ok := BufKeyBindings[e]; ok {
166 estr := BufKeyStrings[e]
167 for _, s := range MultiActions {
169 cursors := h.Buf.GetCursors()
170 for _, c := range cursors {
171 h.Buf.SetCurCursor(c.Num)
188 // DoMouseEvent executes a mouse event by finding the action it is bound
189 // to and executing it
190 func (h *BufHandler) DoMouseEvent(e MouseEvent, te *tcell.EventMouse) bool {
191 if action, ok := BufMouseBindings[e]; ok {
200 // DoRuneInsert inserts a given rune into the current buffer
201 // (possibly multiple times for multiple cursors)
202 func (h *BufHandler) DoRuneInsert(r rune) {
203 cursors := h.Buf.GetCursors()
204 for _, c := range cursors {
205 // Insert a character
206 if c.HasSelection() {
211 if h.isOverwriteMode {
214 h.Buf.Replace(c.Loc, next, string(r))
216 h.Buf.Insert(c.Loc, string(r))
221 func (h *BufHandler) vsplit(buf *buffer.Buffer) {
222 e := NewBufEditPane(0, 0, 0, 0, buf)
223 e.splitID = MainTab.GetNode(h.splitID).VSplit(h.Buf.Settings["splitright"].(bool))
224 MainTab.Panes = append(MainTab.Panes, e)
226 MainTab.SetActive(len(MainTab.Panes) - 1)
228 func (h *BufHandler) hsplit(buf *buffer.Buffer) {
229 e := NewBufEditPane(0, 0, 0, 0, buf)
230 e.splitID = MainTab.GetNode(h.splitID).HSplit(h.Buf.Settings["splitbottom"].(bool))
231 MainTab.Panes = append(MainTab.Panes, e)
233 MainTab.SetActive(len(MainTab.Panes) - 1)
236 // BufKeyActions contains the list of all possible key actions the bufhandler could execute
237 var BufKeyActions = map[string]BufKeyAction{
238 "CursorUp": (*BufHandler).CursorUp,
239 "CursorDown": (*BufHandler).CursorDown,
240 "CursorPageUp": (*BufHandler).CursorPageUp,
241 "CursorPageDown": (*BufHandler).CursorPageDown,
242 "CursorLeft": (*BufHandler).CursorLeft,
243 "CursorRight": (*BufHandler).CursorRight,
244 "CursorStart": (*BufHandler).CursorStart,
245 "CursorEnd": (*BufHandler).CursorEnd,
246 "SelectToStart": (*BufHandler).SelectToStart,
247 "SelectToEnd": (*BufHandler).SelectToEnd,
248 "SelectUp": (*BufHandler).SelectUp,
249 "SelectDown": (*BufHandler).SelectDown,
250 "SelectLeft": (*BufHandler).SelectLeft,
251 "SelectRight": (*BufHandler).SelectRight,
252 "WordRight": (*BufHandler).WordRight,
253 "WordLeft": (*BufHandler).WordLeft,
254 "SelectWordRight": (*BufHandler).SelectWordRight,
255 "SelectWordLeft": (*BufHandler).SelectWordLeft,
256 "DeleteWordRight": (*BufHandler).DeleteWordRight,
257 "DeleteWordLeft": (*BufHandler).DeleteWordLeft,
258 "SelectLine": (*BufHandler).SelectLine,
259 "SelectToStartOfLine": (*BufHandler).SelectToStartOfLine,
260 "SelectToEndOfLine": (*BufHandler).SelectToEndOfLine,
261 "ParagraphPrevious": (*BufHandler).ParagraphPrevious,
262 "ParagraphNext": (*BufHandler).ParagraphNext,
263 "InsertNewline": (*BufHandler).InsertNewline,
264 "InsertSpace": (*BufHandler).InsertSpace,
265 "Backspace": (*BufHandler).Backspace,
266 "Delete": (*BufHandler).Delete,
267 "InsertTab": (*BufHandler).InsertTab,
268 "Save": (*BufHandler).Save,
269 "SaveAll": (*BufHandler).SaveAll,
270 "SaveAs": (*BufHandler).SaveAs,
271 "Find": (*BufHandler).Find,
272 "FindNext": (*BufHandler).FindNext,
273 "FindPrevious": (*BufHandler).FindPrevious,
274 "Center": (*BufHandler).Center,
275 "Undo": (*BufHandler).Undo,
276 "Redo": (*BufHandler).Redo,
277 "Copy": (*BufHandler).Copy,
278 "Cut": (*BufHandler).Cut,
279 "CutLine": (*BufHandler).CutLine,
280 "DuplicateLine": (*BufHandler).DuplicateLine,
281 "DeleteLine": (*BufHandler).DeleteLine,
282 "MoveLinesUp": (*BufHandler).MoveLinesUp,
283 "MoveLinesDown": (*BufHandler).MoveLinesDown,
284 "IndentSelection": (*BufHandler).IndentSelection,
285 "OutdentSelection": (*BufHandler).OutdentSelection,
286 "OutdentLine": (*BufHandler).OutdentLine,
287 "Paste": (*BufHandler).Paste,
288 "PastePrimary": (*BufHandler).PastePrimary,
289 "SelectAll": (*BufHandler).SelectAll,
290 "OpenFile": (*BufHandler).OpenFile,
291 "Start": (*BufHandler).Start,
292 "End": (*BufHandler).End,
293 "PageUp": (*BufHandler).PageUp,
294 "PageDown": (*BufHandler).PageDown,
295 "SelectPageUp": (*BufHandler).SelectPageUp,
296 "SelectPageDown": (*BufHandler).SelectPageDown,
297 "HalfPageUp": (*BufHandler).HalfPageUp,
298 "HalfPageDown": (*BufHandler).HalfPageDown,
299 "StartOfLine": (*BufHandler).StartOfLine,
300 "EndOfLine": (*BufHandler).EndOfLine,
301 "ToggleHelp": (*BufHandler).ToggleHelp,
302 "ToggleKeyMenu": (*BufHandler).ToggleKeyMenu,
303 "ToggleRuler": (*BufHandler).ToggleRuler,
304 "JumpLine": (*BufHandler).JumpLine,
305 "ClearStatus": (*BufHandler).ClearStatus,
306 "ShellMode": (*BufHandler).ShellMode,
307 "CommandMode": (*BufHandler).CommandMode,
308 "ToggleOverwriteMode": (*BufHandler).ToggleOverwriteMode,
309 "Escape": (*BufHandler).Escape,
310 "Quit": (*BufHandler).Quit,
311 "QuitAll": (*BufHandler).QuitAll,
312 "AddTab": (*BufHandler).AddTab,
313 "PreviousTab": (*BufHandler).PreviousTab,
314 "NextTab": (*BufHandler).NextTab,
315 "NextSplit": (*BufHandler).NextSplit,
316 "PreviousSplit": (*BufHandler).PreviousSplit,
317 "Unsplit": (*BufHandler).Unsplit,
318 "VSplit": (*BufHandler).VSplitBinding,
319 "HSplit": (*BufHandler).HSplitBinding,
320 "ToggleMacro": (*BufHandler).ToggleMacro,
321 "PlayMacro": (*BufHandler).PlayMacro,
322 "Suspend": (*BufHandler).Suspend,
323 "ScrollUp": (*BufHandler).ScrollUpAction,
324 "ScrollDown": (*BufHandler).ScrollDownAction,
325 "SpawnMultiCursor": (*BufHandler).SpawnMultiCursor,
326 "SpawnMultiCursorSelect": (*BufHandler).SpawnMultiCursorSelect,
327 "RemoveMultiCursor": (*BufHandler).RemoveMultiCursor,
328 "RemoveAllMultiCursors": (*BufHandler).RemoveAllMultiCursors,
329 "SkipMultiCursor": (*BufHandler).SkipMultiCursor,
330 "JumpToMatchingBrace": (*BufHandler).JumpToMatchingBrace,
332 // This was changed to InsertNewline but I don't want to break backwards compatibility
333 "InsertEnter": (*BufHandler).InsertNewline,
336 // BufMouseActions contains the list of all possible mouse actions the bufhandler could execute
337 var BufMouseActions = map[string]BufMouseAction{
338 "MousePress": (*BufHandler).MousePress,
339 "MouseMultiCursor": (*BufHandler).MouseMultiCursor,
342 // MultiActions is a list of actions that should be executed multiple
343 // times if there are multiple cursors (one per cursor)
344 // Generally actions that modify global editor state like quitting or
345 // saving should not be included in this list
346 var MultiActions = []string{
368 "SelectToStartOfLine",
394 "JumpToMatchingBrace",