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