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