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