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