]> git.lizzy.rs Git - micro.git/blob - cmd/micro/actions.go
Add support for skipping and remove need for lookbehind
[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                 // Remove the whitespaces if keepautoindent setting is off
467                 if IsSpacesOrTabs(v.Buf.Line(v.Cursor.Y-1)) && !v.Buf.Settings["keepautoindent"].(bool) {
468                         line := v.Buf.Line(v.Cursor.Y - 1)
469                         v.Buf.Remove(Loc{0, v.Cursor.Y - 1}, Loc{Count(line), v.Cursor.Y - 1})
470                 }
471         }
472         v.Cursor.LastVisualX = v.Cursor.GetVisualX()
473
474         if usePlugin {
475                 return PostActionCall("InsertNewline", v)
476         }
477         return true
478 }
479
480 // Backspace deletes the previous character
481 func (v *View) Backspace(usePlugin bool) bool {
482         if usePlugin && !PreActionCall("Backspace", v) {
483                 return false
484         }
485
486         // Delete a character
487         if v.Cursor.HasSelection() {
488                 v.Cursor.DeleteSelection()
489                 v.Cursor.ResetSelection()
490         } else if v.Cursor.Loc.GreaterThan(v.Buf.Start()) {
491                 // We have to do something a bit hacky here because we want to
492                 // delete the line by first moving left and then deleting backwards
493                 // but the undo redo would place the cursor in the wrong place
494                 // So instead we move left, save the position, move back, delete
495                 // and restore the position
496
497                 // If the user is using spaces instead of tabs and they are deleting
498                 // whitespace at the start of the line, we should delete as if it's a
499                 // tab (tabSize number of spaces)
500                 lineStart := v.Buf.Line(v.Cursor.Y)[:v.Cursor.X]
501                 tabSize := int(v.Buf.Settings["tabsize"].(float64))
502                 if v.Buf.Settings["tabstospaces"].(bool) && IsSpaces(lineStart) && len(lineStart) != 0 && len(lineStart)%tabSize == 0 {
503                         loc := v.Cursor.Loc
504                         v.Cursor.Loc = loc.Move(-tabSize, v.Buf)
505                         cx, cy := v.Cursor.X, v.Cursor.Y
506                         v.Cursor.Loc = loc
507                         v.Buf.Remove(loc.Move(-tabSize, v.Buf), 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.Buf.Remove(loc.Move(-1, v.Buf), loc)
515                         v.Cursor.X, v.Cursor.Y = cx, cy
516                 }
517         }
518         v.Cursor.LastVisualX = v.Cursor.GetVisualX()
519
520         if usePlugin {
521                 return PostActionCall("Backspace", v)
522         }
523         return true
524 }
525
526 // DeleteWordRight deletes the word to the right of the cursor
527 func (v *View) DeleteWordRight(usePlugin bool) bool {
528         if usePlugin && !PreActionCall("DeleteWordRight", v) {
529                 return false
530         }
531
532         v.SelectWordRight(false)
533         if v.Cursor.HasSelection() {
534                 v.Cursor.DeleteSelection()
535                 v.Cursor.ResetSelection()
536         }
537
538         if usePlugin {
539                 return PostActionCall("DeleteWordRight", v)
540         }
541         return true
542 }
543
544 // DeleteWordLeft deletes the word to the left of the cursor
545 func (v *View) DeleteWordLeft(usePlugin bool) bool {
546         if usePlugin && !PreActionCall("DeleteWordLeft", v) {
547                 return false
548         }
549
550         v.SelectWordLeft(false)
551         if v.Cursor.HasSelection() {
552                 v.Cursor.DeleteSelection()
553                 v.Cursor.ResetSelection()
554         }
555
556         if usePlugin {
557                 return PostActionCall("DeleteWordLeft", v)
558         }
559         return true
560 }
561
562 // Delete deletes the next character
563 func (v *View) Delete(usePlugin bool) bool {
564         if usePlugin && !PreActionCall("Delete", v) {
565                 return false
566         }
567
568         if v.Cursor.HasSelection() {
569                 v.Cursor.DeleteSelection()
570                 v.Cursor.ResetSelection()
571         } else {
572                 loc := v.Cursor.Loc
573                 if loc.LessThan(v.Buf.End()) {
574                         v.Buf.Remove(loc, loc.Move(1, v.Buf))
575                 }
576         }
577
578         if usePlugin {
579                 return PostActionCall("Delete", v)
580         }
581         return true
582 }
583
584 // IndentSelection indents the current selection
585 func (v *View) IndentSelection(usePlugin bool) bool {
586         if usePlugin && !PreActionCall("IndentSelection", v) {
587                 return false
588         }
589
590         if v.Cursor.HasSelection() {
591                 start := v.Cursor.CurSelection[0]
592                 end := v.Cursor.CurSelection[1]
593                 if end.Y < start.Y {
594                         start, end = end, start
595                 }
596
597                 startY := start.Y
598                 endY := end.Move(-1, v.Buf).Y
599                 endX := end.Move(-1, v.Buf).X
600                 for y := startY; y <= endY; y++ {
601                         tabsize := len(v.Buf.IndentString())
602                         v.Buf.Insert(Loc{0, y}, v.Buf.IndentString())
603                         if y == startY && start.X > 0 {
604                                 v.Cursor.SetSelectionStart(start.Move(tabsize, v.Buf))
605                         }
606                         if y == endY {
607                                 v.Cursor.SetSelectionEnd(Loc{endX + tabsize + 1, endY})
608                         }
609                 }
610                 v.Cursor.Relocate()
611
612                 if usePlugin {
613                         return PostActionCall("IndentSelection", v)
614                 }
615                 return true
616         }
617         return false
618 }
619
620 // OutdentLine moves the current line back one indentation
621 func (v *View) OutdentLine(usePlugin bool) bool {
622         if usePlugin && !PreActionCall("OutdentLine", v) {
623                 return false
624         }
625
626         if v.Cursor.HasSelection() {
627                 return false
628         }
629
630         for x := 0; x < len(v.Buf.IndentString()); x++ {
631                 if len(GetLeadingWhitespace(v.Buf.Line(v.Cursor.Y))) == 0 {
632                         break
633                 }
634                 v.Buf.Remove(Loc{0, v.Cursor.Y}, Loc{1, v.Cursor.Y})
635                 v.Cursor.X -= 1
636         }
637         v.Cursor.Relocate()
638
639         if usePlugin {
640                 return PostActionCall("OutdentLine", v)
641         }
642         return true
643 }
644
645 // OutdentSelection takes the current selection and moves it back one indent level
646 func (v *View) OutdentSelection(usePlugin bool) bool {
647         if usePlugin && !PreActionCall("OutdentSelection", v) {
648                 return false
649         }
650
651         if v.Cursor.HasSelection() {
652                 start := v.Cursor.CurSelection[0]
653                 end := v.Cursor.CurSelection[1]
654                 if end.Y < start.Y {
655                         start, end = end, start
656                 }
657
658                 startY := start.Y
659                 endY := end.Move(-1, v.Buf).Y
660                 endX := end.Move(-1, v.Buf).X
661                 for y := startY; y <= endY; y++ {
662                         for x := 0; x < len(v.Buf.IndentString()); x++ {
663                                 if len(GetLeadingWhitespace(v.Buf.Line(y))) == 0 {
664                                         break
665                                 }
666                                 v.Buf.Remove(Loc{0, y}, Loc{1, y})
667                                 if y == startY && start.X > 0 {
668                                         v.Cursor.SetSelectionStart(start.Move(-1, v.Buf))
669                                 }
670                                 if y == endY {
671                                         v.Cursor.SetSelectionEnd(Loc{endX - x, endY})
672                                 }
673                         }
674                 }
675                 v.Cursor.Relocate()
676
677                 if usePlugin {
678                         return PostActionCall("OutdentSelection", v)
679                 }
680                 return true
681         }
682         return false
683 }
684
685 // InsertTab inserts a tab or spaces
686 func (v *View) InsertTab(usePlugin bool) bool {
687         if usePlugin && !PreActionCall("InsertTab", v) {
688                 return false
689         }
690
691         if v.Cursor.HasSelection() {
692                 return false
693         }
694
695         tabBytes := len(v.Buf.IndentString())
696         bytesUntilIndent := tabBytes - (v.Cursor.GetVisualX() % tabBytes)
697         v.Buf.Insert(v.Cursor.Loc, v.Buf.IndentString()[:bytesUntilIndent])
698         for i := 0; i < bytesUntilIndent; i++ {
699                 v.Cursor.Right()
700         }
701
702         if usePlugin {
703                 return PostActionCall("InsertTab", v)
704         }
705         return true
706 }
707
708 // Save the buffer to disk
709 func (v *View) Save(usePlugin bool) bool {
710         if usePlugin && !PreActionCall("Save", v) {
711                 return false
712         }
713
714         if v.Type == vtHelp {
715                 // We can't save the help text
716                 return false
717         }
718         // If this is an empty buffer, ask for a filename
719         if v.Buf.Path == "" {
720                 v.SaveAs(false)
721         }
722         err := v.Buf.Save()
723         if err != nil {
724                 if strings.HasSuffix(err.Error(), "permission denied") {
725                         choice, _ := messenger.YesNoPrompt("Permission denied. Do you want to save this file using sudo? (y,n)")
726                         if choice {
727                                 err = v.Buf.SaveWithSudo()
728                                 if err != nil {
729                                         messenger.Error(err.Error())
730                                         return false
731                                 }
732                                 messenger.Message("Saved " + v.Buf.Path)
733                         }
734                         messenger.Reset()
735                         messenger.Clear()
736                 } else {
737                         messenger.Error(err.Error())
738                 }
739         } else {
740                 messenger.Message("Saved " + v.Buf.Path)
741         }
742
743         if usePlugin {
744                 return PostActionCall("Save", v)
745         }
746         return false
747 }
748
749 // SaveAs saves the buffer to disk with the given name
750 func (v *View) SaveAs(usePlugin bool) bool {
751         filename, canceled := messenger.Prompt("Filename: ", "", "Save", NoCompletion)
752         if !canceled {
753                 // the filename might or might not be quoted, so unquote first then join the strings.
754                 filename = strings.Join(SplitCommandArgs(filename), " ")
755                 v.Buf.Path = filename
756                 v.Buf.name = filename
757
758                 v.Save(true)
759         }
760
761         return false
762 }
763
764 // Find opens a prompt and searches forward for the input
765 func (v *View) Find(usePlugin bool) bool {
766         if usePlugin && !PreActionCall("Find", v) {
767                 return false
768         }
769
770         searchStr := ""
771         if v.Cursor.HasSelection() {
772                 searchStart = ToCharPos(v.Cursor.CurSelection[1], v.Buf)
773                 searchStart = ToCharPos(v.Cursor.CurSelection[1], v.Buf)
774                 searchStr = v.Cursor.GetSelection()
775         } else {
776                 searchStart = ToCharPos(v.Cursor.Loc, v.Buf)
777         }
778         BeginSearch(searchStr)
779
780         if usePlugin {
781                 return PostActionCall("Find", v)
782         }
783         return true
784 }
785
786 // FindNext searches forwards for the last used search term
787 func (v *View) FindNext(usePlugin bool) bool {
788         if usePlugin && !PreActionCall("FindNext", v) {
789                 return false
790         }
791
792         if v.Cursor.HasSelection() {
793                 searchStart = ToCharPos(v.Cursor.CurSelection[1], v.Buf)
794                 // lastSearch = v.Cursor.GetSelection()
795         } else {
796                 searchStart = ToCharPos(v.Cursor.Loc, v.Buf)
797         }
798         if lastSearch == "" {
799                 return true
800         }
801         messenger.Message("Finding: " + lastSearch)
802         Search(lastSearch, v, true)
803
804         if usePlugin {
805                 return PostActionCall("FindNext", v)
806         }
807         return true
808 }
809
810 // FindPrevious searches backwards for the last used search term
811 func (v *View) FindPrevious(usePlugin bool) bool {
812         if usePlugin && !PreActionCall("FindPrevious", v) {
813                 return false
814         }
815
816         if v.Cursor.HasSelection() {
817                 searchStart = ToCharPos(v.Cursor.CurSelection[0], v.Buf)
818         } else {
819                 searchStart = ToCharPos(v.Cursor.Loc, v.Buf)
820         }
821         messenger.Message("Finding: " + lastSearch)
822         Search(lastSearch, v, false)
823
824         if usePlugin {
825                 return PostActionCall("FindPrevious", v)
826         }
827         return true
828 }
829
830 // Undo undoes the last action
831 func (v *View) Undo(usePlugin bool) bool {
832         if usePlugin && !PreActionCall("Undo", v) {
833                 return false
834         }
835
836         v.Buf.Undo()
837         messenger.Message("Undid action")
838
839         if usePlugin {
840                 return PostActionCall("Undo", v)
841         }
842         return true
843 }
844
845 // Redo redoes the last action
846 func (v *View) Redo(usePlugin bool) bool {
847         if usePlugin && !PreActionCall("Redo", v) {
848                 return false
849         }
850
851         v.Buf.Redo()
852         messenger.Message("Redid action")
853
854         if usePlugin {
855                 return PostActionCall("Redo", v)
856         }
857         return true
858 }
859
860 // Copy the selection to the system clipboard
861 func (v *View) Copy(usePlugin bool) bool {
862         if usePlugin && !PreActionCall("Copy", v) {
863                 return false
864         }
865
866         if v.Cursor.HasSelection() {
867                 v.Cursor.CopySelection("clipboard")
868                 v.freshClip = true
869                 messenger.Message("Copied selection")
870         }
871
872         if usePlugin {
873                 return PostActionCall("Copy", v)
874         }
875         return true
876 }
877
878 // CutLine cuts the current line to the clipboard
879 func (v *View) CutLine(usePlugin bool) bool {
880         if usePlugin && !PreActionCall("CutLine", v) {
881                 return false
882         }
883
884         v.Cursor.SelectLine()
885         if !v.Cursor.HasSelection() {
886                 return false
887         }
888         if v.freshClip == true {
889                 if v.Cursor.HasSelection() {
890                         if clip, err := clipboard.ReadAll("clipboard"); err != nil {
891                                 messenger.Error(err)
892                         } else {
893                                 clipboard.WriteAll(clip+v.Cursor.GetSelection(), "clipboard")
894                         }
895                 }
896         } else if time.Since(v.lastCutTime)/time.Second > 10*time.Second || v.freshClip == false {
897                 v.Copy(true)
898         }
899         v.freshClip = true
900         v.lastCutTime = time.Now()
901         v.Cursor.DeleteSelection()
902         v.Cursor.ResetSelection()
903         messenger.Message("Cut line")
904
905         if usePlugin {
906                 return PostActionCall("CutLine", v)
907         }
908         return true
909 }
910
911 // Cut the selection to the system clipboard
912 func (v *View) Cut(usePlugin bool) bool {
913         if usePlugin && !PreActionCall("Cut", v) {
914                 return false
915         }
916
917         if v.Cursor.HasSelection() {
918                 v.Cursor.CopySelection("clipboard")
919                 v.Cursor.DeleteSelection()
920                 v.Cursor.ResetSelection()
921                 v.freshClip = true
922                 messenger.Message("Cut selection")
923
924                 if usePlugin {
925                         return PostActionCall("Cut", v)
926                 }
927                 return true
928         }
929
930         return false
931 }
932
933 // DuplicateLine duplicates the current line or selection
934 func (v *View) DuplicateLine(usePlugin bool) bool {
935         if usePlugin && !PreActionCall("DuplicateLine", v) {
936                 return false
937         }
938
939         if v.Cursor.HasSelection() {
940                 v.Buf.Insert(v.Cursor.CurSelection[1], v.Cursor.GetSelection())
941         } else {
942                 v.Cursor.End()
943                 v.Buf.Insert(v.Cursor.Loc, "\n"+v.Buf.Line(v.Cursor.Y))
944                 v.Cursor.Right()
945         }
946
947         messenger.Message("Duplicated line")
948
949         if usePlugin {
950                 return PostActionCall("DuplicateLine", v)
951         }
952         return true
953 }
954
955 // DeleteLine deletes the current line
956 func (v *View) DeleteLine(usePlugin bool) bool {
957         if usePlugin && !PreActionCall("DeleteLine", v) {
958                 return false
959         }
960
961         v.Cursor.SelectLine()
962         if !v.Cursor.HasSelection() {
963                 return false
964         }
965         v.Cursor.DeleteSelection()
966         v.Cursor.ResetSelection()
967         messenger.Message("Deleted line")
968
969         if usePlugin {
970                 return PostActionCall("DeleteLine", v)
971         }
972         return true
973 }
974
975 // MoveLinesUp moves up the current line or selected lines if any
976 func (v *View) MoveLinesUp(usePlugin bool) bool {
977         if usePlugin && !PreActionCall("MoveLinesUp", v) {
978                 return false
979         }
980
981         if v.Cursor.HasSelection() {
982                 if v.Cursor.CurSelection[0].Y == 0 {
983                         messenger.Message("Can not move further up")
984                         return true
985                 }
986                 v.Buf.MoveLinesUp(
987                         v.Cursor.CurSelection[0].Y,
988                         v.Cursor.CurSelection[1].Y,
989                 )
990                 v.Cursor.UpN(1)
991                 v.Cursor.CurSelection[0].Y -= 1
992                 v.Cursor.CurSelection[1].Y -= 1
993                 messenger.Message("Moved up selected line(s)")
994         } else {
995                 if v.Cursor.Loc.Y == 0 {
996                         messenger.Message("Can not move further up")
997                         return true
998                 }
999                 v.Buf.MoveLinesUp(
1000                         v.Cursor.Loc.Y,
1001                         v.Cursor.Loc.Y+1,
1002                 )
1003                 v.Cursor.UpN(1)
1004                 messenger.Message("Moved up current line")
1005         }
1006         v.Buf.IsModified = true
1007
1008         if usePlugin {
1009                 return PostActionCall("MoveLinesUp", v)
1010         }
1011         return true
1012 }
1013
1014 // MoveLinesDown moves down the current line or selected lines if any
1015 func (v *View) MoveLinesDown(usePlugin bool) bool {
1016         if usePlugin && !PreActionCall("MoveLinesDown", v) {
1017                 return false
1018         }
1019
1020         if v.Cursor.HasSelection() {
1021                 if v.Cursor.CurSelection[1].Y >= len(v.Buf.lines) {
1022                         messenger.Message("Can not move further down")
1023                         return true
1024                 }
1025                 v.Buf.MoveLinesDown(
1026                         v.Cursor.CurSelection[0].Y,
1027                         v.Cursor.CurSelection[1].Y,
1028                 )
1029                 v.Cursor.DownN(1)
1030                 v.Cursor.CurSelection[0].Y += 1
1031                 v.Cursor.CurSelection[1].Y += 1
1032                 messenger.Message("Moved down selected line(s)")
1033         } else {
1034                 if v.Cursor.Loc.Y >= len(v.Buf.lines)-1 {
1035                         messenger.Message("Can not move further down")
1036                         return true
1037                 }
1038                 v.Buf.MoveLinesDown(
1039                         v.Cursor.Loc.Y,
1040                         v.Cursor.Loc.Y+1,
1041                 )
1042                 v.Cursor.DownN(1)
1043                 messenger.Message("Moved down current line")
1044         }
1045         v.Buf.IsModified = true
1046
1047         if usePlugin {
1048                 return PostActionCall("MoveLinesDown", v)
1049         }
1050         return true
1051 }
1052
1053 // Paste whatever is in the system clipboard into the buffer
1054 // Delete and paste if the user has a selection
1055 func (v *View) Paste(usePlugin bool) bool {
1056         if usePlugin && !PreActionCall("Paste", v) {
1057                 return false
1058         }
1059
1060         clip, _ := clipboard.ReadAll("clipboard")
1061         v.paste(clip)
1062
1063         if usePlugin {
1064                 return PostActionCall("Paste", v)
1065         }
1066         return true
1067 }
1068
1069 // PastePrimary pastes from the primary clipboard (only use on linux)
1070 func (v *View) PastePrimary(usePlugin bool) bool {
1071         if usePlugin && !PreActionCall("Paste", v) {
1072                 return false
1073         }
1074
1075         clip, _ := clipboard.ReadAll("primary")
1076         v.paste(clip)
1077
1078         if usePlugin {
1079                 return PostActionCall("Paste", v)
1080         }
1081         return true
1082 }
1083
1084 // SelectAll selects the entire buffer
1085 func (v *View) SelectAll(usePlugin bool) bool {
1086         if usePlugin && !PreActionCall("SelectAll", v) {
1087                 return false
1088         }
1089
1090         v.Cursor.SetSelectionStart(v.Buf.Start())
1091         v.Cursor.SetSelectionEnd(v.Buf.End())
1092         // Put the cursor at the beginning
1093         v.Cursor.X = 0
1094         v.Cursor.Y = 0
1095
1096         if usePlugin {
1097                 return PostActionCall("SelectAll", v)
1098         }
1099         return true
1100 }
1101
1102 // OpenFile opens a new file in the buffer
1103 func (v *View) OpenFile(usePlugin bool) bool {
1104         if usePlugin && !PreActionCall("OpenFile", v) {
1105                 return false
1106         }
1107
1108         if v.CanClose() {
1109                 input, canceled := messenger.Prompt("> ", "open ", "Open", CommandCompletion)
1110                 if !canceled {
1111                         HandleCommand(input)
1112                         if usePlugin {
1113                                 return PostActionCall("OpenFile", v)
1114                         }
1115                 }
1116         }
1117         return false
1118 }
1119
1120 // Start moves the viewport to the start of the buffer
1121 func (v *View) Start(usePlugin bool) bool {
1122         if usePlugin && !PreActionCall("Start", v) {
1123                 return false
1124         }
1125
1126         v.Topline = 0
1127
1128         if usePlugin {
1129                 return PostActionCall("Start", v)
1130         }
1131         return false
1132 }
1133
1134 // End moves the viewport to the end of the buffer
1135 func (v *View) End(usePlugin bool) bool {
1136         if usePlugin && !PreActionCall("End", v) {
1137                 return false
1138         }
1139
1140         if v.Height > v.Buf.NumLines {
1141                 v.Topline = 0
1142         } else {
1143                 v.Topline = v.Buf.NumLines - v.Height
1144         }
1145
1146         if usePlugin {
1147                 return PostActionCall("End", v)
1148         }
1149         return false
1150 }
1151
1152 // PageUp scrolls the view up a page
1153 func (v *View) PageUp(usePlugin bool) bool {
1154         if usePlugin && !PreActionCall("PageUp", v) {
1155                 return false
1156         }
1157
1158         if v.Topline > v.Height {
1159                 v.ScrollUp(v.Height)
1160         } else {
1161                 v.Topline = 0
1162         }
1163
1164         if usePlugin {
1165                 return PostActionCall("PageUp", v)
1166         }
1167         return false
1168 }
1169
1170 // PageDown scrolls the view down a page
1171 func (v *View) PageDown(usePlugin bool) bool {
1172         if usePlugin && !PreActionCall("PageDown", v) {
1173                 return false
1174         }
1175
1176         if v.Buf.NumLines-(v.Topline+v.Height) > v.Height {
1177                 v.ScrollDown(v.Height)
1178         } else if v.Buf.NumLines >= v.Height {
1179                 v.Topline = v.Buf.NumLines - v.Height
1180         }
1181
1182         if usePlugin {
1183                 return PostActionCall("PageDown", v)
1184         }
1185         return false
1186 }
1187
1188 // CursorPageUp places the cursor a page up
1189 func (v *View) CursorPageUp(usePlugin bool) bool {
1190         if usePlugin && !PreActionCall("CursorPageUp", v) {
1191                 return false
1192         }
1193
1194         v.deselect(0)
1195
1196         if v.Cursor.HasSelection() {
1197                 v.Cursor.Loc = v.Cursor.CurSelection[0]
1198                 v.Cursor.ResetSelection()
1199         }
1200         v.Cursor.UpN(v.Height)
1201
1202         if usePlugin {
1203                 return PostActionCall("CursorPageUp", v)
1204         }
1205         return true
1206 }
1207
1208 // CursorPageDown places the cursor a page up
1209 func (v *View) CursorPageDown(usePlugin bool) bool {
1210         if usePlugin && !PreActionCall("CursorPageDown", v) {
1211                 return false
1212         }
1213
1214         v.deselect(0)
1215
1216         if v.Cursor.HasSelection() {
1217                 v.Cursor.Loc = v.Cursor.CurSelection[1]
1218                 v.Cursor.ResetSelection()
1219         }
1220         v.Cursor.DownN(v.Height)
1221
1222         if usePlugin {
1223                 return PostActionCall("CursorPageDown", v)
1224         }
1225         return true
1226 }
1227
1228 // HalfPageUp scrolls the view up half a page
1229 func (v *View) HalfPageUp(usePlugin bool) bool {
1230         if usePlugin && !PreActionCall("HalfPageUp", v) {
1231                 return false
1232         }
1233
1234         if v.Topline > v.Height/2 {
1235                 v.ScrollUp(v.Height / 2)
1236         } else {
1237                 v.Topline = 0
1238         }
1239
1240         if usePlugin {
1241                 return PostActionCall("HalfPageUp", v)
1242         }
1243         return false
1244 }
1245
1246 // HalfPageDown scrolls the view down half a page
1247 func (v *View) HalfPageDown(usePlugin bool) bool {
1248         if usePlugin && !PreActionCall("HalfPageDown", v) {
1249                 return false
1250         }
1251
1252         if v.Buf.NumLines-(v.Topline+v.Height) > v.Height/2 {
1253                 v.ScrollDown(v.Height / 2)
1254         } else {
1255                 if v.Buf.NumLines >= v.Height {
1256                         v.Topline = v.Buf.NumLines - v.Height
1257                 }
1258         }
1259
1260         if usePlugin {
1261                 return PostActionCall("HalfPageDown", v)
1262         }
1263         return false
1264 }
1265
1266 // ToggleRuler turns line numbers off and on
1267 func (v *View) ToggleRuler(usePlugin bool) bool {
1268         if usePlugin && !PreActionCall("ToggleRuler", v) {
1269                 return false
1270         }
1271
1272         if v.Buf.Settings["ruler"] == false {
1273                 v.Buf.Settings["ruler"] = true
1274                 messenger.Message("Enabled ruler")
1275         } else {
1276                 v.Buf.Settings["ruler"] = false
1277                 messenger.Message("Disabled ruler")
1278         }
1279
1280         if usePlugin {
1281                 return PostActionCall("ToggleRuler", v)
1282         }
1283         return false
1284 }
1285
1286 // JumpLine jumps to a line and moves the view accordingly.
1287 func (v *View) JumpLine(usePlugin bool) bool {
1288         if usePlugin && !PreActionCall("JumpLine", v) {
1289                 return false
1290         }
1291
1292         // Prompt for line number
1293         linestring, canceled := messenger.Prompt("Jump to line # ", "", "LineNumber", NoCompletion)
1294         if canceled {
1295                 return false
1296         }
1297         lineint, err := strconv.Atoi(linestring)
1298         lineint = lineint - 1 // fix offset
1299         if err != nil {
1300                 messenger.Error(err) // return errors
1301                 return false
1302         }
1303         // Move cursor and view if possible.
1304         if lineint < v.Buf.NumLines && lineint >= 0 {
1305                 v.Cursor.X = 0
1306                 v.Cursor.Y = lineint
1307
1308                 if usePlugin {
1309                         return PostActionCall("JumpLine", v)
1310                 }
1311                 return true
1312         }
1313         messenger.Error("Only ", v.Buf.NumLines, " lines to jump")
1314         return false
1315 }
1316
1317 // ClearStatus clears the messenger bar
1318 func (v *View) ClearStatus(usePlugin bool) bool {
1319         if usePlugin && !PreActionCall("ClearStatus", v) {
1320                 return false
1321         }
1322
1323         messenger.Message("")
1324
1325         if usePlugin {
1326                 return PostActionCall("ClearStatus", v)
1327         }
1328         return false
1329 }
1330
1331 // ToggleHelp toggles the help screen
1332 func (v *View) ToggleHelp(usePlugin bool) bool {
1333         if usePlugin && !PreActionCall("ToggleHelp", v) {
1334                 return false
1335         }
1336
1337         if v.Type != vtHelp {
1338                 // Open the default help
1339                 v.openHelp("help")
1340         } else {
1341                 v.Quit(true)
1342         }
1343
1344         if usePlugin {
1345                 return PostActionCall("ToggleHelp", v)
1346         }
1347         return true
1348 }
1349
1350 // ShellMode opens a terminal to run a shell command
1351 func (v *View) ShellMode(usePlugin bool) bool {
1352         if usePlugin && !PreActionCall("ShellMode", v) {
1353                 return false
1354         }
1355
1356         input, canceled := messenger.Prompt("$ ", "", "Shell", NoCompletion)
1357         if !canceled {
1358                 // The true here is for openTerm to make the command interactive
1359                 HandleShellCommand(input, true, true)
1360                 if usePlugin {
1361                         return PostActionCall("ShellMode", v)
1362                 }
1363         }
1364         return false
1365 }
1366
1367 // CommandMode lets the user enter a command
1368 func (v *View) CommandMode(usePlugin bool) bool {
1369         if usePlugin && !PreActionCall("CommandMode", v) {
1370                 return false
1371         }
1372
1373         input, canceled := messenger.Prompt("> ", "", "Command", CommandCompletion)
1374         if !canceled {
1375                 HandleCommand(input)
1376                 if usePlugin {
1377                         return PostActionCall("CommandMode", v)
1378                 }
1379         }
1380
1381         return false
1382 }
1383
1384 // Escape leaves current mode
1385 func (v *View) Escape(usePlugin bool) bool {
1386         // check if user is searching, or the last search is still active
1387         if searching || lastSearch != "" {
1388                 ExitSearch(v)
1389                 return true
1390         }
1391         // check if a prompt is shown, hide it and don't quit
1392         if messenger.hasPrompt {
1393                 messenger.Reset() // FIXME
1394                 return true
1395         }
1396
1397         return false
1398 }
1399
1400 // Quit this will close the current tab or view that is open
1401 func (v *View) Quit(usePlugin bool) bool {
1402         if usePlugin && !PreActionCall("Quit", v) {
1403                 return false
1404         }
1405
1406         // Make sure not to quit if there are unsaved changes
1407         if v.CanClose() {
1408                 v.CloseBuffer()
1409                 if len(tabs[curTab].views) > 1 {
1410                         v.splitNode.Delete()
1411                         tabs[v.TabNum].Cleanup()
1412                         tabs[v.TabNum].Resize()
1413                 } else if len(tabs) > 1 {
1414                         if len(tabs[v.TabNum].views) == 1 {
1415                                 tabs = tabs[:v.TabNum+copy(tabs[v.TabNum:], tabs[v.TabNum+1:])]
1416                                 for i, t := range tabs {
1417                                         t.SetNum(i)
1418                                 }
1419                                 if curTab >= len(tabs) {
1420                                         curTab--
1421                                 }
1422                                 if curTab == 0 {
1423                                         CurView().ToggleTabbar()
1424                                 }
1425                         }
1426                 } else {
1427                         if usePlugin {
1428                                 PostActionCall("Quit", v)
1429                         }
1430
1431                         screen.Fini()
1432                         os.Exit(0)
1433                 }
1434         }
1435
1436         if usePlugin {
1437                 return PostActionCall("Quit", v)
1438         }
1439         return false
1440 }
1441
1442 // QuitAll quits the whole editor; all splits and tabs
1443 func (v *View) QuitAll(usePlugin bool) bool {
1444         if usePlugin && !PreActionCall("QuitAll", v) {
1445                 return false
1446         }
1447
1448         closeAll := true
1449         for _, tab := range tabs {
1450                 for _, v := range tab.views {
1451                         if !v.CanClose() {
1452                                 closeAll = false
1453                         }
1454                 }
1455         }
1456
1457         if closeAll {
1458                 // only quit if all of the buffers can be closed and the user confirms that they actually want to quit everything
1459                 shouldQuit, _ := messenger.YesNoPrompt("Do you want to quit micro (all open files will be closed)?")
1460
1461                 if shouldQuit {
1462                         for _, tab := range tabs {
1463                                 for _, v := range tab.views {
1464                                         v.CloseBuffer()
1465                                 }
1466                         }
1467
1468                         if usePlugin {
1469                                 PostActionCall("QuitAll", v)
1470                         }
1471
1472                         screen.Fini()
1473                         os.Exit(0)
1474                 }
1475         }
1476
1477         return false
1478 }
1479
1480 // AddTab adds a new tab with an empty buffer
1481 func (v *View) AddTab(usePlugin bool) bool {
1482         if usePlugin && !PreActionCall("AddTab", v) {
1483                 return false
1484         }
1485
1486         tab := NewTabFromView(NewView(NewBuffer(strings.NewReader(""), "")))
1487         tab.SetNum(len(tabs))
1488         tabs = append(tabs, tab)
1489         curTab = len(tabs) - 1
1490         if len(tabs) == 2 {
1491                 for _, t := range tabs {
1492                         for _, v := range t.views {
1493                                 v.ToggleTabbar()
1494                         }
1495                 }
1496         }
1497
1498         if usePlugin {
1499                 return PostActionCall("AddTab", v)
1500         }
1501         return true
1502 }
1503
1504 // PreviousTab switches to the previous tab in the tab list
1505 func (v *View) PreviousTab(usePlugin bool) bool {
1506         if usePlugin && !PreActionCall("PreviousTab", v) {
1507                 return false
1508         }
1509
1510         if curTab > 0 {
1511                 curTab--
1512         } else if curTab == 0 {
1513                 curTab = len(tabs) - 1
1514         }
1515
1516         if usePlugin {
1517                 return PostActionCall("PreviousTab", v)
1518         }
1519         return false
1520 }
1521
1522 // NextTab switches to the next tab in the tab list
1523 func (v *View) NextTab(usePlugin bool) bool {
1524         if usePlugin && !PreActionCall("NextTab", v) {
1525                 return false
1526         }
1527
1528         if curTab < len(tabs)-1 {
1529                 curTab++
1530         } else if curTab == len(tabs)-1 {
1531                 curTab = 0
1532         }
1533
1534         if usePlugin {
1535                 return PostActionCall("NextTab", v)
1536         }
1537         return false
1538 }
1539
1540 // VSplitBinding opens an empty vertical split
1541 func (v *View) VSplitBinding(usePlugin bool) bool {
1542         if usePlugin && !PreActionCall("VSplit", v) {
1543                 return false
1544         }
1545
1546         v.VSplit(NewBuffer(strings.NewReader(""), ""))
1547
1548         if usePlugin {
1549                 return PostActionCall("VSplit", v)
1550         }
1551         return false
1552 }
1553
1554 // HSplitBinding opens an empty horizontal split
1555 func (v *View) HSplitBinding(usePlugin bool) bool {
1556         if usePlugin && !PreActionCall("HSplit", v) {
1557                 return false
1558         }
1559
1560         v.HSplit(NewBuffer(strings.NewReader(""), ""))
1561
1562         if usePlugin {
1563                 return PostActionCall("HSplit", v)
1564         }
1565         return false
1566 }
1567
1568 // Unsplit closes all splits in the current tab except the active one
1569 func (v *View) Unsplit(usePlugin bool) bool {
1570         if usePlugin && !PreActionCall("Unsplit", v) {
1571                 return false
1572         }
1573
1574         curView := tabs[curTab].CurView
1575         for i := len(tabs[curTab].views) - 1; i >= 0; i-- {
1576                 view := tabs[curTab].views[i]
1577                 if view != nil && view.Num != curView {
1578                         view.Quit(true)
1579                         // messenger.Message("Quit ", view.Buf.Path)
1580                 }
1581         }
1582
1583         if usePlugin {
1584                 return PostActionCall("Unsplit", v)
1585         }
1586         return false
1587 }
1588
1589 // NextSplit changes the view to the next split
1590 func (v *View) NextSplit(usePlugin bool) bool {
1591         if usePlugin && !PreActionCall("NextSplit", v) {
1592                 return false
1593         }
1594
1595         tab := tabs[curTab]
1596         if tab.CurView < len(tab.views)-1 {
1597                 tab.CurView++
1598         } else {
1599                 tab.CurView = 0
1600         }
1601
1602         if usePlugin {
1603                 return PostActionCall("NextSplit", v)
1604         }
1605         return false
1606 }
1607
1608 // PreviousSplit changes the view to the previous split
1609 func (v *View) PreviousSplit(usePlugin bool) bool {
1610         if usePlugin && !PreActionCall("PreviousSplit", v) {
1611                 return false
1612         }
1613
1614         tab := tabs[curTab]
1615         if tab.CurView > 0 {
1616                 tab.CurView--
1617         } else {
1618                 tab.CurView = len(tab.views) - 1
1619         }
1620
1621         if usePlugin {
1622                 return PostActionCall("PreviousSplit", v)
1623         }
1624         return false
1625 }
1626
1627 var curMacro []interface{}
1628 var recordingMacro bool
1629
1630 // ToggleMacro toggles recording of a macro
1631 func (v *View) ToggleMacro(usePlugin bool) bool {
1632         if usePlugin && !PreActionCall("ToggleMacro", v) {
1633                 return false
1634         }
1635
1636         recordingMacro = !recordingMacro
1637
1638         if recordingMacro {
1639                 curMacro = []interface{}{}
1640                 messenger.Message("Recording")
1641         } else {
1642                 messenger.Message("Stopped recording")
1643         }
1644
1645         if usePlugin {
1646                 return PostActionCall("ToggleMacro", v)
1647         }
1648         return true
1649 }
1650
1651 // PlayMacro plays back the most recently recorded macro
1652 func (v *View) PlayMacro(usePlugin bool) bool {
1653         if usePlugin && !PreActionCall("PlayMacro", v) {
1654                 return false
1655         }
1656
1657         for _, action := range curMacro {
1658                 switch t := action.(type) {
1659                 case rune:
1660                         // Insert a character
1661                         if v.Cursor.HasSelection() {
1662                                 v.Cursor.DeleteSelection()
1663                                 v.Cursor.ResetSelection()
1664                         }
1665                         v.Buf.Insert(v.Cursor.Loc, string(t))
1666                         v.Cursor.Right()
1667
1668                         for pl := range loadedPlugins {
1669                                 _, err := Call(pl+".onRune", string(t), v)
1670                                 if err != nil && !strings.HasPrefix(err.Error(), "function does not exist") {
1671                                         TermMessage(err)
1672                                 }
1673                         }
1674                 case func(*View, bool) bool:
1675                         t(v, true)
1676                 }
1677         }
1678
1679         if usePlugin {
1680                 return PostActionCall("PlayMacro", v)
1681         }
1682         return true
1683 }
1684
1685 // None is no action
1686 func None() bool {
1687         return false
1688 }