]> git.lizzy.rs Git - micro.git/blob - cmd/micro/actions.go
Merge pull request #499 from 10sr/addrmtrailingws
[micro.git] / cmd / micro / actions.go
1 package main
2
3 import (
4         "os"
5         "strconv"
6         "strings"
7         "time"
8
9         "github.com/yuin/gopher-lua"
10         "github.com/zyedidia/clipboard"
11 )
12
13 // PreActionCall executes the lua pre callback if possible
14 func PreActionCall(funcName string, view *View) bool {
15         executeAction := true
16         for pl := range loadedPlugins {
17                 ret, err := Call(pl+".pre"+funcName, view)
18                 if err != nil && !strings.HasPrefix(err.Error(), "function does not exist") {
19                         TermMessage(err)
20                         continue
21                 }
22                 if ret == lua.LFalse {
23                         executeAction = false
24                 }
25         }
26         return executeAction
27 }
28
29 // PostActionCall executes the lua plugin callback if possible
30 func PostActionCall(funcName string, view *View) bool {
31         relocate := true
32         for pl := range loadedPlugins {
33                 ret, err := Call(pl+".on"+funcName, view)
34                 if err != nil && !strings.HasPrefix(err.Error(), "function does not exist") {
35                         TermMessage(err)
36                         continue
37                 }
38                 if ret == lua.LFalse {
39                         relocate = false
40                 }
41         }
42         return relocate
43 }
44
45 func (v *View) deselect(index int) bool {
46         if v.Cursor.HasSelection() {
47                 v.Cursor.Loc = v.Cursor.CurSelection[index]
48                 v.Cursor.ResetSelection()
49                 return true
50         }
51         return false
52 }
53
54 // Center centers the view on the cursor
55 func (v *View) Center(usePlugin bool) bool {
56         if usePlugin && !PreActionCall("Center", v) {
57                 return false
58         }
59
60         v.Topline = v.Cursor.Y - v.Height/2
61         if v.Topline+v.Height > v.Buf.NumLines {
62                 v.Topline = v.Buf.NumLines - v.Height
63         }
64         if v.Topline < 0 {
65                 v.Topline = 0
66         }
67
68         if usePlugin {
69                 return PostActionCall("Center", v)
70         }
71         return true
72 }
73
74 // CursorUp moves the cursor up
75 func (v *View) CursorUp(usePlugin bool) bool {
76         if usePlugin && !PreActionCall("CursorUp", v) {
77                 return false
78         }
79
80         v.deselect(0)
81         v.Cursor.Up()
82
83         if usePlugin {
84                 return PostActionCall("CursorUp", v)
85         }
86         return true
87 }
88
89 // CursorDown moves the cursor down
90 func (v *View) CursorDown(usePlugin bool) bool {
91         if usePlugin && !PreActionCall("CursorDown", v) {
92                 return false
93         }
94
95         v.deselect(1)
96         v.Cursor.Down()
97
98         if usePlugin {
99                 return PostActionCall("CursorDown", v)
100         }
101         return true
102 }
103
104 // CursorLeft moves the cursor left
105 func (v *View) CursorLeft(usePlugin bool) bool {
106         if usePlugin && !PreActionCall("CursorLeft", v) {
107                 return false
108         }
109
110         if v.Cursor.HasSelection() {
111                 v.Cursor.Loc = v.Cursor.CurSelection[0]
112                 v.Cursor.ResetSelection()
113         } else {
114                 v.Cursor.Left()
115         }
116
117         if usePlugin {
118                 return PostActionCall("CursorLeft", v)
119         }
120         return true
121 }
122
123 // CursorRight moves the cursor right
124 func (v *View) CursorRight(usePlugin bool) bool {
125         if usePlugin && !PreActionCall("CursorRight", v) {
126                 return false
127         }
128
129         if v.Cursor.HasSelection() {
130                 v.Cursor.Loc = v.Cursor.CurSelection[1].Move(-1, v.Buf)
131                 v.Cursor.ResetSelection()
132         } else {
133                 v.Cursor.Right()
134         }
135
136         if usePlugin {
137                 return PostActionCall("CursorRight", v)
138         }
139         return true
140 }
141
142 // WordRight moves the cursor one word to the right
143 func (v *View) WordRight(usePlugin bool) bool {
144         if usePlugin && !PreActionCall("WordRight", v) {
145                 return false
146         }
147
148         v.Cursor.WordRight()
149
150         if usePlugin {
151                 return PostActionCall("WordRight", v)
152         }
153         return true
154 }
155
156 // WordLeft moves the cursor one word to the left
157 func (v *View) WordLeft(usePlugin bool) bool {
158         if usePlugin && !PreActionCall("WordLeft", v) {
159                 return false
160         }
161
162         v.Cursor.WordLeft()
163
164         if usePlugin {
165                 return PostActionCall("WordLeft", v)
166         }
167         return true
168 }
169
170 // SelectUp selects up one line
171 func (v *View) SelectUp(usePlugin bool) bool {
172         if usePlugin && !PreActionCall("SelectUp", v) {
173                 return false
174         }
175
176         if !v.Cursor.HasSelection() {
177                 v.Cursor.OrigSelection[0] = v.Cursor.Loc
178         }
179         v.Cursor.Up()
180         v.Cursor.SelectTo(v.Cursor.Loc)
181
182         if usePlugin {
183                 return PostActionCall("SelectUp", v)
184         }
185         return true
186 }
187
188 // SelectDown selects down one line
189 func (v *View) SelectDown(usePlugin bool) bool {
190         if usePlugin && !PreActionCall("SelectDown", v) {
191                 return false
192         }
193
194         if !v.Cursor.HasSelection() {
195                 v.Cursor.OrigSelection[0] = v.Cursor.Loc
196         }
197         v.Cursor.Down()
198         v.Cursor.SelectTo(v.Cursor.Loc)
199
200         if usePlugin {
201                 return PostActionCall("SelectDown", v)
202         }
203         return true
204 }
205
206 // SelectLeft selects the character to the left of the cursor
207 func (v *View) SelectLeft(usePlugin bool) bool {
208         if usePlugin && !PreActionCall("SelectLeft", v) {
209                 return false
210         }
211
212         loc := v.Cursor.Loc
213         count := v.Buf.End().Move(-1, v.Buf)
214         if loc.GreaterThan(count) {
215                 loc = count
216         }
217         if !v.Cursor.HasSelection() {
218                 v.Cursor.OrigSelection[0] = loc
219         }
220         v.Cursor.Left()
221         v.Cursor.SelectTo(v.Cursor.Loc)
222
223         if usePlugin {
224                 return PostActionCall("SelectLeft", v)
225         }
226         return true
227 }
228
229 // SelectRight selects the character to the right of the cursor
230 func (v *View) SelectRight(usePlugin bool) bool {
231         if usePlugin && !PreActionCall("SelectRight", v) {
232                 return false
233         }
234
235         loc := v.Cursor.Loc
236         count := v.Buf.End().Move(-1, v.Buf)
237         if loc.GreaterThan(count) {
238                 loc = count
239         }
240         if !v.Cursor.HasSelection() {
241                 v.Cursor.OrigSelection[0] = loc
242         }
243         v.Cursor.Right()
244         v.Cursor.SelectTo(v.Cursor.Loc)
245
246         if usePlugin {
247                 return PostActionCall("SelectRight", v)
248         }
249         return true
250 }
251
252 // SelectWordRight selects the word to the right of the cursor
253 func (v *View) SelectWordRight(usePlugin bool) bool {
254         if usePlugin && !PreActionCall("SelectWordRight", v) {
255                 return false
256         }
257
258         if !v.Cursor.HasSelection() {
259                 v.Cursor.OrigSelection[0] = v.Cursor.Loc
260         }
261         v.Cursor.WordRight()
262         v.Cursor.SelectTo(v.Cursor.Loc)
263
264         if usePlugin {
265                 return PostActionCall("SelectWordRight", v)
266         }
267         return true
268 }
269
270 // SelectWordLeft selects the word to the left of the cursor
271 func (v *View) SelectWordLeft(usePlugin bool) bool {
272         if usePlugin && !PreActionCall("SelectWordLeft", v) {
273                 return false
274         }
275
276         if !v.Cursor.HasSelection() {
277                 v.Cursor.OrigSelection[0] = v.Cursor.Loc
278         }
279         v.Cursor.WordLeft()
280         v.Cursor.SelectTo(v.Cursor.Loc)
281
282         if usePlugin {
283                 return PostActionCall("SelectWordLeft", v)
284         }
285         return true
286 }
287
288 // StartOfLine moves the cursor to the start of the line
289 func (v *View) StartOfLine(usePlugin bool) bool {
290         if usePlugin && !PreActionCall("StartOfLine", v) {
291                 return false
292         }
293
294         v.deselect(0)
295
296         v.Cursor.Start()
297
298         if usePlugin {
299                 return PostActionCall("StartOfLine", v)
300         }
301         return true
302 }
303
304 // EndOfLine moves the cursor to the end of the line
305 func (v *View) EndOfLine(usePlugin bool) bool {
306         if usePlugin && !PreActionCall("EndOfLine", v) {
307                 return false
308         }
309
310         v.deselect(0)
311
312         v.Cursor.End()
313
314         if usePlugin {
315                 return PostActionCall("EndOfLine", v)
316         }
317         return true
318 }
319
320 // SelectToStartOfLine selects to the start of the current line
321 func (v *View) SelectToStartOfLine(usePlugin bool) bool {
322         if usePlugin && !PreActionCall("SelectToStartOfLine", v) {
323                 return false
324         }
325
326         if !v.Cursor.HasSelection() {
327                 v.Cursor.OrigSelection[0] = v.Cursor.Loc
328         }
329         v.Cursor.Start()
330         v.Cursor.SelectTo(v.Cursor.Loc)
331
332         if usePlugin {
333                 return PostActionCall("SelectToStartOfLine", v)
334         }
335         return true
336 }
337
338 // SelectToEndOfLine selects to the end of the current line
339 func (v *View) SelectToEndOfLine(usePlugin bool) bool {
340         if usePlugin && !PreActionCall("SelectToEndOfLine", v) {
341                 return false
342         }
343
344         if !v.Cursor.HasSelection() {
345                 v.Cursor.OrigSelection[0] = v.Cursor.Loc
346         }
347         v.Cursor.End()
348         v.Cursor.SelectTo(v.Cursor.Loc)
349
350         if usePlugin {
351                 return PostActionCall("SelectToEndOfLine", v)
352         }
353         return true
354 }
355
356 // CursorStart moves the cursor to the start of the buffer
357 func (v *View) CursorStart(usePlugin bool) bool {
358         if usePlugin && !PreActionCall("CursorStart", v) {
359                 return false
360         }
361
362         v.deselect(0)
363
364         v.Cursor.X = 0
365         v.Cursor.Y = 0
366
367         if usePlugin {
368                 return PostActionCall("CursorStart", v)
369         }
370         return true
371 }
372
373 // CursorEnd moves the cursor to the end of the buffer
374 func (v *View) CursorEnd(usePlugin bool) bool {
375         if usePlugin && !PreActionCall("CursorEnd", v) {
376                 return false
377         }
378
379         v.deselect(0)
380
381         v.Cursor.Loc = v.Buf.End()
382
383         if usePlugin {
384                 return PostActionCall("CursorEnd", v)
385         }
386         return true
387 }
388
389 // SelectToStart selects the text from the cursor to the start of the buffer
390 func (v *View) SelectToStart(usePlugin bool) bool {
391         if usePlugin && !PreActionCall("SelectToStart", v) {
392                 return false
393         }
394
395         if !v.Cursor.HasSelection() {
396                 v.Cursor.OrigSelection[0] = v.Cursor.Loc
397         }
398         v.CursorStart(false)
399         v.Cursor.SelectTo(v.Buf.Start())
400
401         if usePlugin {
402                 return PostActionCall("SelectToStart", v)
403         }
404         return true
405 }
406
407 // SelectToEnd selects the text from the cursor to the end of the buffer
408 func (v *View) SelectToEnd(usePlugin bool) bool {
409         if usePlugin && !PreActionCall("SelectToEnd", v) {
410                 return false
411         }
412
413         if !v.Cursor.HasSelection() {
414                 v.Cursor.OrigSelection[0] = v.Cursor.Loc
415         }
416         v.CursorEnd(false)
417         v.Cursor.SelectTo(v.Buf.End())
418
419         if usePlugin {
420                 return PostActionCall("SelectToEnd", v)
421         }
422         return true
423 }
424
425 // InsertSpace inserts a space
426 func (v *View) InsertSpace(usePlugin bool) bool {
427         if usePlugin && !PreActionCall("InsertSpace", v) {
428                 return false
429         }
430
431         if v.Cursor.HasSelection() {
432                 v.Cursor.DeleteSelection()
433                 v.Cursor.ResetSelection()
434         }
435         v.Buf.Insert(v.Cursor.Loc, " ")
436         v.Cursor.Right()
437
438         if usePlugin {
439                 return PostActionCall("InsertSpace", v)
440         }
441         return true
442 }
443
444 // InsertNewline inserts a newline plus possible some whitespace if autoindent is on
445 func (v *View) InsertNewline(usePlugin bool) bool {
446         if usePlugin && !PreActionCall("InsertNewline", v) {
447                 return false
448         }
449
450         // Insert a newline
451         if v.Cursor.HasSelection() {
452                 v.Cursor.DeleteSelection()
453                 v.Cursor.ResetSelection()
454         }
455
456         v.Buf.Insert(v.Cursor.Loc, "\n")
457         ws := GetLeadingWhitespace(v.Buf.Line(v.Cursor.Y))
458         v.Cursor.Right()
459
460         if v.Buf.Settings["autoindent"].(bool) {
461                 v.Buf.Insert(v.Cursor.Loc, ws)
462                 for i := 0; i < len(ws); i++ {
463                         v.Cursor.Right()
464                 }
465
466                 if IsSpacesOrTabs(v.Buf.Line(v.Cursor.Y - 1)) {
467                         line := v.Buf.Line(v.Cursor.Y - 1)
468                         v.Buf.Remove(Loc{0, v.Cursor.Y - 1}, Loc{Count(line), v.Cursor.Y - 1})
469                 }
470         }
471         v.Cursor.LastVisualX = v.Cursor.GetVisualX()
472
473         if usePlugin {
474                 return PostActionCall("InsertNewline", v)
475         }
476         return true
477 }
478
479 // Backspace deletes the previous character
480 func (v *View) Backspace(usePlugin bool) bool {
481         if usePlugin && !PreActionCall("Backspace", v) {
482                 return false
483         }
484
485         // Delete a character
486         if v.Cursor.HasSelection() {
487                 v.Cursor.DeleteSelection()
488                 v.Cursor.ResetSelection()
489         } else if v.Cursor.Loc.GreaterThan(v.Buf.Start()) {
490                 // We have to do something a bit hacky here because we want to
491                 // delete the line by first moving left and then deleting backwards
492                 // but the undo redo would place the cursor in the wrong place
493                 // So instead we move left, save the position, move back, delete
494                 // and restore the position
495
496                 // If the user is using spaces instead of tabs and they are deleting
497                 // whitespace at the start of the line, we should delete as if it's a
498                 // tab (tabSize number of spaces)
499                 lineStart := v.Buf.Line(v.Cursor.Y)[:v.Cursor.X]
500                 tabSize := int(v.Buf.Settings["tabsize"].(float64))
501                 if v.Buf.Settings["tabstospaces"].(bool) && IsSpaces(lineStart) && len(lineStart) != 0 && len(lineStart)%tabSize == 0 {
502                         loc := v.Cursor.Loc
503                         v.Cursor.Loc = loc.Move(-tabSize, v.Buf)
504                         cx, cy := v.Cursor.X, v.Cursor.Y
505                         v.Cursor.Loc = loc
506                         v.Buf.Remove(loc.Move(-tabSize, v.Buf), loc)
507                         v.Cursor.X, v.Cursor.Y = cx, cy
508                 } else {
509                         v.Cursor.Left()
510                         cx, cy := v.Cursor.X, v.Cursor.Y
511                         v.Cursor.Right()
512                         loc := v.Cursor.Loc
513                         v.Buf.Remove(loc.Move(-1, v.Buf), loc)
514                         v.Cursor.X, v.Cursor.Y = cx, cy
515                 }
516         }
517         v.Cursor.LastVisualX = v.Cursor.GetVisualX()
518
519         if usePlugin {
520                 return PostActionCall("Backspace", v)
521         }
522         return true
523 }
524
525 // DeleteWordRight deletes the word to the right of the cursor
526 func (v *View) DeleteWordRight(usePlugin bool) bool {
527         if usePlugin && !PreActionCall("DeleteWordRight", v) {
528                 return false
529         }
530
531         v.SelectWordRight(false)
532         if v.Cursor.HasSelection() {
533                 v.Cursor.DeleteSelection()
534                 v.Cursor.ResetSelection()
535         }
536
537         if usePlugin {
538                 return PostActionCall("DeleteWordRight", v)
539         }
540         return true
541 }
542
543 // DeleteWordLeft deletes the word to the left of the cursor
544 func (v *View) DeleteWordLeft(usePlugin bool) bool {
545         if usePlugin && !PreActionCall("DeleteWordLeft", v) {
546                 return false
547         }
548
549         v.SelectWordLeft(false)
550         if v.Cursor.HasSelection() {
551                 v.Cursor.DeleteSelection()
552                 v.Cursor.ResetSelection()
553         }
554
555         if usePlugin {
556                 return PostActionCall("DeleteWordLeft", v)
557         }
558         return true
559 }
560
561 // Delete deletes the next character
562 func (v *View) Delete(usePlugin bool) bool {
563         if usePlugin && !PreActionCall("Delete", v) {
564                 return false
565         }
566
567         if v.Cursor.HasSelection() {
568                 v.Cursor.DeleteSelection()
569                 v.Cursor.ResetSelection()
570         } else {
571                 loc := v.Cursor.Loc
572                 if loc.LessThan(v.Buf.End()) {
573                         v.Buf.Remove(loc, loc.Move(1, v.Buf))
574                 }
575         }
576
577         if usePlugin {
578                 return PostActionCall("Delete", v)
579         }
580         return true
581 }
582
583 // IndentSelection indents the current selection
584 func (v *View) IndentSelection(usePlugin bool) bool {
585         if usePlugin && !PreActionCall("IndentSelection", v) {
586                 return false
587         }
588
589         if v.Cursor.HasSelection() {
590                 startY := v.Cursor.CurSelection[0].Y
591                 endY := v.Cursor.CurSelection[1].Move(-1, v.Buf).Y
592                 endX := v.Cursor.CurSelection[1].Move(-1, v.Buf).X
593                 for y := startY; y <= endY; y++ {
594                         tabsize := len(v.Buf.IndentString())
595                         v.Buf.Insert(Loc{0, y}, v.Buf.IndentString())
596                         if y == startY && v.Cursor.CurSelection[0].X > 0 {
597                                 v.Cursor.SetSelectionStart(v.Cursor.CurSelection[0].Move(tabsize, v.Buf))
598                         }
599                         if y == endY {
600                                 v.Cursor.SetSelectionEnd(Loc{endX + tabsize + 1, endY})
601                         }
602                 }
603                 v.Cursor.Relocate()
604
605                 if usePlugin {
606                         return PostActionCall("IndentSelection", v)
607                 }
608                 return true
609         }
610         return false
611 }
612
613 // OutdentLine moves the current line back one indentation
614 func (v *View) OutdentLine(usePlugin bool) bool {
615         if usePlugin && !PreActionCall("OutdentLine", v) {
616                 return false
617         }
618
619         if v.Cursor.HasSelection() {
620                 return false
621         }
622
623         for x := 0; x < len(v.Buf.IndentString()); x++ {
624                 if len(GetLeadingWhitespace(v.Buf.Line(v.Cursor.Y))) == 0 {
625                         break
626                 }
627                 v.Buf.Remove(Loc{0, v.Cursor.Y}, Loc{1, v.Cursor.Y})
628                 v.Cursor.X -= 1
629         }
630         v.Cursor.Relocate()
631
632         if usePlugin {
633                 return PostActionCall("OutdentLine", v)
634         }
635         return true
636 }
637
638 // OutdentSelection takes the current selection and moves it back one indent level
639 func (v *View) OutdentSelection(usePlugin bool) bool {
640         if usePlugin && !PreActionCall("OutdentSelection", v) {
641                 return false
642         }
643
644         if v.Cursor.HasSelection() {
645                 startY := v.Cursor.CurSelection[0].Y
646                 endY := v.Cursor.CurSelection[1].Move(-1, v.Buf).Y
647                 endX := v.Cursor.CurSelection[1].Move(-1, v.Buf).X
648                 for y := startY; y <= endY; y++ {
649                         for x := 0; x < len(v.Buf.IndentString()); x++ {
650                                 if len(GetLeadingWhitespace(v.Buf.Line(y))) == 0 {
651                                         break
652                                 }
653                                 v.Buf.Remove(Loc{0, y}, Loc{1, y})
654                                 if y == startY && v.Cursor.CurSelection[0].X > 0 {
655                                         v.Cursor.SetSelectionStart(v.Cursor.CurSelection[0].Move(-1, v.Buf))
656                                 }
657                                 if y == endY {
658                                         v.Cursor.SetSelectionEnd(Loc{endX - x, endY})
659                                 }
660                         }
661                 }
662                 v.Cursor.Relocate()
663
664                 if usePlugin {
665                         return PostActionCall("OutdentSelection", v)
666                 }
667                 return true
668         }
669         return false
670 }
671
672 // InsertTab inserts a tab or spaces
673 func (v *View) InsertTab(usePlugin bool) bool {
674         if usePlugin && !PreActionCall("InsertTab", v) {
675                 return false
676         }
677
678         if v.Cursor.HasSelection() {
679                 return false
680         }
681
682         tabBytes := len(v.Buf.IndentString())
683         bytesUntilIndent := tabBytes - (v.Cursor.GetVisualX() % tabBytes)
684         v.Buf.Insert(v.Cursor.Loc, v.Buf.IndentString()[:bytesUntilIndent])
685         for i := 0; i < bytesUntilIndent; i++ {
686                 v.Cursor.Right()
687         }
688
689         if usePlugin {
690                 return PostActionCall("InsertTab", v)
691         }
692         return true
693 }
694
695 // Save the buffer to disk
696 func (v *View) Save(usePlugin bool) bool {
697         if usePlugin && !PreActionCall("Save", v) {
698                 return false
699         }
700
701         if v.Type == vtHelp {
702                 // We can't save the help text
703                 return false
704         }
705         // If this is an empty buffer, ask for a filename
706         if v.Buf.Path == "" {
707                 v.SaveAs(false)
708         }
709         err := v.Buf.Save()
710         if err != nil {
711                 if strings.HasSuffix(err.Error(), "permission denied") {
712                         choice, _ := messenger.YesNoPrompt("Permission denied. Do you want to save this file using sudo? (y,n)")
713                         if choice {
714                                 err = v.Buf.SaveWithSudo()
715                                 if err != nil {
716                                         messenger.Error(err.Error())
717                                         return false
718                                 }
719                                 messenger.Message("Saved " + v.Buf.Path)
720                         }
721                         messenger.Reset()
722                         messenger.Clear()
723                 } else {
724                         messenger.Error(err.Error())
725                 }
726         } else {
727                 messenger.Message("Saved " + v.Buf.Path)
728         }
729
730         if usePlugin {
731                 return PostActionCall("Save", v)
732         }
733         return false
734 }
735
736 // SaveAs saves the buffer to disk with the given name
737 func (v *View) SaveAs(usePlugin bool) bool {
738         filename, canceled := messenger.Prompt("Filename: ", "", "Save", NoCompletion)
739         if !canceled {
740                 // the filename might or might not be quoted, so unquote first then join the strings.
741                 filename = strings.Join(SplitCommandArgs(filename), " ")
742                 v.Buf.Path = filename
743                 v.Buf.name = filename
744
745                 v.Save(true)
746         }
747
748         return false
749 }
750
751 // Find opens a prompt and searches forward for the input
752 func (v *View) Find(usePlugin bool) bool {
753         if usePlugin && !PreActionCall("Find", v) {
754                 return false
755         }
756
757         searchStr := ""
758         if v.Cursor.HasSelection() {
759                 searchStart = ToCharPos(v.Cursor.CurSelection[1], v.Buf)
760                 searchStart = ToCharPos(v.Cursor.CurSelection[1], v.Buf)
761                 searchStr = v.Cursor.GetSelection()
762         } else {
763                 searchStart = ToCharPos(v.Cursor.Loc, v.Buf)
764         }
765         BeginSearch(searchStr)
766
767         if usePlugin {
768                 return PostActionCall("Find", v)
769         }
770         return true
771 }
772
773 // FindNext searches forwards for the last used search term
774 func (v *View) FindNext(usePlugin bool) bool {
775         if usePlugin && !PreActionCall("FindNext", v) {
776                 return false
777         }
778
779         if v.Cursor.HasSelection() {
780                 searchStart = ToCharPos(v.Cursor.CurSelection[1], v.Buf)
781                 lastSearch = v.Cursor.GetSelection()
782         } else {
783                 searchStart = ToCharPos(v.Cursor.Loc, v.Buf)
784         }
785         if lastSearch == "" {
786                 return true
787         }
788         messenger.Message("Finding: " + lastSearch)
789         Search(lastSearch, v, true)
790
791         if usePlugin {
792                 return PostActionCall("FindNext", v)
793         }
794         return true
795 }
796
797 // FindPrevious searches backwards for the last used search term
798 func (v *View) FindPrevious(usePlugin bool) bool {
799         if usePlugin && !PreActionCall("FindPrevious", v) {
800                 return false
801         }
802
803         if v.Cursor.HasSelection() {
804                 searchStart = ToCharPos(v.Cursor.CurSelection[0], v.Buf)
805         } else {
806                 searchStart = ToCharPos(v.Cursor.Loc, v.Buf)
807         }
808         messenger.Message("Finding: " + lastSearch)
809         Search(lastSearch, v, false)
810
811         if usePlugin {
812                 return PostActionCall("FindPrevious", v)
813         }
814         return true
815 }
816
817 // Undo undoes the last action
818 func (v *View) Undo(usePlugin bool) bool {
819         if usePlugin && !PreActionCall("Undo", v) {
820                 return false
821         }
822
823         v.Buf.Undo()
824         messenger.Message("Undid action")
825
826         if usePlugin {
827                 return PostActionCall("Undo", v)
828         }
829         return true
830 }
831
832 // Redo redoes the last action
833 func (v *View) Redo(usePlugin bool) bool {
834         if usePlugin && !PreActionCall("Redo", v) {
835                 return false
836         }
837
838         v.Buf.Redo()
839         messenger.Message("Redid action")
840
841         if usePlugin {
842                 return PostActionCall("Redo", v)
843         }
844         return true
845 }
846
847 // Copy the selection to the system clipboard
848 func (v *View) Copy(usePlugin bool) bool {
849         if usePlugin && !PreActionCall("Copy", v) {
850                 return false
851         }
852
853         if v.Cursor.HasSelection() {
854                 clipboard.WriteAll(v.Cursor.GetSelection(), "clipboard")
855                 v.freshClip = true
856                 messenger.Message("Copied selection")
857         }
858
859         if usePlugin {
860                 return PostActionCall("Copy", v)
861         }
862         return true
863 }
864
865 // CutLine cuts the current line to the clipboard
866 func (v *View) CutLine(usePlugin bool) bool {
867         if usePlugin && !PreActionCall("CutLine", v) {
868                 return false
869         }
870
871         v.Cursor.SelectLine()
872         if !v.Cursor.HasSelection() {
873                 return false
874         }
875         if v.freshClip == true {
876                 if v.Cursor.HasSelection() {
877                         if clip, err := clipboard.ReadAll("clipboard"); err != nil {
878                                 messenger.Error(err)
879                         } else {
880                                 clipboard.WriteAll(clip+v.Cursor.GetSelection(), "clipboard")
881                         }
882                 }
883         } else if time.Since(v.lastCutTime)/time.Second > 10*time.Second || v.freshClip == false {
884                 v.Copy(true)
885         }
886         v.freshClip = true
887         v.lastCutTime = time.Now()
888         v.Cursor.DeleteSelection()
889         v.Cursor.ResetSelection()
890         messenger.Message("Cut line")
891
892         if usePlugin {
893                 return PostActionCall("CutLine", v)
894         }
895         return true
896 }
897
898 // Cut the selection to the system clipboard
899 func (v *View) Cut(usePlugin bool) bool {
900         if usePlugin && !PreActionCall("Cut", v) {
901                 return false
902         }
903
904         if v.Cursor.HasSelection() {
905                 clipboard.WriteAll(v.Cursor.GetSelection(), "clipboard")
906                 v.Cursor.DeleteSelection()
907                 v.Cursor.ResetSelection()
908                 v.freshClip = true
909                 messenger.Message("Cut selection")
910
911                 if usePlugin {
912                         return PostActionCall("Cut", v)
913                 }
914                 return true
915         }
916
917         return false
918 }
919
920 // DuplicateLine duplicates the current line or selection
921 func (v *View) DuplicateLine(usePlugin bool) bool {
922         if usePlugin && !PreActionCall("DuplicateLine", v) {
923                 return false
924         }
925
926         if v.Cursor.HasSelection() {
927                 v.Buf.Insert(v.Cursor.CurSelection[1], v.Cursor.GetSelection())
928         } else {
929                 v.Cursor.End()
930                 v.Buf.Insert(v.Cursor.Loc, "\n"+v.Buf.Line(v.Cursor.Y))
931                 v.Cursor.Right()
932         }
933
934         messenger.Message("Duplicated line")
935
936         if usePlugin {
937                 return PostActionCall("DuplicateLine", v)
938         }
939         return true
940 }
941
942 // DeleteLine deletes the current line
943 func (v *View) DeleteLine(usePlugin bool) bool {
944         if usePlugin && !PreActionCall("DeleteLine", v) {
945                 return false
946         }
947
948         v.Cursor.SelectLine()
949         if !v.Cursor.HasSelection() {
950                 return false
951         }
952         v.Cursor.DeleteSelection()
953         v.Cursor.ResetSelection()
954         messenger.Message("Deleted line")
955
956         if usePlugin {
957                 return PostActionCall("DeleteLine", v)
958         }
959         return true
960 }
961
962 // MoveLinesUp moves up the current line or selected lines if any
963 func (v *View) MoveLinesUp(usePlugin bool) bool {
964         if usePlugin && !PreActionCall("MoveLinesUp", v) {
965                 return false
966         }
967
968         if v.Cursor.HasSelection() {
969                 if v.Cursor.CurSelection[0].Y == 0 {
970                         messenger.Message("Can not move further up")
971                         return true
972                 }
973                 v.Buf.MoveLinesUp(
974                         v.Cursor.CurSelection[0].Y,
975                         v.Cursor.CurSelection[1].Y,
976                 )
977                 v.Cursor.UpN(1)
978                 v.Cursor.CurSelection[0].Y -= 1
979                 v.Cursor.CurSelection[1].Y -= 1
980                 messenger.Message("Moved up selected line(s)")
981         } else {
982                 if v.Cursor.Loc.Y == 0 {
983                         messenger.Message("Can not move further up")
984                         return true
985                 }
986                 v.Buf.MoveLinesUp(
987                         v.Cursor.Loc.Y,
988                         v.Cursor.Loc.Y+1,
989                 )
990                 v.Cursor.UpN(1)
991                 messenger.Message("Moved up current line")
992         }
993         v.Buf.IsModified = true
994
995         if usePlugin {
996                 return PostActionCall("MoveLinesUp", v)
997         }
998         return true
999 }
1000
1001 // MoveLinesDown moves down the current line or selected lines if any
1002 func (v *View) MoveLinesDown(usePlugin bool) bool {
1003         if usePlugin && !PreActionCall("MoveLinesDown", v) {
1004                 return false
1005         }
1006
1007         if v.Cursor.HasSelection() {
1008                 if v.Cursor.CurSelection[1].Y >= len(v.Buf.lines) {
1009                         messenger.Message("Can not move further down")
1010                         return true
1011                 }
1012                 v.Buf.MoveLinesDown(
1013                         v.Cursor.CurSelection[0].Y,
1014                         v.Cursor.CurSelection[1].Y,
1015                 )
1016                 v.Cursor.DownN(1)
1017                 v.Cursor.CurSelection[0].Y += 1
1018                 v.Cursor.CurSelection[1].Y += 1
1019                 messenger.Message("Moved down selected line(s)")
1020         } else {
1021                 if v.Cursor.Loc.Y >= len(v.Buf.lines)-1 {
1022                         messenger.Message("Can not move further down")
1023                         return true
1024                 }
1025                 v.Buf.MoveLinesDown(
1026                         v.Cursor.Loc.Y,
1027                         v.Cursor.Loc.Y+1,
1028                 )
1029                 v.Cursor.DownN(1)
1030                 messenger.Message("Moved down current line")
1031         }
1032         v.Buf.IsModified = true
1033
1034         if usePlugin {
1035                 return PostActionCall("MoveLinesDown", v)
1036         }
1037         return true
1038 }
1039
1040 // Paste whatever is in the system clipboard into the buffer
1041 // Delete and paste if the user has a selection
1042 func (v *View) Paste(usePlugin bool) bool {
1043         if usePlugin && !PreActionCall("Paste", v) {
1044                 return false
1045         }
1046
1047         clip, _ := clipboard.ReadAll("clipboard")
1048         v.paste(clip)
1049
1050         if usePlugin {
1051                 return PostActionCall("Paste", v)
1052         }
1053         return true
1054 }
1055
1056 // PastePrimary pastes from the primary clipboard (only use on linux)
1057 func (v *View) PastePrimary(usePlugin bool) bool {
1058         if usePlugin && !PreActionCall("Paste", v) {
1059                 return false
1060         }
1061
1062         clip, _ := clipboard.ReadAll("primary")
1063         v.paste(clip)
1064
1065         if usePlugin {
1066                 return PostActionCall("Paste", v)
1067         }
1068         return true
1069 }
1070
1071 // SelectAll selects the entire buffer
1072 func (v *View) SelectAll(usePlugin bool) bool {
1073         if usePlugin && !PreActionCall("SelectAll", v) {
1074                 return false
1075         }
1076
1077         v.Cursor.SetSelectionStart(v.Buf.Start())
1078         v.Cursor.SetSelectionEnd(v.Buf.End())
1079         // Put the cursor at the beginning
1080         v.Cursor.X = 0
1081         v.Cursor.Y = 0
1082
1083         if usePlugin {
1084                 return PostActionCall("SelectAll", v)
1085         }
1086         return true
1087 }
1088
1089 // OpenFile opens a new file in the buffer
1090 func (v *View) OpenFile(usePlugin bool) bool {
1091         if usePlugin && !PreActionCall("OpenFile", v) {
1092                 return false
1093         }
1094
1095         if v.CanClose() {
1096                 input, canceled := messenger.Prompt("> ", "open ", "Open", CommandCompletion)
1097                 if !canceled {
1098                         HandleCommand(input)
1099                         if usePlugin {
1100                                 return PostActionCall("OpenFile", v)
1101                         }
1102                 }
1103         }
1104         return false
1105 }
1106
1107 // Start moves the viewport to the start of the buffer
1108 func (v *View) Start(usePlugin bool) bool {
1109         if usePlugin && !PreActionCall("Start", v) {
1110                 return false
1111         }
1112
1113         v.Topline = 0
1114
1115         if usePlugin {
1116                 return PostActionCall("Start", v)
1117         }
1118         return false
1119 }
1120
1121 // End moves the viewport to the end of the buffer
1122 func (v *View) End(usePlugin bool) bool {
1123         if usePlugin && !PreActionCall("End", v) {
1124                 return false
1125         }
1126
1127         if v.Height > v.Buf.NumLines {
1128                 v.Topline = 0
1129         } else {
1130                 v.Topline = v.Buf.NumLines - v.Height
1131         }
1132
1133         if usePlugin {
1134                 return PostActionCall("End", v)
1135         }
1136         return false
1137 }
1138
1139 // PageUp scrolls the view up a page
1140 func (v *View) PageUp(usePlugin bool) bool {
1141         if usePlugin && !PreActionCall("PageUp", v) {
1142                 return false
1143         }
1144
1145         if v.Topline > v.Height {
1146                 v.ScrollUp(v.Height)
1147         } else {
1148                 v.Topline = 0
1149         }
1150
1151         if usePlugin {
1152                 return PostActionCall("PageUp", v)
1153         }
1154         return false
1155 }
1156
1157 // PageDown scrolls the view down a page
1158 func (v *View) PageDown(usePlugin bool) bool {
1159         if usePlugin && !PreActionCall("PageDown", v) {
1160                 return false
1161         }
1162
1163         if v.Buf.NumLines-(v.Topline+v.Height) > v.Height {
1164                 v.ScrollDown(v.Height)
1165         } else if v.Buf.NumLines >= v.Height {
1166                 v.Topline = v.Buf.NumLines - v.Height
1167         }
1168
1169         if usePlugin {
1170                 return PostActionCall("PageDown", v)
1171         }
1172         return false
1173 }
1174
1175 // CursorPageUp places the cursor a page up
1176 func (v *View) CursorPageUp(usePlugin bool) bool {
1177         if usePlugin && !PreActionCall("CursorPageUp", v) {
1178                 return false
1179         }
1180
1181         v.deselect(0)
1182
1183         if v.Cursor.HasSelection() {
1184                 v.Cursor.Loc = v.Cursor.CurSelection[0]
1185                 v.Cursor.ResetSelection()
1186         }
1187         v.Cursor.UpN(v.Height)
1188
1189         if usePlugin {
1190                 return PostActionCall("CursorPageUp", v)
1191         }
1192         return true
1193 }
1194
1195 // CursorPageDown places the cursor a page up
1196 func (v *View) CursorPageDown(usePlugin bool) bool {
1197         if usePlugin && !PreActionCall("CursorPageDown", v) {
1198                 return false
1199         }
1200
1201         v.deselect(0)
1202
1203         if v.Cursor.HasSelection() {
1204                 v.Cursor.Loc = v.Cursor.CurSelection[1]
1205                 v.Cursor.ResetSelection()
1206         }
1207         v.Cursor.DownN(v.Height)
1208
1209         if usePlugin {
1210                 return PostActionCall("CursorPageDown", v)
1211         }
1212         return true
1213 }
1214
1215 // HalfPageUp scrolls the view up half a page
1216 func (v *View) HalfPageUp(usePlugin bool) bool {
1217         if usePlugin && !PreActionCall("HalfPageUp", v) {
1218                 return false
1219         }
1220
1221         if v.Topline > v.Height/2 {
1222                 v.ScrollUp(v.Height / 2)
1223         } else {
1224                 v.Topline = 0
1225         }
1226
1227         if usePlugin {
1228                 return PostActionCall("HalfPageUp", v)
1229         }
1230         return false
1231 }
1232
1233 // HalfPageDown scrolls the view down half a page
1234 func (v *View) HalfPageDown(usePlugin bool) bool {
1235         if usePlugin && !PreActionCall("HalfPageDown", v) {
1236                 return false
1237         }
1238
1239         if v.Buf.NumLines-(v.Topline+v.Height) > v.Height/2 {
1240                 v.ScrollDown(v.Height / 2)
1241         } else {
1242                 if v.Buf.NumLines >= v.Height {
1243                         v.Topline = v.Buf.NumLines - v.Height
1244                 }
1245         }
1246
1247         if usePlugin {
1248                 return PostActionCall("HalfPageDown", v)
1249         }
1250         return false
1251 }
1252
1253 // ToggleRuler turns line numbers off and on
1254 func (v *View) ToggleRuler(usePlugin bool) bool {
1255         if usePlugin && !PreActionCall("ToggleRuler", v) {
1256                 return false
1257         }
1258
1259         if v.Buf.Settings["ruler"] == false {
1260                 v.Buf.Settings["ruler"] = true
1261                 messenger.Message("Enabled ruler")
1262         } else {
1263                 v.Buf.Settings["ruler"] = false
1264                 messenger.Message("Disabled ruler")
1265         }
1266
1267         if usePlugin {
1268                 return PostActionCall("ToggleRuler", v)
1269         }
1270         return false
1271 }
1272
1273 // JumpLine jumps to a line and moves the view accordingly.
1274 func (v *View) JumpLine(usePlugin bool) bool {
1275         if usePlugin && !PreActionCall("JumpLine", v) {
1276                 return false
1277         }
1278
1279         // Prompt for line number
1280         linestring, canceled := messenger.Prompt("Jump to line # ", "", "LineNumber", NoCompletion)
1281         if canceled {
1282                 return false
1283         }
1284         lineint, err := strconv.Atoi(linestring)
1285         lineint = lineint - 1 // fix offset
1286         if err != nil {
1287                 messenger.Error(err) // return errors
1288                 return false
1289         }
1290         // Move cursor and view if possible.
1291         if lineint < v.Buf.NumLines && lineint >= 0 {
1292                 v.Cursor.X = 0
1293                 v.Cursor.Y = lineint
1294
1295                 if usePlugin {
1296                         return PostActionCall("JumpLine", v)
1297                 }
1298                 return true
1299         }
1300         messenger.Error("Only ", v.Buf.NumLines, " lines to jump")
1301         return false
1302 }
1303
1304 // ClearStatus clears the messenger bar
1305 func (v *View) ClearStatus(usePlugin bool) bool {
1306         if usePlugin && !PreActionCall("ClearStatus", v) {
1307                 return false
1308         }
1309
1310         messenger.Message("")
1311
1312         if usePlugin {
1313                 return PostActionCall("ClearStatus", v)
1314         }
1315         return false
1316 }
1317
1318 // ToggleHelp toggles the help screen
1319 func (v *View) ToggleHelp(usePlugin bool) bool {
1320         if usePlugin && !PreActionCall("ToggleHelp", v) {
1321                 return false
1322         }
1323
1324         if v.Type != vtHelp {
1325                 // Open the default help
1326                 v.openHelp("help")
1327         } else {
1328                 v.Quit(true)
1329         }
1330
1331         if usePlugin {
1332                 return PostActionCall("ToggleHelp", v)
1333         }
1334         return true
1335 }
1336
1337 // ShellMode opens a terminal to run a shell command
1338 func (v *View) ShellMode(usePlugin bool) bool {
1339         if usePlugin && !PreActionCall("ShellMode", v) {
1340                 return false
1341         }
1342
1343         input, canceled := messenger.Prompt("$ ", "", "Shell", NoCompletion)
1344         if !canceled {
1345                 // The true here is for openTerm to make the command interactive
1346                 HandleShellCommand(input, true, true)
1347                 if usePlugin {
1348                         return PostActionCall("ShellMode", v)
1349                 }
1350         }
1351         return false
1352 }
1353
1354 // CommandMode lets the user enter a command
1355 func (v *View) CommandMode(usePlugin bool) bool {
1356         if usePlugin && !PreActionCall("CommandMode", v) {
1357                 return false
1358         }
1359
1360         input, canceled := messenger.Prompt("> ", "", "Command", CommandCompletion)
1361         if !canceled {
1362                 HandleCommand(input)
1363                 if usePlugin {
1364                         return PostActionCall("CommandMode", v)
1365                 }
1366         }
1367
1368         return false
1369 }
1370
1371 // Escape leaves current mode / quits the editor
1372 func (v *View) Escape(usePlugin bool) bool {
1373         // check if user is searching, or the last search is still active
1374         if searching || lastSearch != "" {
1375                 ExitSearch(v)
1376                 return true
1377         }
1378         // check if a prompt is shown, hide it and don't quit
1379         if messenger.hasPrompt {
1380                 messenger.Reset() // FIXME
1381                 return true
1382         }
1383         return v.Quit(usePlugin)
1384 }
1385
1386 // Quit quits the editor
1387 // This behavior needs to be changed and should really only quit the editor if this
1388 // is the last view
1389 // However, since micro only supports one view for now, it doesn't really matter
1390 func (v *View) Quit(usePlugin bool) bool {
1391         if usePlugin && !PreActionCall("Quit", v) {
1392                 return false
1393         }
1394
1395         // Make sure not to quit if there are unsaved changes
1396         if v.CanClose() {
1397                 v.CloseBuffer()
1398                 if len(tabs[curTab].views) > 1 {
1399                         v.splitNode.Delete()
1400                         tabs[v.TabNum].Cleanup()
1401                         tabs[v.TabNum].Resize()
1402                 } else if len(tabs) > 1 {
1403                         if len(tabs[v.TabNum].views) == 1 {
1404                                 tabs = tabs[:v.TabNum+copy(tabs[v.TabNum:], tabs[v.TabNum+1:])]
1405                                 for i, t := range tabs {
1406                                         t.SetNum(i)
1407                                 }
1408                                 if curTab >= len(tabs) {
1409                                         curTab--
1410                                 }
1411                                 if curTab == 0 {
1412                                         // CurView().Resize(screen.Size())
1413                                         CurView().ToggleTabbar()
1414                                         CurView().matches = Match(CurView())
1415                                 }
1416                         }
1417                 } else {
1418                         if usePlugin {
1419                                 PostActionCall("Quit", v)
1420                         }
1421
1422                         screen.Fini()
1423                         os.Exit(0)
1424                 }
1425         }
1426
1427         if usePlugin {
1428                 return PostActionCall("Quit", v)
1429         }
1430         return false
1431 }
1432
1433 // QuitAll quits the whole editor; all splits and tabs
1434 func (v *View) QuitAll(usePlugin bool) bool {
1435         if usePlugin && !PreActionCall("QuitAll", v) {
1436                 return false
1437         }
1438
1439         closeAll := true
1440         for _, tab := range tabs {
1441                 for _, v := range tab.views {
1442                         if !v.CanClose() {
1443                                 closeAll = false
1444                         }
1445                 }
1446         }
1447
1448         if closeAll {
1449                 for _, tab := range tabs {
1450                         for _, v := range tab.views {
1451                                 v.CloseBuffer()
1452                         }
1453                 }
1454
1455                 if usePlugin {
1456                         PostActionCall("QuitAll", v)
1457                 }
1458
1459                 screen.Fini()
1460                 os.Exit(0)
1461         }
1462
1463         return false
1464 }
1465
1466 // AddTab adds a new tab with an empty buffer
1467 func (v *View) AddTab(usePlugin bool) bool {
1468         if usePlugin && !PreActionCall("AddTab", v) {
1469                 return false
1470         }
1471
1472         tab := NewTabFromView(NewView(NewBuffer(strings.NewReader(""), "")))
1473         tab.SetNum(len(tabs))
1474         tabs = append(tabs, tab)
1475         curTab = len(tabs) - 1
1476         if len(tabs) == 2 {
1477                 for _, t := range tabs {
1478                         for _, v := range t.views {
1479                                 v.ToggleTabbar()
1480                         }
1481                 }
1482         }
1483
1484         if usePlugin {
1485                 return PostActionCall("AddTab", v)
1486         }
1487         return true
1488 }
1489
1490 // PreviousTab switches to the previous tab in the tab list
1491 func (v *View) PreviousTab(usePlugin bool) bool {
1492         if usePlugin && !PreActionCall("PreviousTab", v) {
1493                 return false
1494         }
1495
1496         if curTab > 0 {
1497                 curTab--
1498         } else if curTab == 0 {
1499                 curTab = len(tabs) - 1
1500         }
1501
1502         if usePlugin {
1503                 return PostActionCall("PreviousTab", v)
1504         }
1505         return false
1506 }
1507
1508 // NextTab switches to the next tab in the tab list
1509 func (v *View) NextTab(usePlugin bool) bool {
1510         if usePlugin && !PreActionCall("NextTab", v) {
1511                 return false
1512         }
1513
1514         if curTab < len(tabs)-1 {
1515                 curTab++
1516         } else if curTab == len(tabs)-1 {
1517                 curTab = 0
1518         }
1519
1520         if usePlugin {
1521                 return PostActionCall("NextTab", v)
1522         }
1523         return false
1524 }
1525
1526 // VSplitBinding opens an empty vertical split
1527 func (v *View) VSplitBinding(usePlugin bool) bool {
1528         if usePlugin && !PreActionCall("VSplit", v) {
1529                 return false
1530         }
1531
1532         v.VSplit(NewBuffer(strings.NewReader(""), ""))
1533
1534         if usePlugin {
1535                 return PostActionCall("VSplit", v)
1536         }
1537         return false
1538 }
1539
1540 // HSplitBinding opens an empty horizontal split
1541 func (v *View) HSplitBinding(usePlugin bool) bool {
1542         if usePlugin && !PreActionCall("HSplit", v) {
1543                 return false
1544         }
1545
1546         v.HSplit(NewBuffer(strings.NewReader(""), ""))
1547
1548         if usePlugin {
1549                 return PostActionCall("HSplit", v)
1550         }
1551         return false
1552 }
1553
1554 // Unsplit closes all splits in the current tab except the active one
1555 func (v *View) Unsplit(usePlugin bool) bool {
1556         if usePlugin && !PreActionCall("Unsplit", v) {
1557                 return false
1558         }
1559
1560         curView := tabs[curTab].CurView
1561         for i := len(tabs[curTab].views) - 1; i >= 0; i-- {
1562                 view := tabs[curTab].views[i]
1563                 if view != nil && view.Num != curView {
1564                         view.Quit(true)
1565                         // messenger.Message("Quit ", view.Buf.Path)
1566                 }
1567         }
1568
1569         if usePlugin {
1570                 return PostActionCall("Unsplit", v)
1571         }
1572         return false
1573 }
1574
1575 // NextSplit changes the view to the next split
1576 func (v *View) NextSplit(usePlugin bool) bool {
1577         if usePlugin && !PreActionCall("NextSplit", v) {
1578                 return false
1579         }
1580
1581         tab := tabs[curTab]
1582         if tab.CurView < len(tab.views)-1 {
1583                 tab.CurView++
1584         } else {
1585                 tab.CurView = 0
1586         }
1587
1588         if usePlugin {
1589                 return PostActionCall("NextSplit", v)
1590         }
1591         return false
1592 }
1593
1594 // PreviousSplit changes the view to the previous split
1595 func (v *View) PreviousSplit(usePlugin bool) bool {
1596         if usePlugin && !PreActionCall("PreviousSplit", v) {
1597                 return false
1598         }
1599
1600         tab := tabs[curTab]
1601         if tab.CurView > 0 {
1602                 tab.CurView--
1603         } else {
1604                 tab.CurView = len(tab.views) - 1
1605         }
1606
1607         if usePlugin {
1608                 return PostActionCall("PreviousSplit", v)
1609         }
1610         return false
1611 }
1612
1613 var curMacro []interface{}
1614 var recordingMacro bool
1615
1616 // ToggleMacro toggles recording of a macro
1617 func (v *View) ToggleMacro(usePlugin bool) bool {
1618         if usePlugin && !PreActionCall("ToggleMacro", v) {
1619                 return false
1620         }
1621
1622         recordingMacro = !recordingMacro
1623
1624         if recordingMacro {
1625                 curMacro = []interface{}{}
1626                 messenger.Message("Recording")
1627         } else {
1628                 messenger.Message("Stopped recording")
1629         }
1630
1631         if usePlugin {
1632                 return PostActionCall("ToggleMacro", v)
1633         }
1634         return true
1635 }
1636
1637 // PlayMacro plays back the most recently recorded macro
1638 func (v *View) PlayMacro(usePlugin bool) bool {
1639         if usePlugin && !PreActionCall("PlayMacro", v) {
1640                 return false
1641         }
1642
1643         for _, action := range curMacro {
1644                 switch t := action.(type) {
1645                 case rune:
1646                         // Insert a character
1647                         if v.Cursor.HasSelection() {
1648                                 v.Cursor.DeleteSelection()
1649                                 v.Cursor.ResetSelection()
1650                         }
1651                         v.Buf.Insert(v.Cursor.Loc, string(t))
1652                         v.Cursor.Right()
1653
1654                         for pl := range loadedPlugins {
1655                                 _, err := Call(pl+".onRune", string(t), v)
1656                                 if err != nil && !strings.HasPrefix(err.Error(), "function does not exist") {
1657                                         TermMessage(err)
1658                                 }
1659                         }
1660                 case func(*View, bool) bool:
1661                         t(v, true)
1662                 }
1663         }
1664
1665         if usePlugin {
1666                 return PostActionCall("PlayMacro", v)
1667         }
1668         return true
1669 }
1670
1671 // None is no action
1672 func None() bool {
1673         return false
1674 }