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