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