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 BufMouseBindings map[MouseEvent]BufMouseAction
19 BufKeyBindings = make(map[KeyEvent]BufKeyAction)
20 BufMouseBindings = make(map[MouseEvent]BufMouseAction)
23 func BufMapKey(k KeyEvent, action string) {
24 if f, ok := BufKeyActions[action]; ok {
27 util.TermMessage("Error:", action, "does not exist")
30 func BufMapMouse(k MouseEvent, action string) {
31 if f, ok := BufMouseActions[action]; ok {
32 BufMouseBindings[k] = f
33 } else if f, ok := BufKeyActions[action]; ok {
34 // allowed to map mouse buttons to key actions
35 BufMouseBindings[k] = func(h *BufHandler, e *tcell.EventMouse) bool {
39 util.TermMessage("Error:", action, "does not exist")
43 // The BufHandler connects the buffer and the window
44 // It provides a cursor (or multiple) and defines a set of actions
45 // that can be taken on the buffer
46 // The ActionHandler can access the window for necessary info about
47 // visual positions for mouse clicks and scrolling
48 type BufHandler struct {
52 cursors []*buffer.Cursor
53 Cursor *buffer.Cursor // the active cursor
55 StartLine int // Vertical scrolling
56 StartCol int // Horizontal scrolling
58 // Since tcell doesn't differentiate between a mouse release event
59 // and a mouse move event with no keys pressed, we need to keep
60 // track of whether or not the mouse was pressed (or not released) last event to determine
61 // mouse release events
64 // We need to keep track of insert key press toggle
66 // This stores when the last click was
67 // This is useful for detecting double and triple clicks
68 lastClickTime time.Time
71 // lastCutTime stores when the last ctrl+k was issued.
72 // It is used for clearing the clipboard to replace it with fresh cut lines.
75 // freshClip returns true if the clipboard has never been pasted.
78 // Was the last mouse event actually a double click?
79 // Useful for detecting triple clicks -- if a double click is detected
80 // but the last mouse event was actually a double click, it's a triple click
82 // Same here, just to keep track for mouse move events
86 func NewBufHandler(buf *buffer.Buffer, win display.Window) *BufHandler {
91 h.cursors = []*buffer.Cursor{buffer.NewCursor(buf, buf.StartCursor)}
92 h.Cursor = h.cursors[0]
93 h.mouseReleased = true
95 buf.SetCursors(h.cursors)
99 // HandleEvent executes the tcell event properly
100 // TODO: multiple actions bound to one key
101 func (h *BufHandler) HandleEvent(event tcell.Event) {
102 switch e := event.(type) {
103 case *tcell.EventKey:
109 cursors := h.Buf.GetCursors()
110 for _, c := range cursors {
111 h.Buf.SetCurCursor(c.Num)
113 done := h.DoKeyEvent(ke)
114 if !done && e.Key() == tcell.KeyRune {
115 h.DoRuneInsert(e.Rune())
118 // TODO: maybe reset curcursor to 0
119 case *tcell.EventMouse:
121 case tcell.ButtonNone:
122 // Mouse event with no click
123 if !h.mouseReleased {
124 // Mouse was just released
126 mx, my := e.Position()
127 mouseLoc := h.Win.GetMouseLoc(buffer.Loc{X: mx, Y: my})
129 // Relocating here isn't really necessary because the cursor will
130 // be in the right place from the last mouse event
131 // However, if we are running in a terminal that doesn't support mouse motion
132 // events, this still allows the user to make selections, except only after they
135 if !h.doubleClick && !h.tripleClick {
136 h.Cursor.Loc = mouseLoc
137 h.Cursor.SetSelectionEnd(h.Cursor.Loc)
138 h.Cursor.CopySelection("primary")
140 h.mouseReleased = true
148 cursors := h.Buf.GetCursors()
149 for _, c := range cursors {
150 h.Buf.SetCurCursor(c.Num)
152 h.DoMouseEvent(me, e)
157 func (h *BufHandler) DoKeyEvent(e KeyEvent) bool {
158 if action, ok := BufKeyBindings[e]; ok {
167 func (h *BufHandler) DoMouseEvent(e MouseEvent, te *tcell.EventMouse) bool {
168 if action, ok := BufMouseBindings[e]; ok {
177 func (h *BufHandler) DoRuneInsert(r rune) {
178 // Insert a character
179 if h.Cursor.HasSelection() {
180 h.Cursor.DeleteSelection()
181 h.Cursor.ResetSelection()
184 if h.isOverwriteMode {
187 h.Buf.Replace(h.Cursor.Loc, next, string(r))
189 h.Buf.Insert(h.Cursor.Loc, string(r))
193 var BufKeyActions = map[string]BufKeyAction{
194 "CursorUp": (*BufHandler).CursorUp,
195 "CursorDown": (*BufHandler).CursorDown,
196 "CursorPageUp": (*BufHandler).CursorPageUp,
197 "CursorPageDown": (*BufHandler).CursorPageDown,
198 "CursorLeft": (*BufHandler).CursorLeft,
199 "CursorRight": (*BufHandler).CursorRight,
200 "CursorStart": (*BufHandler).CursorStart,
201 "CursorEnd": (*BufHandler).CursorEnd,
202 "SelectToStart": (*BufHandler).SelectToStart,
203 "SelectToEnd": (*BufHandler).SelectToEnd,
204 "SelectUp": (*BufHandler).SelectUp,
205 "SelectDown": (*BufHandler).SelectDown,
206 "SelectLeft": (*BufHandler).SelectLeft,
207 "SelectRight": (*BufHandler).SelectRight,
208 "WordRight": (*BufHandler).WordRight,
209 "WordLeft": (*BufHandler).WordLeft,
210 "SelectWordRight": (*BufHandler).SelectWordRight,
211 "SelectWordLeft": (*BufHandler).SelectWordLeft,
212 "DeleteWordRight": (*BufHandler).DeleteWordRight,
213 "DeleteWordLeft": (*BufHandler).DeleteWordLeft,
214 "SelectLine": (*BufHandler).SelectLine,
215 "SelectToStartOfLine": (*BufHandler).SelectToStartOfLine,
216 "SelectToEndOfLine": (*BufHandler).SelectToEndOfLine,
217 "ParagraphPrevious": (*BufHandler).ParagraphPrevious,
218 "ParagraphNext": (*BufHandler).ParagraphNext,
219 "InsertNewline": (*BufHandler).InsertNewline,
220 "InsertSpace": (*BufHandler).InsertSpace,
221 "Backspace": (*BufHandler).Backspace,
222 "Delete": (*BufHandler).Delete,
223 "InsertTab": (*BufHandler).InsertTab,
224 "Save": (*BufHandler).Save,
225 "SaveAll": (*BufHandler).SaveAll,
226 "SaveAs": (*BufHandler).SaveAs,
227 "Find": (*BufHandler).Find,
228 "FindNext": (*BufHandler).FindNext,
229 "FindPrevious": (*BufHandler).FindPrevious,
230 "Center": (*BufHandler).Center,
231 "Undo": (*BufHandler).Undo,
232 "Redo": (*BufHandler).Redo,
233 "Copy": (*BufHandler).Copy,
234 "Cut": (*BufHandler).Cut,
235 "CutLine": (*BufHandler).CutLine,
236 "DuplicateLine": (*BufHandler).DuplicateLine,
237 "DeleteLine": (*BufHandler).DeleteLine,
238 "MoveLinesUp": (*BufHandler).MoveLinesUp,
239 "MoveLinesDown": (*BufHandler).MoveLinesDown,
240 "IndentSelection": (*BufHandler).IndentSelection,
241 "OutdentSelection": (*BufHandler).OutdentSelection,
242 "OutdentLine": (*BufHandler).OutdentLine,
243 "Paste": (*BufHandler).Paste,
244 "PastePrimary": (*BufHandler).PastePrimary,
245 "SelectAll": (*BufHandler).SelectAll,
246 "OpenFile": (*BufHandler).OpenFile,
247 "Start": (*BufHandler).Start,
248 "End": (*BufHandler).End,
249 "PageUp": (*BufHandler).PageUp,
250 "PageDown": (*BufHandler).PageDown,
251 "SelectPageUp": (*BufHandler).SelectPageUp,
252 "SelectPageDown": (*BufHandler).SelectPageDown,
253 "HalfPageUp": (*BufHandler).HalfPageUp,
254 "HalfPageDown": (*BufHandler).HalfPageDown,
255 "StartOfLine": (*BufHandler).StartOfLine,
256 "EndOfLine": (*BufHandler).EndOfLine,
257 "ToggleHelp": (*BufHandler).ToggleHelp,
258 "ToggleKeyMenu": (*BufHandler).ToggleKeyMenu,
259 "ToggleRuler": (*BufHandler).ToggleRuler,
260 "JumpLine": (*BufHandler).JumpLine,
261 "ClearStatus": (*BufHandler).ClearStatus,
262 "ShellMode": (*BufHandler).ShellMode,
263 "CommandMode": (*BufHandler).CommandMode,
264 "ToggleOverwriteMode": (*BufHandler).ToggleOverwriteMode,
265 "Escape": (*BufHandler).Escape,
266 "Quit": (*BufHandler).Quit,
267 "QuitAll": (*BufHandler).QuitAll,
268 "AddTab": (*BufHandler).AddTab,
269 "PreviousTab": (*BufHandler).PreviousTab,
270 "NextTab": (*BufHandler).NextTab,
271 "NextSplit": (*BufHandler).NextSplit,
272 "PreviousSplit": (*BufHandler).PreviousSplit,
273 "Unsplit": (*BufHandler).Unsplit,
274 "VSplit": (*BufHandler).VSplitBinding,
275 "HSplit": (*BufHandler).HSplitBinding,
276 "ToggleMacro": (*BufHandler).ToggleMacro,
277 "PlayMacro": (*BufHandler).PlayMacro,
278 "Suspend": (*BufHandler).Suspend,
279 "ScrollUp": (*BufHandler).ScrollUpAction,
280 "ScrollDown": (*BufHandler).ScrollDownAction,
281 "SpawnMultiCursor": (*BufHandler).SpawnMultiCursor,
282 "SpawnMultiCursorSelect": (*BufHandler).SpawnMultiCursorSelect,
283 "RemoveMultiCursor": (*BufHandler).RemoveMultiCursor,
284 "RemoveAllMultiCursors": (*BufHandler).RemoveAllMultiCursors,
285 "SkipMultiCursor": (*BufHandler).SkipMultiCursor,
286 "JumpToMatchingBrace": (*BufHandler).JumpToMatchingBrace,
288 // This was changed to InsertNewline but I don't want to break backwards compatibility
289 "InsertEnter": (*BufHandler).InsertNewline,
291 var BufMouseActions = map[string]BufMouseAction{
292 "MousePress": (*BufHandler).MousePress,
293 "MouseMultiCursor": (*BufHandler).MouseMultiCursor,