]> git.lizzy.rs Git - micro.git/blob - cmd/micro/bindings.go
2f623fb116071ed5b9f9a5f5c38c34e6664f2b24
[micro.git] / cmd / micro / bindings.go
1 package main
2
3 import (
4         "encoding/json"
5         "io/ioutil"
6         "os"
7         "strconv"
8         "strings"
9         "time"
10
11         "github.com/mitchellh/go-homedir"
12         "github.com/zyedidia/clipboard"
13         "github.com/zyedidia/tcell"
14 )
15
16 var bindings map[Key]func(*View) bool
17 var helpBinding string
18
19 var bindingActions = map[string]func(*View) bool{
20         "CursorUp":            (*View).CursorUp,
21         "CursorDown":          (*View).CursorDown,
22         "CursorPageUp":        (*View).CursorPageUp,
23         "CursorPageDown":      (*View).CursorPageDown,
24         "CursorLeft":          (*View).CursorLeft,
25         "CursorRight":         (*View).CursorRight,
26         "CursorStart":         (*View).CursorStart,
27         "CursorEnd":           (*View).CursorEnd,
28         "SelectToStart":       (*View).SelectToStart,
29         "SelectToEnd":         (*View).SelectToEnd,
30         "SelectUp":            (*View).SelectUp,
31         "SelectDown":          (*View).SelectDown,
32         "SelectLeft":          (*View).SelectLeft,
33         "SelectRight":         (*View).SelectRight,
34         "WordRight":           (*View).WordRight,
35         "WordLeft":            (*View).WordLeft,
36         "SelectWordRight":     (*View).SelectWordRight,
37         "SelectWordLeft":      (*View).SelectWordLeft,
38         "DeleteWordRight":     (*View).DeleteWordRight,
39         "DeleteWordLeft":      (*View).DeleteWordLeft,
40         "SelectToStartOfLine": (*View).SelectToStartOfLine,
41         "SelectToEndOfLine":   (*View).SelectToEndOfLine,
42         "InsertEnter":         (*View).InsertEnter,
43         "InsertSpace":         (*View).InsertSpace,
44         "Backspace":           (*View).Backspace,
45         "Delete":              (*View).Delete,
46         "InsertTab":           (*View).InsertTab,
47         "Save":                (*View).Save,
48         "Find":                (*View).Find,
49         "FindNext":            (*View).FindNext,
50         "FindPrevious":        (*View).FindPrevious,
51         "Undo":                (*View).Undo,
52         "Redo":                (*View).Redo,
53         "Copy":                (*View).Copy,
54         "Cut":                 (*View).Cut,
55         "CutLine":             (*View).CutLine,
56         "DuplicateLine":       (*View).DuplicateLine,
57         "Paste":               (*View).Paste,
58         "SelectAll":           (*View).SelectAll,
59         "OpenFile":            (*View).OpenFile,
60         "Start":               (*View).Start,
61         "End":                 (*View).End,
62         "PageUp":              (*View).PageUp,
63         "PageDown":            (*View).PageDown,
64         "HalfPageUp":          (*View).HalfPageUp,
65         "HalfPageDown":        (*View).HalfPageDown,
66         "StartOfLine":         (*View).StartOfLine,
67         "EndOfLine":           (*View).EndOfLine,
68         "ToggleHelp":          (*View).ToggleHelp,
69         "ToggleRuler":         (*View).ToggleRuler,
70         "JumpLine":            (*View).JumpLine,
71         "ClearStatus":         (*View).ClearStatus,
72         "ShellMode":           (*View).ShellMode,
73         "CommandMode":         (*View).CommandMode,
74         "Quit":                (*View).Quit,
75 }
76
77 var bindingKeys = map[string]Key{
78         "Up":             Key{tcell.KeyUp, tcell.ModNone, 0},
79         "Down":           Key{tcell.KeyDown, tcell.ModNone, 0},
80         "Right":          Key{tcell.KeyRight, tcell.ModNone, 0},
81         "Left":           Key{tcell.KeyLeft, tcell.ModNone, 0},
82         "AltUp":          Key{tcell.KeyUp, tcell.ModAlt, 0},
83         "AltDown":        Key{tcell.KeyDown, tcell.ModAlt, 0},
84         "AltLeft":        Key{tcell.KeyLeft, tcell.ModAlt, 0},
85         "AltRight":       Key{tcell.KeyRight, tcell.ModAlt, 0},
86         "CtrlUp":         Key{tcell.KeyUp, tcell.ModCtrl, 0},
87         "CtrlDown":       Key{tcell.KeyDown, tcell.ModCtrl, 0},
88         "CtrlLeft":       Key{tcell.KeyLeft, tcell.ModCtrl, 0},
89         "CtrlRight":      Key{tcell.KeyRight, tcell.ModCtrl, 0},
90         "ShiftUp":        Key{tcell.KeyUp, tcell.ModShift, 0},
91         "ShiftDown":      Key{tcell.KeyDown, tcell.ModShift, 0},
92         "ShiftLeft":      Key{tcell.KeyLeft, tcell.ModShift, 0},
93         "ShiftRight":     Key{tcell.KeyRight, tcell.ModShift, 0},
94         "AltShiftUp":     Key{tcell.KeyUp, tcell.ModShift | tcell.ModAlt, 0},
95         "AltShiftDown":   Key{tcell.KeyDown, tcell.ModShift | tcell.ModAlt, 0},
96         "AltShiftLeft":   Key{tcell.KeyLeft, tcell.ModShift | tcell.ModAlt, 0},
97         "AltShiftRight":  Key{tcell.KeyRight, tcell.ModShift | tcell.ModAlt, 0},
98         "CtrlShiftUp":    Key{tcell.KeyUp, tcell.ModShift | tcell.ModCtrl, 0},
99         "CtrlShiftDown":  Key{tcell.KeyDown, tcell.ModShift | tcell.ModCtrl, 0},
100         "CtrlShiftLeft":  Key{tcell.KeyLeft, tcell.ModShift | tcell.ModCtrl, 0},
101         "CtrlShiftRight": Key{tcell.KeyRight, tcell.ModShift | tcell.ModCtrl, 0},
102         "UpLeft":         Key{tcell.KeyUpLeft, tcell.ModNone, 0},
103         "UpRight":        Key{tcell.KeyUpRight, tcell.ModNone, 0},
104         "DownLeft":       Key{tcell.KeyDownLeft, tcell.ModNone, 0},
105         "DownRight":      Key{tcell.KeyDownRight, tcell.ModNone, 0},
106         "Center":         Key{tcell.KeyCenter, tcell.ModNone, 0},
107         "PageUp":         Key{tcell.KeyPgUp, tcell.ModNone, 0},
108         "PageDown":       Key{tcell.KeyPgDn, tcell.ModNone, 0},
109         "Home":           Key{tcell.KeyHome, tcell.ModNone, 0},
110         "End":            Key{tcell.KeyEnd, tcell.ModNone, 0},
111         "Insert":         Key{tcell.KeyInsert, tcell.ModNone, 0},
112         "Delete":         Key{tcell.KeyDelete, tcell.ModNone, 0},
113         "Help":           Key{tcell.KeyHelp, tcell.ModNone, 0},
114         "Exit":           Key{tcell.KeyExit, tcell.ModNone, 0},
115         "Clear":          Key{tcell.KeyClear, tcell.ModNone, 0},
116         "Cancel":         Key{tcell.KeyCancel, tcell.ModNone, 0},
117         "Print":          Key{tcell.KeyPrint, tcell.ModNone, 0},
118         "Pause":          Key{tcell.KeyPause, tcell.ModNone, 0},
119         "Backtab":        Key{tcell.KeyBacktab, tcell.ModNone, 0},
120         "F1":             Key{tcell.KeyF1, tcell.ModNone, 0},
121         "F2":             Key{tcell.KeyF2, tcell.ModNone, 0},
122         "F3":             Key{tcell.KeyF3, tcell.ModNone, 0},
123         "F4":             Key{tcell.KeyF4, tcell.ModNone, 0},
124         "F5":             Key{tcell.KeyF5, tcell.ModNone, 0},
125         "F6":             Key{tcell.KeyF6, tcell.ModNone, 0},
126         "F7":             Key{tcell.KeyF7, tcell.ModNone, 0},
127         "F8":             Key{tcell.KeyF8, tcell.ModNone, 0},
128         "F9":             Key{tcell.KeyF9, tcell.ModNone, 0},
129         "F10":            Key{tcell.KeyF10, tcell.ModNone, 0},
130         "F11":            Key{tcell.KeyF11, tcell.ModNone, 0},
131         "F12":            Key{tcell.KeyF12, tcell.ModNone, 0},
132         "F13":            Key{tcell.KeyF13, tcell.ModNone, 0},
133         "F14":            Key{tcell.KeyF14, tcell.ModNone, 0},
134         "F15":            Key{tcell.KeyF15, tcell.ModNone, 0},
135         "F16":            Key{tcell.KeyF16, tcell.ModNone, 0},
136         "F17":            Key{tcell.KeyF17, tcell.ModNone, 0},
137         "F18":            Key{tcell.KeyF18, tcell.ModNone, 0},
138         "F19":            Key{tcell.KeyF19, tcell.ModNone, 0},
139         "F20":            Key{tcell.KeyF20, tcell.ModNone, 0},
140         "F21":            Key{tcell.KeyF21, tcell.ModNone, 0},
141         "F22":            Key{tcell.KeyF22, tcell.ModNone, 0},
142         "F23":            Key{tcell.KeyF23, tcell.ModNone, 0},
143         "F24":            Key{tcell.KeyF24, tcell.ModNone, 0},
144         "F25":            Key{tcell.KeyF25, tcell.ModNone, 0},
145         "F26":            Key{tcell.KeyF26, tcell.ModNone, 0},
146         "F27":            Key{tcell.KeyF27, tcell.ModNone, 0},
147         "F28":            Key{tcell.KeyF28, tcell.ModNone, 0},
148         "F29":            Key{tcell.KeyF29, tcell.ModNone, 0},
149         "F30":            Key{tcell.KeyF30, tcell.ModNone, 0},
150         "F31":            Key{tcell.KeyF31, tcell.ModNone, 0},
151         "F32":            Key{tcell.KeyF32, tcell.ModNone, 0},
152         "F33":            Key{tcell.KeyF33, tcell.ModNone, 0},
153         "F34":            Key{tcell.KeyF34, tcell.ModNone, 0},
154         "F35":            Key{tcell.KeyF35, tcell.ModNone, 0},
155         "F36":            Key{tcell.KeyF36, tcell.ModNone, 0},
156         "F37":            Key{tcell.KeyF37, tcell.ModNone, 0},
157         "F38":            Key{tcell.KeyF38, tcell.ModNone, 0},
158         "F39":            Key{tcell.KeyF39, tcell.ModNone, 0},
159         "F40":            Key{tcell.KeyF40, tcell.ModNone, 0},
160         "F41":            Key{tcell.KeyF41, tcell.ModNone, 0},
161         "F42":            Key{tcell.KeyF42, tcell.ModNone, 0},
162         "F43":            Key{tcell.KeyF43, tcell.ModNone, 0},
163         "F44":            Key{tcell.KeyF44, tcell.ModNone, 0},
164         "F45":            Key{tcell.KeyF45, tcell.ModNone, 0},
165         "F46":            Key{tcell.KeyF46, tcell.ModNone, 0},
166         "F47":            Key{tcell.KeyF47, tcell.ModNone, 0},
167         "F48":            Key{tcell.KeyF48, tcell.ModNone, 0},
168         "F49":            Key{tcell.KeyF49, tcell.ModNone, 0},
169         "F50":            Key{tcell.KeyF50, tcell.ModNone, 0},
170         "F51":            Key{tcell.KeyF51, tcell.ModNone, 0},
171         "F52":            Key{tcell.KeyF52, tcell.ModNone, 0},
172         "F53":            Key{tcell.KeyF53, tcell.ModNone, 0},
173         "F54":            Key{tcell.KeyF54, tcell.ModNone, 0},
174         "F55":            Key{tcell.KeyF55, tcell.ModNone, 0},
175         "F56":            Key{tcell.KeyF56, tcell.ModNone, 0},
176         "F57":            Key{tcell.KeyF57, tcell.ModNone, 0},
177         "F58":            Key{tcell.KeyF58, tcell.ModNone, 0},
178         "F59":            Key{tcell.KeyF59, tcell.ModNone, 0},
179         "F60":            Key{tcell.KeyF60, tcell.ModNone, 0},
180         "F61":            Key{tcell.KeyF61, tcell.ModNone, 0},
181         "F62":            Key{tcell.KeyF62, tcell.ModNone, 0},
182         "F63":            Key{tcell.KeyF63, tcell.ModNone, 0},
183         "F64":            Key{tcell.KeyF64, tcell.ModNone, 0},
184         "CtrlSpace":      Key{tcell.KeyCtrlSpace, tcell.ModCtrl, 0},
185         "CtrlA":          Key{tcell.KeyCtrlA, tcell.ModCtrl, 0},
186         "CtrlB":          Key{tcell.KeyCtrlB, tcell.ModCtrl, 0},
187         "CtrlC":          Key{tcell.KeyCtrlC, tcell.ModCtrl, 0},
188         "CtrlD":          Key{tcell.KeyCtrlD, tcell.ModCtrl, 0},
189         "CtrlE":          Key{tcell.KeyCtrlE, tcell.ModCtrl, 0},
190         "CtrlF":          Key{tcell.KeyCtrlF, tcell.ModCtrl, 0},
191         "CtrlG":          Key{tcell.KeyCtrlG, tcell.ModCtrl, 0},
192         "CtrlH":          Key{tcell.KeyCtrlH, tcell.ModCtrl, 0},
193         "CtrlI":          Key{tcell.KeyCtrlI, tcell.ModCtrl, 0},
194         "CtrlJ":          Key{tcell.KeyCtrlJ, tcell.ModCtrl, 0},
195         "CtrlK":          Key{tcell.KeyCtrlK, tcell.ModCtrl, 0},
196         "CtrlL":          Key{tcell.KeyCtrlL, tcell.ModCtrl, 0},
197         "CtrlM":          Key{tcell.KeyCtrlM, tcell.ModCtrl, 0},
198         "CtrlN":          Key{tcell.KeyCtrlN, tcell.ModCtrl, 0},
199         "CtrlO":          Key{tcell.KeyCtrlO, tcell.ModCtrl, 0},
200         "CtrlP":          Key{tcell.KeyCtrlP, tcell.ModCtrl, 0},
201         "CtrlQ":          Key{tcell.KeyCtrlQ, tcell.ModCtrl, 0},
202         "CtrlR":          Key{tcell.KeyCtrlR, tcell.ModCtrl, 0},
203         "CtrlS":          Key{tcell.KeyCtrlS, tcell.ModCtrl, 0},
204         "CtrlT":          Key{tcell.KeyCtrlT, tcell.ModCtrl, 0},
205         "CtrlU":          Key{tcell.KeyCtrlU, tcell.ModCtrl, 0},
206         "CtrlV":          Key{tcell.KeyCtrlV, tcell.ModCtrl, 0},
207         "CtrlW":          Key{tcell.KeyCtrlW, tcell.ModCtrl, 0},
208         "CtrlX":          Key{tcell.KeyCtrlX, tcell.ModCtrl, 0},
209         "CtrlY":          Key{tcell.KeyCtrlY, tcell.ModCtrl, 0},
210         "CtrlZ":          Key{tcell.KeyCtrlZ, tcell.ModCtrl, 0},
211         "CtrlLeftSq":     Key{tcell.KeyCtrlLeftSq, tcell.ModCtrl, 0},
212         "CtrlBackslash":  Key{tcell.KeyCtrlBackslash, tcell.ModCtrl, 0},
213         "CtrlRightSq":    Key{tcell.KeyCtrlRightSq, tcell.ModCtrl, 0},
214         "CtrlCarat":      Key{tcell.KeyCtrlCarat, tcell.ModCtrl, 0},
215         "CtrlUnderscore": Key{tcell.KeyCtrlUnderscore, tcell.ModCtrl, 0},
216         "Backspace":      Key{tcell.KeyBackspace, tcell.ModNone, 0},
217         "Tab":            Key{tcell.KeyTab, tcell.ModNone, 0},
218         "Esc":            Key{tcell.KeyEsc, tcell.ModNone, 0},
219         "Escape":         Key{tcell.KeyEscape, tcell.ModNone, 0},
220         "Enter":          Key{tcell.KeyEnter, tcell.ModNone, 0},
221         "Backspace2":     Key{tcell.KeyBackspace2, tcell.ModNone, 0},
222
223         // I renamed these keys to PageUp and PageDown but I don't want to break someone's keybindings
224         "PgUp":   Key{tcell.KeyPgUp, tcell.ModNone, 0},
225         "PgDown": Key{tcell.KeyPgDn, tcell.ModNone, 0},
226 }
227
228 // The Key struct holds the data for a keypress (keycode + modifiers)
229 type Key struct {
230         keyCode   tcell.Key
231         modifiers tcell.ModMask
232         r         rune
233 }
234
235 // InitBindings initializes the keybindings for micro
236 func InitBindings() {
237         bindings = make(map[Key]func(*View) bool)
238
239         var parsed map[string]string
240         defaults := DefaultBindings()
241
242         filename := configDir + "/bindings.json"
243         if _, e := os.Stat(filename); e == nil {
244                 input, err := ioutil.ReadFile(filename)
245                 if err != nil {
246                         TermMessage("Error reading bindings.json file: " + err.Error())
247                         return
248                 }
249
250                 err = json.Unmarshal(input, &parsed)
251                 if err != nil {
252                         TermMessage("Error reading bindings.json:", err.Error())
253                 }
254         }
255
256         parseBindings(defaults)
257         parseBindings(parsed)
258 }
259
260 func parseBindings(userBindings map[string]string) {
261         for k, v := range userBindings {
262                 BindKey(k, v)
263         }
264 }
265
266 // BindKey takes a key and an action and binds the two together
267 func BindKey(k, v string) {
268         var key Key
269         if strings.Contains(k, "Alt-") {
270                 split := strings.Split(k, "-")
271                 if len(split[1]) > 1 {
272                         key = Key{bindingKeys[split[1]].keyCode, bindingKeys[k].modifiers | tcell.ModAlt, 0}
273                 } else {
274                         key = Key{tcell.KeyRune, tcell.ModAlt, rune(k[len(k)-1])}
275                 }
276         } else {
277                 key = bindingKeys[k]
278         }
279
280         action := bindingActions[v]
281         if _, ok := bindingActions[v]; !ok {
282                 // If the user seems to be binding a function that doesn't exist
283                 // We hope that it's a lua function that exists and bind it to that
284                 action = LuaFunctionBinding(v)
285         }
286
287         bindings[key] = action
288         if v == "ToggleHelp" {
289                 helpBinding = k
290         }
291 }
292
293 // DefaultBindings returns a map containing micro's default keybindings
294 func DefaultBindings() map[string]string {
295         return map[string]string{
296                 "Up":             "CursorUp",
297                 "Down":           "CursorDown",
298                 "Right":          "CursorRight",
299                 "Left":           "CursorLeft",
300                 "ShiftUp":        "SelectUp",
301                 "ShiftDown":      "SelectDown",
302                 "ShiftLeft":      "SelectLeft",
303                 "ShiftRight":     "SelectRight",
304                 "AltLeft":        "WordLeft",
305                 "AltRight":       "WordRight",
306                 "AltShiftRight":  "SelectWordRight",
307                 "AltShiftLeft":   "SelectWordLeft",
308                 "CtrlLeft":       "StartOfLine",
309                 "CtrlRight":      "EndOfLine",
310                 "CtrlShiftLeft":  "SelectToStartOfLine",
311                 "CtrlShiftRight": "SelectToEndOfLine",
312                 "CtrlUp":         "CursorStart",
313                 "CtrlDown":       "CursorEnd",
314                 "CtrlShiftUp":    "SelectToStart",
315                 "CtrlShiftDown":  "SelectToEnd",
316                 "Enter":          "InsertEnter",
317                 "Space":          "InsertSpace",
318                 "Backspace":      "Backspace",
319                 "Backspace2":     "Backspace",
320                 "Alt-Backspace":  "DeleteWordLeft",
321                 "Alt-Backspace2": "DeleteWordLeft",
322                 "Tab":            "InsertTab",
323                 "CtrlO":          "OpenFile",
324                 "CtrlS":          "Save",
325                 "CtrlF":          "Find",
326                 "CtrlN":          "FindNext",
327                 "CtrlP":          "FindPrevious",
328                 "CtrlZ":          "Undo",
329                 "CtrlY":          "Redo",
330                 "CtrlC":          "Copy",
331                 "CtrlX":          "Cut",
332                 "CtrlK":          "CutLine",
333                 "CtrlD":          "DuplicateLine",
334                 "CtrlV":          "Paste",
335                 "CtrlA":          "SelectAll",
336                 "Home":           "Start",
337                 "End":            "End",
338                 "PageUp":         "CursorPageUp",
339                 "PageDown":       "CursorPageDown",
340                 "CtrlG":          "ToggleHelp",
341                 "CtrlR":          "ToggleRuler",
342                 "CtrlL":          "JumpLine",
343                 "Delete":         "Delete",
344                 "Esc":            "ClearStatus",
345                 "CtrlB":          "ShellMode",
346                 "CtrlQ":          "Quit",
347                 "CtrlE":          "CommandMode",
348
349                 // Emacs-style keybindings
350                 "Alt-f": "WordRight",
351                 "Alt-b": "WordLeft",
352                 "Alt-a": "StartOfLine",
353                 "Alt-e": "EndOfLine",
354                 "Alt-p": "CursorUp",
355                 "Alt-n": "CursorDown",
356         }
357 }
358
359 // CursorUp moves the cursor up
360 func (v *View) CursorUp() bool {
361         if v.Cursor.HasSelection() {
362                 v.Cursor.SetLoc(v.Cursor.CurSelection[0])
363                 v.Cursor.ResetSelection()
364         }
365         v.Cursor.Up()
366         return true
367 }
368
369 // CursorDown moves the cursor down
370 func (v *View) CursorDown() bool {
371         if v.Cursor.HasSelection() {
372                 v.Cursor.SetLoc(v.Cursor.CurSelection[1])
373                 v.Cursor.ResetSelection()
374         }
375         v.Cursor.Down()
376         return true
377 }
378
379 // CursorLeft moves the cursor left
380 func (v *View) CursorLeft() bool {
381         if v.Cursor.HasSelection() {
382                 v.Cursor.SetLoc(v.Cursor.CurSelection[0])
383                 v.Cursor.ResetSelection()
384         } else {
385                 v.Cursor.Left()
386         }
387         return true
388 }
389
390 // CursorRight moves the cursor right
391 func (v *View) CursorRight() bool {
392         if v.Cursor.HasSelection() {
393                 v.Cursor.SetLoc(v.Cursor.CurSelection[1] - 1)
394                 v.Cursor.ResetSelection()
395         } else {
396                 v.Cursor.Right()
397         }
398         return true
399 }
400
401 // WordRight moves the cursor one word to the right
402 func (v *View) WordRight() bool {
403         v.Cursor.WordRight()
404         return true
405 }
406
407 // WordLeft moves the cursor one word to the left
408 func (v *View) WordLeft() bool {
409         v.Cursor.WordLeft()
410         return true
411 }
412
413 // SelectUp selects up one line
414 func (v *View) SelectUp() bool {
415         loc := v.Cursor.Loc()
416         if !v.Cursor.HasSelection() {
417                 v.Cursor.OrigSelection[0] = loc
418         }
419         v.Cursor.Up()
420         v.Cursor.SelectTo(v.Cursor.Loc())
421         return true
422 }
423
424 // SelectDown selects down one line
425 func (v *View) SelectDown() bool {
426         loc := v.Cursor.Loc()
427         if !v.Cursor.HasSelection() {
428                 v.Cursor.OrigSelection[0] = loc
429         }
430         v.Cursor.Down()
431         v.Cursor.SelectTo(v.Cursor.Loc())
432         return true
433 }
434
435 // SelectLeft selects the character to the left of the cursor
436 func (v *View) SelectLeft() bool {
437         loc := v.Cursor.Loc()
438         count := v.Buf.Len() - 1
439         if loc > count {
440                 loc = count
441         }
442         if !v.Cursor.HasSelection() {
443                 v.Cursor.OrigSelection[0] = loc
444         }
445         v.Cursor.Left()
446         v.Cursor.SelectTo(v.Cursor.Loc())
447         return true
448 }
449
450 // SelectRight selects the character to the right of the cursor
451 func (v *View) SelectRight() bool {
452         loc := v.Cursor.Loc()
453         count := v.Buf.Len() - 1
454         if loc > count {
455                 loc = count
456         }
457         if !v.Cursor.HasSelection() {
458                 v.Cursor.OrigSelection[0] = loc
459         }
460         v.Cursor.Right()
461         v.Cursor.SelectTo(v.Cursor.Loc())
462         return true
463 }
464
465 // SelectWordRight selects the word to the right of the cursor
466 func (v *View) SelectWordRight() bool {
467         loc := v.Cursor.Loc()
468         if !v.Cursor.HasSelection() {
469                 v.Cursor.OrigSelection[0] = loc
470         }
471         v.Cursor.WordRight()
472         v.Cursor.SelectTo(v.Cursor.Loc())
473         return true
474 }
475
476 // SelectWordLeft selects the word to the left of the cursor
477 func (v *View) SelectWordLeft() bool {
478         loc := v.Cursor.Loc()
479         if !v.Cursor.HasSelection() {
480                 v.Cursor.OrigSelection[0] = loc
481         }
482         v.Cursor.WordLeft()
483         v.Cursor.SelectTo(v.Cursor.Loc())
484         return true
485 }
486
487 // StartOfLine moves the cursor to the start of the line
488 func (v *View) StartOfLine() bool {
489         v.Cursor.Start()
490         return true
491 }
492
493 // EndOfLine moves the cursor to the end of the line
494 func (v *View) EndOfLine() bool {
495         v.Cursor.End()
496         return true
497 }
498
499 // SelectToStartOfLine selects to the start of the current line
500 func (v *View) SelectToStartOfLine() bool {
501         loc := v.Cursor.Loc()
502         if !v.Cursor.HasSelection() {
503                 v.Cursor.OrigSelection[0] = loc
504         }
505         v.Cursor.Start()
506         v.Cursor.SelectTo(v.Cursor.Loc())
507         return true
508 }
509
510 // SelectToEndOfLine selects to the end of the current line
511 func (v *View) SelectToEndOfLine() bool {
512         loc := v.Cursor.Loc()
513         if !v.Cursor.HasSelection() {
514                 v.Cursor.OrigSelection[0] = loc
515         }
516         v.Cursor.End()
517         v.Cursor.SelectTo(v.Cursor.Loc())
518         return true
519 }
520
521 // CursorStart moves the cursor to the start of the buffer
522 func (v *View) CursorStart() bool {
523         v.Cursor.X = 0
524         v.Cursor.Y = 0
525         return true
526 }
527
528 // CursorEnd moves the cursor to the end of the buffer
529 func (v *View) CursorEnd() bool {
530         v.Cursor.SetLoc(v.Buf.Len())
531         return true
532 }
533
534 // SelectToStart selects the text from the cursor to the start of the buffer
535 func (v *View) SelectToStart() bool {
536         loc := v.Cursor.Loc()
537         if !v.Cursor.HasSelection() {
538                 v.Cursor.OrigSelection[0] = loc
539         }
540         v.CursorStart()
541         v.Cursor.SelectTo(0)
542         return true
543 }
544
545 // SelectToEnd selects the text from the cursor to the end of the buffer
546 func (v *View) SelectToEnd() bool {
547         loc := v.Cursor.Loc()
548         if !v.Cursor.HasSelection() {
549                 v.Cursor.OrigSelection[0] = loc
550         }
551         v.CursorEnd()
552         v.Cursor.SelectTo(v.Buf.Len())
553         return true
554 }
555
556 // InsertSpace inserts a space
557 func (v *View) InsertSpace() bool {
558         if v.Cursor.HasSelection() {
559                 v.Cursor.DeleteSelection()
560                 v.Cursor.ResetSelection()
561         }
562         v.Buf.Insert(v.Cursor.Loc(), " ")
563         v.Cursor.Right()
564         return true
565 }
566
567 // InsertEnter inserts a newline plus possible some whitespace if autoindent is on
568 func (v *View) InsertEnter() bool {
569         // Insert a newline
570         if v.Cursor.HasSelection() {
571                 v.Cursor.DeleteSelection()
572                 v.Cursor.ResetSelection()
573         }
574
575         v.Buf.Insert(v.Cursor.Loc(), "\n")
576         ws := GetLeadingWhitespace(v.Buf.Lines[v.Cursor.Y])
577         v.Cursor.Right()
578
579         if settings["autoindent"].(bool) {
580                 v.Buf.Insert(v.Cursor.Loc(), ws)
581                 for i := 0; i < len(ws); i++ {
582                         v.Cursor.Right()
583                 }
584         }
585         v.Cursor.LastVisualX = v.Cursor.GetVisualX()
586         return true
587 }
588
589 // Backspace deletes the previous character
590 func (v *View) Backspace() bool {
591         // Delete a character
592         if v.Cursor.HasSelection() {
593                 v.Cursor.DeleteSelection()
594                 v.Cursor.ResetSelection()
595         } else if v.Cursor.Loc() > 0 {
596                 // We have to do something a bit hacky here because we want to
597                 // delete the line by first moving left and then deleting backwards
598                 // but the undo redo would place the cursor in the wrong place
599                 // So instead we move left, save the position, move back, delete
600                 // and restore the position
601
602                 // If the user is using spaces instead of tabs and they are deleting
603                 // whitespace at the start of the line, we should delete as if its a
604                 // tab (tabSize number of spaces)
605                 lineStart := v.Buf.Lines[v.Cursor.Y][:v.Cursor.X]
606                 tabSize := int(settings["tabsize"].(float64))
607                 if settings["tabstospaces"].(bool) && IsSpaces(lineStart) && len(lineStart) != 0 && len(lineStart)%tabSize == 0 {
608                         loc := v.Cursor.Loc()
609                         v.Cursor.SetLoc(loc - tabSize)
610                         cx, cy := v.Cursor.X, v.Cursor.Y
611                         v.Cursor.SetLoc(loc)
612                         v.Buf.Remove(loc-tabSize, loc)
613                         v.Cursor.X, v.Cursor.Y = cx, cy
614                 } else {
615                         v.Cursor.Left()
616                         cx, cy := v.Cursor.X, v.Cursor.Y
617                         v.Cursor.Right()
618                         loc := v.Cursor.Loc()
619                         v.Buf.Remove(loc-1, loc)
620                         v.Cursor.X, v.Cursor.Y = cx, cy
621                 }
622         }
623         v.Cursor.LastVisualX = v.Cursor.GetVisualX()
624         return true
625 }
626
627 // DeleteWordRight deletes the word to the right of the cursor
628 func (v *View) DeleteWordRight() bool {
629         v.SelectWordRight()
630         if v.Cursor.HasSelection() {
631                 v.Cursor.DeleteSelection()
632                 v.Cursor.ResetSelection()
633         }
634         return true
635 }
636
637 // DeleteWordLeft deletes the word to the left of the cursor
638 func (v *View) DeleteWordLeft() bool {
639         v.SelectWordLeft()
640         if v.Cursor.HasSelection() {
641                 v.Cursor.DeleteSelection()
642                 v.Cursor.ResetSelection()
643         }
644         return true
645 }
646
647 // Delete deletes the next character
648 func (v *View) Delete() bool {
649         if v.Cursor.HasSelection() {
650                 v.Cursor.DeleteSelection()
651                 v.Cursor.ResetSelection()
652         } else {
653                 loc := v.Cursor.Loc()
654                 if loc < v.Buf.Len() {
655                         v.Buf.Remove(loc, loc+1)
656                 }
657         }
658         return true
659 }
660
661 // InsertTab inserts a tab or spaces
662 func (v *View) InsertTab() bool {
663         // Insert a tab
664         if v.Cursor.HasSelection() {
665                 v.Cursor.DeleteSelection()
666                 v.Cursor.ResetSelection()
667         }
668         if settings["tabstospaces"].(bool) {
669                 tabSize := int(settings["tabsize"].(float64))
670                 v.Buf.Insert(v.Cursor.Loc(), Spaces(tabSize))
671                 for i := 0; i < tabSize; i++ {
672                         v.Cursor.Right()
673                 }
674         } else {
675                 v.Buf.Insert(v.Cursor.Loc(), "\t")
676                 v.Cursor.Right()
677         }
678         return true
679 }
680
681 // Save the buffer to disk
682 func (v *View) Save() bool {
683         if v.helpOpen {
684                 // We can't save the help text
685                 return false
686         }
687         // If this is an empty buffer, ask for a filename
688         if v.Buf.Path == "" {
689                 filename, canceled := messenger.Prompt("Filename: ", "Save")
690                 if !canceled {
691                         v.Buf.Path = filename
692                         v.Buf.Name = filename
693                 } else {
694                         return true
695                 }
696         }
697         err := v.Buf.Save()
698         if err != nil {
699                 messenger.Error(err.Error())
700         } else {
701                 messenger.Message("Saved " + v.Buf.Path)
702         }
703         return true
704 }
705
706 // Find opens a prompt and searches forward for the input
707 func (v *View) Find() bool {
708         if v.Cursor.HasSelection() {
709                 searchStart = v.Cursor.CurSelection[1]
710         } else {
711                 searchStart = ToCharPos(v.Cursor.X, v.Cursor.Y, v.Buf)
712         }
713         BeginSearch()
714         return true
715 }
716
717 // FindNext searches forwards for the last used search term
718 func (v *View) FindNext() bool {
719         if v.Cursor.HasSelection() {
720                 searchStart = v.Cursor.CurSelection[1]
721         } else {
722                 searchStart = ToCharPos(v.Cursor.X, v.Cursor.Y, v.Buf)
723         }
724         messenger.Message("Finding: " + lastSearch)
725         Search(lastSearch, v, true)
726         return true
727 }
728
729 // FindPrevious searches backwards for the last used search term
730 func (v *View) FindPrevious() bool {
731         if v.Cursor.HasSelection() {
732                 searchStart = v.Cursor.CurSelection[0]
733         } else {
734                 searchStart = ToCharPos(v.Cursor.X, v.Cursor.Y, v.Buf)
735         }
736         messenger.Message("Finding: " + lastSearch)
737         Search(lastSearch, v, false)
738         return true
739 }
740
741 // Undo undoes the last action
742 func (v *View) Undo() bool {
743         v.Buf.Undo()
744         messenger.Message("Undid action")
745         return true
746 }
747
748 // Redo redoes the last action
749 func (v *View) Redo() bool {
750         v.Buf.Redo()
751         messenger.Message("Redid action")
752         return true
753 }
754
755 // Copy the selection to the system clipboard
756 func (v *View) Copy() bool {
757         if v.Cursor.HasSelection() {
758                 clipboard.WriteAll(v.Cursor.GetSelection())
759                 v.freshClip = true
760                 messenger.Message("Copied selection")
761         }
762         return true
763 }
764
765 // CutLine cuts the current line to the clipboard
766 func (v *View) CutLine() bool {
767         v.Cursor.SelectLine()
768         if !v.Cursor.HasSelection() {
769                 return false
770         }
771         if v.freshClip == true {
772                 if v.Cursor.HasSelection() {
773                         if clip, err := clipboard.ReadAll(); err != nil {
774                                 messenger.Error(err)
775                         } else {
776                                 clipboard.WriteAll(clip + v.Cursor.GetSelection())
777                         }
778                 }
779         } else if time.Since(v.lastCutTime)/time.Second > 10*time.Second || v.freshClip == false {
780                 v.Copy()
781         }
782         v.freshClip = true
783         v.lastCutTime = time.Now()
784         v.Cursor.DeleteSelection()
785         v.Cursor.ResetSelection()
786         messenger.Message("Cut line")
787         return true
788 }
789
790 // Cut the selection to the system clipboard
791 func (v *View) Cut() bool {
792         if v.Cursor.HasSelection() {
793                 clipboard.WriteAll(v.Cursor.GetSelection())
794                 v.Cursor.DeleteSelection()
795                 v.Cursor.ResetSelection()
796                 v.freshClip = true
797                 messenger.Message("Cut selection")
798         }
799         return true
800 }
801
802 // DuplicateLine duplicates the current line
803 func (v *View) DuplicateLine() bool {
804         v.Cursor.End()
805         v.Buf.Insert(v.Cursor.Loc(), "\n"+v.Buf.Lines[v.Cursor.Y])
806         v.Cursor.Right()
807         messenger.Message("Duplicated line")
808         return true
809 }
810
811 // Paste whatever is in the system clipboard into the buffer
812 // Delete and paste if the user has a selection
813 func (v *View) Paste() bool {
814         if v.Cursor.HasSelection() {
815                 v.Cursor.DeleteSelection()
816                 v.Cursor.ResetSelection()
817         }
818         clip, _ := clipboard.ReadAll()
819         v.Buf.Insert(v.Cursor.Loc(), clip)
820         v.Cursor.SetLoc(v.Cursor.Loc() + Count(clip))
821         v.freshClip = false
822         messenger.Message("Pasted clipboard")
823         return true
824 }
825
826 // SelectAll selects the entire buffer
827 func (v *View) SelectAll() bool {
828         v.Cursor.CurSelection[0] = 0
829         v.Cursor.CurSelection[1] = v.Buf.Len()
830         // Put the cursor at the beginning
831         v.Cursor.X = 0
832         v.Cursor.Y = 0
833         return true
834 }
835
836 // OpenFile opens a new file in the buffer
837 func (v *View) OpenFile() bool {
838         if v.CanClose("Continue? (yes, no, save) ") {
839                 filename, canceled := messenger.Prompt("File to open: ", "Open")
840                 if canceled {
841                         return true
842                 }
843                 home, _ := homedir.Dir()
844                 filename = strings.Replace(filename, "~", home, 1)
845                 file, err := ioutil.ReadFile(filename)
846
847                 if err != nil {
848                         messenger.Error(err.Error())
849                         return true
850                 }
851                 buf := NewBuffer(string(file), filename)
852                 v.OpenBuffer(buf)
853         }
854         return true
855 }
856
857 // Start moves the viewport to the start of the buffer
858 func (v *View) Start() bool {
859         v.Topline = 0
860         return false
861 }
862
863 // End moves the viewport to the end of the buffer
864 func (v *View) End() bool {
865         if v.height > v.Buf.NumLines {
866                 v.Topline = 0
867         } else {
868                 v.Topline = v.Buf.NumLines - v.height
869         }
870         return false
871 }
872
873 // PageUp scrolls the view up a page
874 func (v *View) PageUp() bool {
875         if v.Topline > v.height {
876                 v.ScrollUp(v.height)
877         } else {
878                 v.Topline = 0
879         }
880         return false
881 }
882
883 // PageDown scrolls the view down a page
884 func (v *View) PageDown() bool {
885         if v.Buf.NumLines-(v.Topline+v.height) > v.height {
886                 v.ScrollDown(v.height)
887         } else if v.Buf.NumLines >= v.height {
888                 v.Topline = v.Buf.NumLines - v.height
889         }
890         return false
891 }
892
893 // CursorPageUp places the cursor a page up
894 func (v *View) CursorPageUp() bool {
895         if v.Cursor.HasSelection() {
896                 v.Cursor.SetLoc(v.Cursor.CurSelection[0])
897                 v.Cursor.ResetSelection()
898         }
899         v.Cursor.UpN(v.height)
900         return true
901 }
902
903 // CursorPageDown places the cursor a page up
904 func (v *View) CursorPageDown() bool {
905         if v.Cursor.HasSelection() {
906                 v.Cursor.SetLoc(v.Cursor.CurSelection[1])
907                 v.Cursor.ResetSelection()
908         }
909         v.Cursor.DownN(v.height)
910         return true
911 }
912
913 // HalfPageUp scrolls the view up half a page
914 func (v *View) HalfPageUp() bool {
915         if v.Topline > v.height/2 {
916                 v.ScrollUp(v.height / 2)
917         } else {
918                 v.Topline = 0
919         }
920         return false
921 }
922
923 // HalfPageDown scrolls the view down half a page
924 func (v *View) HalfPageDown() bool {
925         if v.Buf.NumLines-(v.Topline+v.height) > v.height/2 {
926                 v.ScrollDown(v.height / 2)
927         } else {
928                 if v.Buf.NumLines >= v.height {
929                         v.Topline = v.Buf.NumLines - v.height
930                 }
931         }
932         return false
933 }
934
935 // ToggleRuler turns line numbers off and on
936 func (v *View) ToggleRuler() bool {
937         if settings["ruler"] == false {
938                 settings["ruler"] = true
939                 messenger.Message("Enabled ruler")
940         } else {
941                 settings["ruler"] = false
942                 messenger.Message("Disabled ruler")
943         }
944         return false
945 }
946
947 // JumpLine jumps to a line and moves the view accordingly.
948 func (v *View) JumpLine() bool {
949         // Prompt for line number
950         linestring, canceled := messenger.Prompt("Jump to line # ", "LineNumber")
951         if canceled {
952                 return false
953         }
954         lineint, err := strconv.Atoi(linestring)
955         lineint = lineint - 1 // fix offset
956         if err != nil {
957                 messenger.Error(err) // return errors
958                 return false
959         }
960         // Move cursor and view if possible.
961         if lineint < v.Buf.NumLines {
962                 v.Cursor.X = 0
963                 v.Cursor.Y = lineint
964                 return true
965         }
966         messenger.Error("Only ", v.Buf.NumLines, " lines to jump")
967         return false
968 }
969
970 // ClearStatus clears the messenger bar
971 func (v *View) ClearStatus() bool {
972         messenger.Message("")
973         return false
974 }
975
976 // ToggleHelp toggles the help screen
977 func (v *View) ToggleHelp() bool {
978         if !v.helpOpen {
979                 v.lastBuffer = v.Buf
980                 helpBuffer := NewBuffer(helpTxt, "help.md")
981                 helpBuffer.Name = "Help"
982                 v.helpOpen = true
983                 v.OpenBuffer(helpBuffer)
984         } else {
985                 v.OpenBuffer(v.lastBuffer)
986                 v.helpOpen = false
987         }
988         return true
989 }
990
991 // ShellMode opens a terminal to run a shell command
992 func (v *View) ShellMode() bool {
993         input, canceled := messenger.Prompt("$ ", "Shell")
994         if !canceled {
995                 // The true here is for openTerm to make the command interactive
996                 HandleShellCommand(input, true)
997         }
998         return false
999 }
1000
1001 // CommandMode lets the user enter a command
1002 func (v *View) CommandMode() bool {
1003         input, canceled := messenger.Prompt("> ", "Command")
1004         if !canceled {
1005                 HandleCommand(input)
1006         }
1007         return false
1008 }
1009
1010 // Quit quits the editor
1011 // This behavior needs to be changed and should really only quit the editor if this
1012 // is the last view
1013 // However, since micro only supports one view for now, it doesn't really matter
1014 func (v *View) Quit() bool {
1015         if v.helpOpen {
1016                 return v.ToggleHelp()
1017         }
1018         // Make sure not to quit if there are unsaved changes
1019         if views[mainView].CanClose("Quit anyway? (yes, no, save) ") {
1020                 views[mainView].CloseBuffer()
1021                 screen.Fini()
1022                 os.Exit(0)
1023         }
1024         return false
1025 }
1026
1027 // None is no action
1028 func None() bool {
1029         return false
1030 }