]> git.lizzy.rs Git - micro.git/blob - cmd/micro/actions.go
5b64a4d53cac80bd2f4154421b34b3b40166bb31
[micro.git] / cmd / micro / actions.go
1 package main
2
3 import (
4         "fmt"
5         "os"
6         "regexp"
7         "strconv"
8         "strings"
9         "time"
10         "unicode/utf8"
11
12         "github.com/yuin/gopher-lua"
13         "github.com/zyedidia/clipboard"
14         "github.com/zyedidia/micro/cmd/micro/shellwords"
15         "github.com/zyedidia/tcell"
16 )
17
18 // PreActionCall executes the lua pre callback if possible
19 func PreActionCall(funcName string, view *View, args ...interface{}) bool {
20         executeAction := true
21         for pl := range loadedPlugins {
22                 ret, err := Call(pl+".pre"+funcName, append([]interface{}{view}, args...)...)
23                 if err != nil && !strings.HasPrefix(err.Error(), "function does not exist") {
24                         TermMessage(err)
25                         continue
26                 }
27                 if ret == lua.LFalse {
28                         executeAction = false
29                 }
30         }
31         return executeAction
32 }
33
34 // PostActionCall executes the lua plugin callback if possible
35 func PostActionCall(funcName string, view *View, args ...interface{}) bool {
36         relocate := true
37         for pl := range loadedPlugins {
38                 ret, err := Call(pl+".on"+funcName, append([]interface{}{view}, args...)...)
39                 if err != nil && !strings.HasPrefix(err.Error(), "function does not exist") {
40                         TermMessage(err)
41                         continue
42                 }
43                 if ret == lua.LFalse {
44                         relocate = false
45                 }
46         }
47         return relocate
48 }
49
50 func (v *View) deselect(index int) bool {
51         if v.Cursor.HasSelection() {
52                 v.Cursor.Loc = v.Cursor.CurSelection[index]
53                 v.Cursor.ResetSelection()
54                 v.Cursor.StoreVisualX()
55                 return true
56         }
57         return false
58 }
59
60 // MousePress is the event that should happen when a normal click happens
61 // This is almost always bound to left click
62 func (v *View) MousePress(usePlugin bool, e *tcell.EventMouse) bool {
63         if usePlugin && !PreActionCall("MousePress", v, e) {
64                 return false
65         }
66
67         x, y := e.Position()
68         x -= v.lineNumOffset - v.leftCol + v.x
69         y += v.Topline - v.y
70
71         // This is usually bound to left click
72         v.MoveToMouseClick(x, y)
73         if v.mouseReleased {
74                 if len(v.Buf.cursors) > 1 {
75                         for i := 1; i < len(v.Buf.cursors); i++ {
76                                 v.Buf.cursors[i] = nil
77                         }
78                         v.Buf.cursors = v.Buf.cursors[:1]
79                         v.Buf.UpdateCursors()
80                         v.Cursor.ResetSelection()
81                         v.Relocate()
82                 }
83                 if time.Since(v.lastClickTime)/time.Millisecond < doubleClickThreshold && (x == v.lastLoc.X && y == v.lastLoc.Y) {
84                         if v.doubleClick {
85                                 // Triple click
86                                 v.lastClickTime = time.Now()
87
88                                 v.tripleClick = true
89                                 v.doubleClick = false
90
91                                 v.Cursor.SelectLine()
92                                 v.Cursor.CopySelection("primary")
93                         } else {
94                                 // Double click
95                                 v.lastClickTime = time.Now()
96
97                                 v.doubleClick = true
98                                 v.tripleClick = false
99
100                                 v.Cursor.SelectWord()
101                                 v.Cursor.CopySelection("primary")
102                         }
103                 } else {
104                         v.doubleClick = false
105                         v.tripleClick = false
106                         v.lastClickTime = time.Now()
107
108                         v.Cursor.OrigSelection[0] = v.Cursor.Loc
109                         v.Cursor.CurSelection[0] = v.Cursor.Loc
110                         v.Cursor.CurSelection[1] = v.Cursor.Loc
111                 }
112                 v.mouseReleased = false
113         } else if !v.mouseReleased {
114                 if v.tripleClick {
115                         v.Cursor.AddLineToSelection()
116                 } else if v.doubleClick {
117                         v.Cursor.AddWordToSelection()
118                 } else {
119                         v.Cursor.SetSelectionEnd(v.Cursor.Loc)
120                         v.Cursor.CopySelection("primary")
121                 }
122         }
123
124         v.lastLoc = Loc{x, y}
125
126         if usePlugin {
127                 PostActionCall("MousePress", v, e)
128         }
129         return false
130 }
131
132 // ScrollUpAction scrolls the view up
133 func (v *View) ScrollUpAction(usePlugin bool) bool {
134         if v.mainCursor() {
135                 if usePlugin && !PreActionCall("ScrollUp", v) {
136                         return false
137                 }
138
139                 scrollspeed := int(v.Buf.Settings["scrollspeed"].(float64))
140                 v.ScrollUp(scrollspeed)
141
142                 if usePlugin {
143                         PostActionCall("ScrollUp", v)
144                 }
145         }
146         return false
147 }
148
149 // ScrollDownAction scrolls the view up
150 func (v *View) ScrollDownAction(usePlugin bool) bool {
151         if v.mainCursor() {
152                 if usePlugin && !PreActionCall("ScrollDown", v) {
153                         return false
154                 }
155
156                 scrollspeed := int(v.Buf.Settings["scrollspeed"].(float64))
157                 v.ScrollDown(scrollspeed)
158
159                 if usePlugin {
160                         PostActionCall("ScrollDown", v)
161                 }
162         }
163         return false
164 }
165
166 // Center centers the view on the cursor
167 func (v *View) Center(usePlugin bool) bool {
168         if usePlugin && !PreActionCall("Center", v) {
169                 return false
170         }
171
172         v.Topline = v.Cursor.Y - v.Height/2
173         if v.Topline+v.Height > v.Buf.NumLines {
174                 v.Topline = v.Buf.NumLines - v.Height
175         }
176         if v.Topline < 0 {
177                 v.Topline = 0
178         }
179
180         if usePlugin {
181                 return PostActionCall("Center", v)
182         }
183         return true
184 }
185
186 // CursorUp moves the cursor up
187 func (v *View) CursorUp(usePlugin bool) bool {
188         if usePlugin && !PreActionCall("CursorUp", v) {
189                 return false
190         }
191
192         v.deselect(0)
193         v.Cursor.Up()
194
195         if usePlugin {
196                 return PostActionCall("CursorUp", v)
197         }
198         return true
199 }
200
201 // CursorDown moves the cursor down
202 func (v *View) CursorDown(usePlugin bool) bool {
203         if usePlugin && !PreActionCall("CursorDown", v) {
204                 return false
205         }
206
207         v.deselect(1)
208         v.Cursor.Down()
209
210         if usePlugin {
211                 return PostActionCall("CursorDown", v)
212         }
213         return true
214 }
215
216 // CursorLeft moves the cursor left
217 func (v *View) CursorLeft(usePlugin bool) bool {
218         if usePlugin && !PreActionCall("CursorLeft", v) {
219                 return false
220         }
221
222         if v.Cursor.HasSelection() {
223                 v.Cursor.Loc = v.Cursor.CurSelection[0]
224                 v.Cursor.ResetSelection()
225                 v.Cursor.StoreVisualX()
226         } else {
227                 tabstospaces := v.Buf.Settings["tabstospaces"].(bool)
228                 tabmovement := v.Buf.Settings["tabmovement"].(bool)
229                 if tabstospaces && tabmovement {
230                         tabsize := int(v.Buf.Settings["tabsize"].(float64))
231                         line := v.Buf.Line(v.Cursor.Y)
232                         if v.Cursor.X-tabsize >= 0 && line[v.Cursor.X-tabsize:v.Cursor.X] == Spaces(tabsize) && IsStrWhitespace(line[0:v.Cursor.X-tabsize]) {
233                                 for i := 0; i < tabsize; i++ {
234                                         v.Cursor.Left()
235                                 }
236                         } else {
237                                 v.Cursor.Left()
238                         }
239                 } else {
240                         v.Cursor.Left()
241                 }
242         }
243
244         if usePlugin {
245                 return PostActionCall("CursorLeft", v)
246         }
247         return true
248 }
249
250 // CursorRight moves the cursor right
251 func (v *View) CursorRight(usePlugin bool) bool {
252         if usePlugin && !PreActionCall("CursorRight", v) {
253                 return false
254         }
255
256         if v.Cursor.HasSelection() {
257                 v.Cursor.Loc = v.Cursor.CurSelection[1]
258                 v.Cursor.ResetSelection()
259                 v.Cursor.StoreVisualX()
260         } else {
261                 tabstospaces := v.Buf.Settings["tabstospaces"].(bool)
262                 tabmovement := v.Buf.Settings["tabmovement"].(bool)
263                 if tabstospaces && tabmovement {
264                         tabsize := int(v.Buf.Settings["tabsize"].(float64))
265                         line := v.Buf.Line(v.Cursor.Y)
266                         if v.Cursor.X+tabsize < Count(line) && line[v.Cursor.X:v.Cursor.X+tabsize] == Spaces(tabsize) && IsStrWhitespace(line[0:v.Cursor.X]) {
267                                 for i := 0; i < tabsize; i++ {
268                                         v.Cursor.Right()
269                                 }
270                         } else {
271                                 v.Cursor.Right()
272                         }
273                 } else {
274                         v.Cursor.Right()
275                 }
276         }
277
278         if usePlugin {
279                 return PostActionCall("CursorRight", v)
280         }
281         return true
282 }
283
284 // WordRight moves the cursor one word to the right
285 func (v *View) WordRight(usePlugin bool) bool {
286         if usePlugin && !PreActionCall("WordRight", v) {
287                 return false
288         }
289
290         v.Cursor.WordRight()
291
292         if usePlugin {
293                 return PostActionCall("WordRight", v)
294         }
295         return true
296 }
297
298 // WordLeft moves the cursor one word to the left
299 func (v *View) WordLeft(usePlugin bool) bool {
300         if usePlugin && !PreActionCall("WordLeft", v) {
301                 return false
302         }
303
304         v.Cursor.WordLeft()
305
306         if usePlugin {
307                 return PostActionCall("WordLeft", v)
308         }
309         return true
310 }
311
312 // SelectUp selects up one line
313 func (v *View) SelectUp(usePlugin bool) bool {
314         if usePlugin && !PreActionCall("SelectUp", v) {
315                 return false
316         }
317
318         if !v.Cursor.HasSelection() {
319                 v.Cursor.OrigSelection[0] = v.Cursor.Loc
320         }
321         v.Cursor.Up()
322         v.Cursor.SelectTo(v.Cursor.Loc)
323
324         if usePlugin {
325                 return PostActionCall("SelectUp", v)
326         }
327         return true
328 }
329
330 // SelectDown selects down one line
331 func (v *View) SelectDown(usePlugin bool) bool {
332         if usePlugin && !PreActionCall("SelectDown", v) {
333                 return false
334         }
335
336         if !v.Cursor.HasSelection() {
337                 v.Cursor.OrigSelection[0] = v.Cursor.Loc
338         }
339         v.Cursor.Down()
340         v.Cursor.SelectTo(v.Cursor.Loc)
341
342         if usePlugin {
343                 return PostActionCall("SelectDown", v)
344         }
345         return true
346 }
347
348 // SelectLeft selects the character to the left of the cursor
349 func (v *View) SelectLeft(usePlugin bool) bool {
350         if usePlugin && !PreActionCall("SelectLeft", v) {
351                 return false
352         }
353
354         loc := v.Cursor.Loc
355         count := v.Buf.End()
356         if loc.GreaterThan(count) {
357                 loc = count
358         }
359         if !v.Cursor.HasSelection() {
360                 v.Cursor.OrigSelection[0] = loc
361         }
362         v.Cursor.Left()
363         v.Cursor.SelectTo(v.Cursor.Loc)
364
365         if usePlugin {
366                 return PostActionCall("SelectLeft", v)
367         }
368         return true
369 }
370
371 // SelectRight selects the character to the right of the cursor
372 func (v *View) SelectRight(usePlugin bool) bool {
373         if usePlugin && !PreActionCall("SelectRight", v) {
374                 return false
375         }
376
377         loc := v.Cursor.Loc
378         count := v.Buf.End()
379         if loc.GreaterThan(count) {
380                 loc = count
381         }
382         if !v.Cursor.HasSelection() {
383                 v.Cursor.OrigSelection[0] = loc
384         }
385         v.Cursor.Right()
386         v.Cursor.SelectTo(v.Cursor.Loc)
387
388         if usePlugin {
389                 return PostActionCall("SelectRight", v)
390         }
391         return true
392 }
393
394 // SelectWordRight selects the word to the right of the cursor
395 func (v *View) SelectWordRight(usePlugin bool) bool {
396         if usePlugin && !PreActionCall("SelectWordRight", v) {
397                 return false
398         }
399
400         if !v.Cursor.HasSelection() {
401                 v.Cursor.OrigSelection[0] = v.Cursor.Loc
402         }
403         v.Cursor.WordRight()
404         v.Cursor.SelectTo(v.Cursor.Loc)
405
406         if usePlugin {
407                 return PostActionCall("SelectWordRight", v)
408         }
409         return true
410 }
411
412 // SelectWordLeft selects the word to the left of the cursor
413 func (v *View) SelectWordLeft(usePlugin bool) bool {
414         if usePlugin && !PreActionCall("SelectWordLeft", v) {
415                 return false
416         }
417
418         if !v.Cursor.HasSelection() {
419                 v.Cursor.OrigSelection[0] = v.Cursor.Loc
420         }
421         v.Cursor.WordLeft()
422         v.Cursor.SelectTo(v.Cursor.Loc)
423
424         if usePlugin {
425                 return PostActionCall("SelectWordLeft", v)
426         }
427         return true
428 }
429
430 // StartOfLine moves the cursor to the start of the line
431 func (v *View) StartOfLine(usePlugin bool) bool {
432         if usePlugin && !PreActionCall("StartOfLine", v) {
433                 return false
434         }
435
436         v.deselect(0)
437
438         v.Cursor.Start()
439
440         if usePlugin {
441                 return PostActionCall("StartOfLine", v)
442         }
443         return true
444 }
445
446 // EndOfLine moves the cursor to the end of the line
447 func (v *View) EndOfLine(usePlugin bool) bool {
448         if usePlugin && !PreActionCall("EndOfLine", v) {
449                 return false
450         }
451
452         v.deselect(0)
453
454         v.Cursor.End()
455
456         if usePlugin {
457                 return PostActionCall("EndOfLine", v)
458         }
459         return true
460 }
461
462 // SelectLine selects the entire current line
463 func (v *View) SelectLine(usePlugin bool) bool {
464         if usePlugin && !PreActionCall("SelectLine", v) {
465                 return false
466         }
467
468         v.Cursor.SelectLine()
469
470         if usePlugin {
471                 return PostActionCall("SelectLine", v)
472         }
473         return true
474 }
475
476 // SelectToStartOfLine selects to the start of the current line
477 func (v *View) SelectToStartOfLine(usePlugin bool) bool {
478         if usePlugin && !PreActionCall("SelectToStartOfLine", v) {
479                 return false
480         }
481
482         if !v.Cursor.HasSelection() {
483                 v.Cursor.OrigSelection[0] = v.Cursor.Loc
484         }
485         v.Cursor.Start()
486         v.Cursor.SelectTo(v.Cursor.Loc)
487
488         if usePlugin {
489                 return PostActionCall("SelectToStartOfLine", v)
490         }
491         return true
492 }
493
494 // SelectToEndOfLine selects to the end of the current line
495 func (v *View) SelectToEndOfLine(usePlugin bool) bool {
496         if usePlugin && !PreActionCall("SelectToEndOfLine", v) {
497                 return false
498         }
499
500         if !v.Cursor.HasSelection() {
501                 v.Cursor.OrigSelection[0] = v.Cursor.Loc
502         }
503         v.Cursor.End()
504         v.Cursor.SelectTo(v.Cursor.Loc)
505
506         if usePlugin {
507                 return PostActionCall("SelectToEndOfLine", v)
508         }
509         return true
510 }
511
512 // ParagraphPrevious moves the cursor to the previous empty line, or beginning of the buffer if there's none
513 func (v *View) ParagraphPrevious(usePlugin bool) bool {
514         if usePlugin && !PreActionCall("ParagraphPrevious", v) {
515                 return false
516         }
517         var line int
518         for line = v.Cursor.Y; line > 0; line-- {
519                 if len(v.Buf.lines[line].data) == 0 && line != v.Cursor.Y {
520                         v.Cursor.X = 0
521                         v.Cursor.Y = line
522                         break
523                 }
524         }
525         // If no empty line found. move cursor to end of buffer
526         if line == 0 {
527                 v.Cursor.Loc = v.Buf.Start()
528         }
529
530         if usePlugin {
531                 return PostActionCall("ParagraphPrevious", v)
532         }
533         return true
534 }
535
536 // ParagraphNext moves the cursor to the next empty line, or end of the buffer if there's none
537 func (v *View) ParagraphNext(usePlugin bool) bool {
538         if usePlugin && !PreActionCall("ParagraphNext", v) {
539                 return false
540         }
541
542         var line int
543         for line = v.Cursor.Y; line < len(v.Buf.lines); line++ {
544                 if len(v.Buf.lines[line].data) == 0 && line != v.Cursor.Y {
545                         v.Cursor.X = 0
546                         v.Cursor.Y = line
547                         break
548                 }
549         }
550         // If no empty line found. move cursor to end of buffer
551         if line == len(v.Buf.lines) {
552                 v.Cursor.Loc = v.Buf.End()
553         }
554
555         if usePlugin {
556                 return PostActionCall("ParagraphNext", v)
557         }
558         return true
559 }
560
561 // Retab changes all tabs to spaces or all spaces to tabs depending
562 // on the user's settings
563 func (v *View) Retab(usePlugin bool) bool {
564         if usePlugin && !PreActionCall("Retab", v) {
565                 return false
566         }
567
568         toSpaces := v.Buf.Settings["tabstospaces"].(bool)
569         tabsize := int(v.Buf.Settings["tabsize"].(float64))
570         dirty := false
571
572         for i := 0; i < v.Buf.NumLines; i++ {
573                 l := v.Buf.Line(i)
574
575                 ws := GetLeadingWhitespace(l)
576                 if ws != "" {
577                         if toSpaces {
578                                 ws = strings.Replace(ws, "\t", Spaces(tabsize), -1)
579                         } else {
580                                 ws = strings.Replace(ws, Spaces(tabsize), "\t", -1)
581                         }
582                 }
583
584                 l = strings.TrimLeft(l, " \t")
585                 v.Buf.lines[i].data = []byte(ws + l)
586                 dirty = true
587         }
588
589         v.Buf.IsModified = dirty
590
591         if usePlugin {
592                 return PostActionCall("Retab", v)
593         }
594         return true
595 }
596
597 // CursorStart moves the cursor to the start of the buffer
598 func (v *View) CursorStart(usePlugin bool) bool {
599         if usePlugin && !PreActionCall("CursorStart", v) {
600                 return false
601         }
602
603         v.deselect(0)
604
605         v.Cursor.X = 0
606         v.Cursor.Y = 0
607
608         if usePlugin {
609                 return PostActionCall("CursorStart", v)
610         }
611         return true
612 }
613
614 // CursorEnd moves the cursor to the end of the buffer
615 func (v *View) CursorEnd(usePlugin bool) bool {
616         if usePlugin && !PreActionCall("CursorEnd", v) {
617                 return false
618         }
619
620         v.deselect(0)
621
622         v.Cursor.Loc = v.Buf.End()
623         v.Cursor.StoreVisualX()
624
625         if usePlugin {
626                 return PostActionCall("CursorEnd", v)
627         }
628         return true
629 }
630
631 // SelectToStart selects the text from the cursor to the start of the buffer
632 func (v *View) SelectToStart(usePlugin bool) bool {
633         if usePlugin && !PreActionCall("SelectToStart", v) {
634                 return false
635         }
636
637         if !v.Cursor.HasSelection() {
638                 v.Cursor.OrigSelection[0] = v.Cursor.Loc
639         }
640         v.CursorStart(false)
641         v.Cursor.SelectTo(v.Buf.Start())
642
643         if usePlugin {
644                 return PostActionCall("SelectToStart", v)
645         }
646         return true
647 }
648
649 // SelectToEnd selects the text from the cursor to the end of the buffer
650 func (v *View) SelectToEnd(usePlugin bool) bool {
651         if usePlugin && !PreActionCall("SelectToEnd", v) {
652                 return false
653         }
654
655         if !v.Cursor.HasSelection() {
656                 v.Cursor.OrigSelection[0] = v.Cursor.Loc
657         }
658         v.CursorEnd(false)
659         v.Cursor.SelectTo(v.Buf.End())
660
661         if usePlugin {
662                 return PostActionCall("SelectToEnd", v)
663         }
664         return true
665 }
666
667 // InsertSpace inserts a space
668 func (v *View) InsertSpace(usePlugin bool) bool {
669         if usePlugin && !PreActionCall("InsertSpace", v) {
670                 return false
671         }
672
673         if v.Cursor.HasSelection() {
674                 v.Cursor.DeleteSelection()
675                 v.Cursor.ResetSelection()
676         }
677         v.Buf.Insert(v.Cursor.Loc, " ")
678         // v.Cursor.Right()
679
680         if usePlugin {
681                 return PostActionCall("InsertSpace", v)
682         }
683         return true
684 }
685
686 // InsertNewline inserts a newline plus possible some whitespace if autoindent is on
687 func (v *View) InsertNewline(usePlugin bool) bool {
688         if usePlugin && !PreActionCall("InsertNewline", v) {
689                 return false
690         }
691
692         // Insert a newline
693         if v.Cursor.HasSelection() {
694                 v.Cursor.DeleteSelection()
695                 v.Cursor.ResetSelection()
696         }
697
698         ws := GetLeadingWhitespace(v.Buf.Line(v.Cursor.Y))
699         cx := v.Cursor.X
700         v.Buf.Insert(v.Cursor.Loc, "\n")
701         // v.Cursor.Right()
702
703         if v.Buf.Settings["autoindent"].(bool) {
704                 if cx < len(ws) {
705                         ws = ws[0:cx]
706                 }
707                 v.Buf.Insert(v.Cursor.Loc, ws)
708                 // for i := 0; i < len(ws); i++ {
709                 //      v.Cursor.Right()
710                 // }
711
712                 // Remove the whitespaces if keepautoindent setting is off
713                 if IsSpacesOrTabs(v.Buf.Line(v.Cursor.Y-1)) && !v.Buf.Settings["keepautoindent"].(bool) {
714                         line := v.Buf.Line(v.Cursor.Y - 1)
715                         v.Buf.Remove(Loc{0, v.Cursor.Y - 1}, Loc{Count(line), v.Cursor.Y - 1})
716                 }
717         }
718         v.Cursor.LastVisualX = v.Cursor.GetVisualX()
719
720         if usePlugin {
721                 return PostActionCall("InsertNewline", v)
722         }
723         return true
724 }
725
726 // Backspace deletes the previous character
727 func (v *View) Backspace(usePlugin bool) bool {
728         if usePlugin && !PreActionCall("Backspace", v) {
729                 return false
730         }
731
732         // Delete a character
733         if v.Cursor.HasSelection() {
734                 v.Cursor.DeleteSelection()
735                 v.Cursor.ResetSelection()
736         } else if v.Cursor.Loc.GreaterThan(v.Buf.Start()) {
737                 // We have to do something a bit hacky here because we want to
738                 // delete the line by first moving left and then deleting backwards
739                 // but the undo redo would place the cursor in the wrong place
740                 // So instead we move left, save the position, move back, delete
741                 // and restore the position
742
743                 // If the user is using spaces instead of tabs and they are deleting
744                 // whitespace at the start of the line, we should delete as if it's a
745                 // tab (tabSize number of spaces)
746                 lineStart := sliceEnd(v.Buf.LineBytes(v.Cursor.Y), v.Cursor.X)
747                 tabSize := int(v.Buf.Settings["tabsize"].(float64))
748                 if v.Buf.Settings["tabstospaces"].(bool) && IsSpaces(lineStart) && utf8.RuneCount(lineStart) != 0 && utf8.RuneCount(lineStart)%tabSize == 0 {
749                         loc := v.Cursor.Loc
750                         v.Buf.Remove(loc.Move(-tabSize, v.Buf), loc)
751                 } else {
752                         loc := v.Cursor.Loc
753                         v.Buf.Remove(loc.Move(-1, v.Buf), loc)
754                 }
755         }
756         v.Cursor.LastVisualX = v.Cursor.GetVisualX()
757
758         if usePlugin {
759                 return PostActionCall("Backspace", v)
760         }
761         return true
762 }
763
764 // DeleteWordRight deletes the word to the right of the cursor
765 func (v *View) DeleteWordRight(usePlugin bool) bool {
766         if usePlugin && !PreActionCall("DeleteWordRight", v) {
767                 return false
768         }
769
770         v.SelectWordRight(false)
771         if v.Cursor.HasSelection() {
772                 v.Cursor.DeleteSelection()
773                 v.Cursor.ResetSelection()
774         }
775
776         if usePlugin {
777                 return PostActionCall("DeleteWordRight", v)
778         }
779         return true
780 }
781
782 // DeleteWordLeft deletes the word to the left of the cursor
783 func (v *View) DeleteWordLeft(usePlugin bool) bool {
784         if usePlugin && !PreActionCall("DeleteWordLeft", v) {
785                 return false
786         }
787
788         v.SelectWordLeft(false)
789         if v.Cursor.HasSelection() {
790                 v.Cursor.DeleteSelection()
791                 v.Cursor.ResetSelection()
792         }
793
794         if usePlugin {
795                 return PostActionCall("DeleteWordLeft", v)
796         }
797         return true
798 }
799
800 // Delete deletes the next character
801 func (v *View) Delete(usePlugin bool) bool {
802         if usePlugin && !PreActionCall("Delete", v) {
803                 return false
804         }
805
806         if v.Cursor.HasSelection() {
807                 v.Cursor.DeleteSelection()
808                 v.Cursor.ResetSelection()
809         } else {
810                 loc := v.Cursor.Loc
811                 if loc.LessThan(v.Buf.End()) {
812                         v.Buf.Remove(loc, loc.Move(1, v.Buf))
813                 }
814         }
815
816         if usePlugin {
817                 return PostActionCall("Delete", v)
818         }
819         return true
820 }
821
822 // IndentSelection indents the current selection
823 func (v *View) IndentSelection(usePlugin bool) bool {
824         if usePlugin && !PreActionCall("IndentSelection", v) {
825                 return false
826         }
827
828         if v.Cursor.HasSelection() {
829                 start := v.Cursor.CurSelection[0]
830                 end := v.Cursor.CurSelection[1]
831                 if end.Y < start.Y {
832                         start, end = end, start
833                         v.Cursor.SetSelectionStart(start)
834                         v.Cursor.SetSelectionEnd(end)
835                 }
836
837                 startY := start.Y
838                 endY := end.Move(-1, v.Buf).Y
839                 endX := end.Move(-1, v.Buf).X
840                 tabsize := len(v.Buf.IndentString())
841                 for y := startY; y <= endY; y++ {
842                         v.Buf.Insert(Loc{0, y}, v.Buf.IndentString())
843                         if y == startY && start.X > 0 {
844                                 v.Cursor.SetSelectionStart(start.Move(tabsize, v.Buf))
845                         }
846                         if y == endY {
847                                 v.Cursor.SetSelectionEnd(Loc{endX + tabsize + 1, endY})
848                         }
849                 }
850                 v.Cursor.Relocate()
851
852                 if usePlugin {
853                         return PostActionCall("IndentSelection", v)
854                 }
855                 return true
856         }
857         return false
858 }
859
860 // OutdentLine moves the current line back one indentation
861 func (v *View) OutdentLine(usePlugin bool) bool {
862         if usePlugin && !PreActionCall("OutdentLine", v) {
863                 return false
864         }
865
866         if v.Cursor.HasSelection() {
867                 return false
868         }
869
870         for x := 0; x < len(v.Buf.IndentString()); x++ {
871                 if len(GetLeadingWhitespace(v.Buf.Line(v.Cursor.Y))) == 0 {
872                         break
873                 }
874                 v.Buf.Remove(Loc{0, v.Cursor.Y}, Loc{1, v.Cursor.Y})
875         }
876         v.Cursor.Relocate()
877
878         if usePlugin {
879                 return PostActionCall("OutdentLine", v)
880         }
881         return true
882 }
883
884 // OutdentSelection takes the current selection and moves it back one indent level
885 func (v *View) OutdentSelection(usePlugin bool) bool {
886         if usePlugin && !PreActionCall("OutdentSelection", v) {
887                 return false
888         }
889
890         if v.Cursor.HasSelection() {
891                 start := v.Cursor.CurSelection[0]
892                 end := v.Cursor.CurSelection[1]
893                 if end.Y < start.Y {
894                         start, end = end, start
895                         v.Cursor.SetSelectionStart(start)
896                         v.Cursor.SetSelectionEnd(end)
897                 }
898
899                 startY := start.Y
900                 endY := end.Move(-1, v.Buf).Y
901                 for y := startY; y <= endY; y++ {
902                         for x := 0; x < len(v.Buf.IndentString()); x++ {
903                                 if len(GetLeadingWhitespace(v.Buf.Line(y))) == 0 {
904                                         break
905                                 }
906                                 v.Buf.Remove(Loc{0, y}, Loc{1, y})
907                         }
908                 }
909                 v.Cursor.Relocate()
910
911                 if usePlugin {
912                         return PostActionCall("OutdentSelection", v)
913                 }
914                 return true
915         }
916         return false
917 }
918
919 // InsertTab inserts a tab or spaces
920 func (v *View) InsertTab(usePlugin bool) bool {
921         if usePlugin && !PreActionCall("InsertTab", v) {
922                 return false
923         }
924
925         if v.Cursor.HasSelection() {
926                 return false
927         }
928
929         tabBytes := len(v.Buf.IndentString())
930         bytesUntilIndent := tabBytes - (v.Cursor.GetVisualX() % tabBytes)
931         v.Buf.Insert(v.Cursor.Loc, v.Buf.IndentString()[:bytesUntilIndent])
932         // for i := 0; i < bytesUntilIndent; i++ {
933         //      v.Cursor.Right()
934         // }
935
936         if usePlugin {
937                 return PostActionCall("InsertTab", v)
938         }
939         return true
940 }
941
942 // SaveAll saves all open buffers
943 func (v *View) SaveAll(usePlugin bool) bool {
944         if v.mainCursor() {
945                 if usePlugin && !PreActionCall("SaveAll", v) {
946                         return false
947                 }
948
949                 for _, t := range tabs {
950                         for _, v := range t.views {
951                                 v.Save(false)
952                         }
953                 }
954
955                 if usePlugin {
956                         return PostActionCall("SaveAll", v)
957                 }
958         }
959         return false
960 }
961
962 // Save the buffer to disk
963 func (v *View) Save(usePlugin bool) bool {
964         if v.mainCursor() {
965                 if usePlugin && !PreActionCall("Save", v) {
966                         return false
967                 }
968
969                 if v.Type.Scratch == true {
970                         // We can't save any view type with scratch set. eg help and log text
971                         return false
972                 }
973                 // If this is an empty buffer, ask for a filename
974                 if v.Buf.Path == "" {
975                         v.SaveAs(false)
976                 } else {
977                         v.saveToFile(v.Buf.Path)
978                 }
979
980                 if usePlugin {
981                         return PostActionCall("Save", v)
982                 }
983         }
984         return false
985 }
986
987 // This function saves the buffer to `filename` and changes the buffer's path and name
988 // to `filename` if the save is successful
989 func (v *View) saveToFile(filename string) {
990         err := v.Buf.SaveAs(filename)
991         if err != nil {
992                 if strings.HasSuffix(err.Error(), "permission denied") {
993                         choice, _ := messenger.YesNoPrompt("Permission denied. Do you want to save this file using sudo? (y,n)")
994                         if choice {
995                                 err = v.Buf.SaveAsWithSudo(filename)
996                                 if err != nil {
997                                         messenger.Error(err.Error())
998                                 } else {
999                                         v.Buf.Path = filename
1000                                         v.Buf.name = filename
1001                                         messenger.Message("Saved " + filename)
1002                                 }
1003                         }
1004                         messenger.Reset()
1005                         messenger.Clear()
1006                 } else {
1007                         messenger.Error(err.Error())
1008                 }
1009         } else {
1010                 v.Buf.Path = filename
1011                 v.Buf.name = filename
1012                 messenger.Message("Saved " + filename)
1013         }
1014 }
1015
1016 // SaveAs saves the buffer to disk with the given name
1017 func (v *View) SaveAs(usePlugin bool) bool {
1018         if v.mainCursor() {
1019                 if usePlugin && !PreActionCall("SaveAs", v) {
1020                         return false
1021                 }
1022
1023                 filename, canceled := messenger.Prompt("Filename: ", "", "Save", NoCompletion)
1024                 if !canceled {
1025                         // the filename might or might not be quoted, so unquote first then join the strings.
1026                         args, err := shellwords.Split(filename)
1027                         filename = strings.Join(args, " ")
1028                         if err != nil {
1029                                 messenger.Error("Error parsing arguments: ", err)
1030                                 return false
1031                         }
1032                         v.saveToFile(filename)
1033                 }
1034
1035                 if usePlugin {
1036                         PostActionCall("SaveAs", v)
1037                 }
1038         }
1039         return false
1040 }
1041
1042 // Find opens a prompt and searches forward for the input
1043 func (v *View) Find(usePlugin bool) bool {
1044         if v.mainCursor() {
1045                 if usePlugin && !PreActionCall("Find", v) {
1046                         return false
1047                 }
1048
1049                 searchStr := ""
1050                 if v.Cursor.HasSelection() {
1051                         searchStart = v.Cursor.CurSelection[1]
1052                         searchStart = v.Cursor.CurSelection[1]
1053                         searchStr = v.Cursor.GetSelection()
1054                 } else {
1055                         searchStart = v.Cursor.Loc
1056                 }
1057                 BeginSearch(searchStr)
1058
1059                 if usePlugin {
1060                         return PostActionCall("Find", v)
1061                 }
1062         }
1063         return true
1064 }
1065
1066 // FindNext searches forwards for the last used search term
1067 func (v *View) FindNext(usePlugin bool) bool {
1068         if usePlugin && !PreActionCall("FindNext", v) {
1069                 return false
1070         }
1071
1072         if v.Cursor.HasSelection() {
1073                 searchStart = v.Cursor.CurSelection[1]
1074                 // lastSearch = v.Cursor.GetSelection()
1075         } else {
1076                 searchStart = v.Cursor.Loc
1077         }
1078         if lastSearch == "" {
1079                 return true
1080         }
1081         messenger.Message("Finding: " + lastSearch)
1082         Search(lastSearch, v, true)
1083
1084         if usePlugin {
1085                 return PostActionCall("FindNext", v)
1086         }
1087         return true
1088 }
1089
1090 // FindPrevious searches backwards for the last used search term
1091 func (v *View) FindPrevious(usePlugin bool) bool {
1092         if usePlugin && !PreActionCall("FindPrevious", v) {
1093                 return false
1094         }
1095
1096         if v.Cursor.HasSelection() {
1097                 searchStart = v.Cursor.CurSelection[0]
1098         } else {
1099                 searchStart = v.Cursor.Loc
1100         }
1101         messenger.Message("Finding: " + lastSearch)
1102         Search(lastSearch, v, false)
1103
1104         if usePlugin {
1105                 return PostActionCall("FindPrevious", v)
1106         }
1107         return true
1108 }
1109
1110 // Undo undoes the last action
1111 func (v *View) Undo(usePlugin bool) bool {
1112         if usePlugin && !PreActionCall("Undo", v) {
1113                 return false
1114         }
1115
1116         if v.Buf.curCursor == 0 {
1117                 v.Buf.clearCursors()
1118         }
1119
1120         v.Buf.Undo()
1121         messenger.Message("Undid action")
1122
1123         if usePlugin {
1124                 return PostActionCall("Undo", v)
1125         }
1126         return true
1127 }
1128
1129 // Redo redoes the last action
1130 func (v *View) Redo(usePlugin bool) bool {
1131         if usePlugin && !PreActionCall("Redo", v) {
1132                 return false
1133         }
1134
1135         if v.Buf.curCursor == 0 {
1136                 v.Buf.clearCursors()
1137         }
1138
1139         v.Buf.Redo()
1140         messenger.Message("Redid action")
1141
1142         if usePlugin {
1143                 return PostActionCall("Redo", v)
1144         }
1145         return true
1146 }
1147
1148 // Copy the selection to the system clipboard
1149 func (v *View) Copy(usePlugin bool) bool {
1150         if v.mainCursor() {
1151                 if usePlugin && !PreActionCall("Copy", v) {
1152                         return false
1153                 }
1154
1155                 if v.Cursor.HasSelection() {
1156                         v.Cursor.CopySelection("clipboard")
1157                         v.freshClip = true
1158                         messenger.Message("Copied selection")
1159                 }
1160
1161                 if usePlugin {
1162                         return PostActionCall("Copy", v)
1163                 }
1164         }
1165         return true
1166 }
1167
1168 // CutLine cuts the current line to the clipboard
1169 func (v *View) CutLine(usePlugin bool) bool {
1170         if usePlugin && !PreActionCall("CutLine", v) {
1171                 return false
1172         }
1173
1174         v.Cursor.SelectLine()
1175         if !v.Cursor.HasSelection() {
1176                 return false
1177         }
1178         if v.freshClip == true {
1179                 if v.Cursor.HasSelection() {
1180                         if clip, err := clipboard.ReadAll("clipboard"); err != nil {
1181                                 messenger.Error(err)
1182                         } else {
1183                                 clipboard.WriteAll(clip+v.Cursor.GetSelection(), "clipboard")
1184                         }
1185                 }
1186         } else if time.Since(v.lastCutTime)/time.Second > 10*time.Second || v.freshClip == false {
1187                 v.Copy(true)
1188         }
1189         v.freshClip = true
1190         v.lastCutTime = time.Now()
1191         v.Cursor.DeleteSelection()
1192         v.Cursor.ResetSelection()
1193         messenger.Message("Cut line")
1194
1195         if usePlugin {
1196                 return PostActionCall("CutLine", v)
1197         }
1198         return true
1199 }
1200
1201 // Cut the selection to the system clipboard
1202 func (v *View) Cut(usePlugin bool) bool {
1203         if usePlugin && !PreActionCall("Cut", v) {
1204                 return false
1205         }
1206
1207         if v.Cursor.HasSelection() {
1208                 v.Cursor.CopySelection("clipboard")
1209                 v.Cursor.DeleteSelection()
1210                 v.Cursor.ResetSelection()
1211                 v.freshClip = true
1212                 messenger.Message("Cut selection")
1213
1214                 if usePlugin {
1215                         return PostActionCall("Cut", v)
1216                 }
1217                 return true
1218         }
1219
1220         return false
1221 }
1222
1223 // DuplicateLine duplicates the current line or selection
1224 func (v *View) DuplicateLine(usePlugin bool) bool {
1225         if usePlugin && !PreActionCall("DuplicateLine", v) {
1226                 return false
1227         }
1228
1229         if v.Cursor.HasSelection() {
1230                 v.Buf.Insert(v.Cursor.CurSelection[1], v.Cursor.GetSelection())
1231         } else {
1232                 v.Cursor.End()
1233                 v.Buf.Insert(v.Cursor.Loc, "\n"+v.Buf.Line(v.Cursor.Y))
1234                 // v.Cursor.Right()
1235         }
1236
1237         messenger.Message("Duplicated line")
1238
1239         if usePlugin {
1240                 return PostActionCall("DuplicateLine", v)
1241         }
1242         return true
1243 }
1244
1245 // DeleteLine deletes the current line
1246 func (v *View) DeleteLine(usePlugin bool) bool {
1247         if usePlugin && !PreActionCall("DeleteLine", v) {
1248                 return false
1249         }
1250
1251         v.Cursor.SelectLine()
1252         if !v.Cursor.HasSelection() {
1253                 return false
1254         }
1255         v.Cursor.DeleteSelection()
1256         v.Cursor.ResetSelection()
1257         messenger.Message("Deleted line")
1258
1259         if usePlugin {
1260                 return PostActionCall("DeleteLine", v)
1261         }
1262         return true
1263 }
1264
1265 // MoveLinesUp moves up the current line or selected lines if any
1266 func (v *View) MoveLinesUp(usePlugin bool) bool {
1267         if usePlugin && !PreActionCall("MoveLinesUp", v) {
1268                 return false
1269         }
1270
1271         if v.Cursor.HasSelection() {
1272                 if v.Cursor.CurSelection[0].Y == 0 {
1273                         messenger.Message("Can not move further up")
1274                         return true
1275                 }
1276                 start := v.Cursor.CurSelection[0].Y
1277                 end := v.Cursor.CurSelection[1].Y
1278                 if start > end {
1279                         end, start = start, end
1280                 }
1281
1282                 v.Buf.MoveLinesUp(
1283                         start,
1284                         end,
1285                 )
1286                 v.Cursor.CurSelection[1].Y -= 1
1287                 messenger.Message("Moved up selected line(s)")
1288         } else {
1289                 if v.Cursor.Loc.Y == 0 {
1290                         messenger.Message("Can not move further up")
1291                         return true
1292                 }
1293                 v.Buf.MoveLinesUp(
1294                         v.Cursor.Loc.Y,
1295                         v.Cursor.Loc.Y+1,
1296                 )
1297                 messenger.Message("Moved up current line")
1298         }
1299         v.Buf.IsModified = true
1300
1301         if usePlugin {
1302                 return PostActionCall("MoveLinesUp", v)
1303         }
1304         return true
1305 }
1306
1307 // MoveLinesDown moves down the current line or selected lines if any
1308 func (v *View) MoveLinesDown(usePlugin bool) bool {
1309         if usePlugin && !PreActionCall("MoveLinesDown", v) {
1310                 return false
1311         }
1312
1313         if v.Cursor.HasSelection() {
1314                 if v.Cursor.CurSelection[1].Y >= len(v.Buf.lines) {
1315                         messenger.Message("Can not move further down")
1316                         return true
1317                 }
1318                 start := v.Cursor.CurSelection[0].Y
1319                 end := v.Cursor.CurSelection[1].Y
1320                 if start > end {
1321                         end, start = start, end
1322                 }
1323
1324                 v.Buf.MoveLinesDown(
1325                         start,
1326                         end,
1327                 )
1328                 messenger.Message("Moved down selected line(s)")
1329         } else {
1330                 if v.Cursor.Loc.Y >= len(v.Buf.lines)-1 {
1331                         messenger.Message("Can not move further down")
1332                         return true
1333                 }
1334                 v.Buf.MoveLinesDown(
1335                         v.Cursor.Loc.Y,
1336                         v.Cursor.Loc.Y+1,
1337                 )
1338                 messenger.Message("Moved down current line")
1339         }
1340         v.Buf.IsModified = true
1341
1342         if usePlugin {
1343                 return PostActionCall("MoveLinesDown", v)
1344         }
1345         return true
1346 }
1347
1348 // Paste whatever is in the system clipboard into the buffer
1349 // Delete and paste if the user has a selection
1350 func (v *View) Paste(usePlugin bool) bool {
1351         if usePlugin && !PreActionCall("Paste", v) {
1352                 return false
1353         }
1354
1355         clip, _ := clipboard.ReadAll("clipboard")
1356         v.paste(clip)
1357
1358         if usePlugin {
1359                 return PostActionCall("Paste", v)
1360         }
1361         return true
1362 }
1363
1364 // PastePrimary pastes from the primary clipboard (only use on linux)
1365 func (v *View) PastePrimary(usePlugin bool) bool {
1366         if usePlugin && !PreActionCall("Paste", v) {
1367                 return false
1368         }
1369
1370         clip, _ := clipboard.ReadAll("primary")
1371         v.paste(clip)
1372
1373         if usePlugin {
1374                 return PostActionCall("Paste", v)
1375         }
1376         return true
1377 }
1378
1379 // JumpToMatchingBrace moves the cursor to the matching brace if it is
1380 // currently on a brace
1381 func (v *View) JumpToMatchingBrace(usePlugin bool) bool {
1382         if usePlugin && !PreActionCall("JumpToMatchingBrace", v) {
1383                 return false
1384         }
1385
1386         for _, bp := range bracePairs {
1387                 r := v.Cursor.RuneUnder(v.Cursor.X)
1388                 if r == bp[0] || r == bp[1] {
1389                         matchingBrace := v.Buf.FindMatchingBrace(bp, v.Cursor.Loc)
1390                         v.Cursor.GotoLoc(matchingBrace)
1391                 }
1392         }
1393
1394         if usePlugin {
1395                 return PostActionCall("JumpToMatchingBrace", v)
1396         }
1397         return true
1398 }
1399
1400 // SelectAll selects the entire buffer
1401 func (v *View) SelectAll(usePlugin bool) bool {
1402         if usePlugin && !PreActionCall("SelectAll", v) {
1403                 return false
1404         }
1405
1406         v.Cursor.SetSelectionStart(v.Buf.Start())
1407         v.Cursor.SetSelectionEnd(v.Buf.End())
1408         // Put the cursor at the beginning
1409         v.Cursor.X = 0
1410         v.Cursor.Y = 0
1411
1412         if usePlugin {
1413                 return PostActionCall("SelectAll", v)
1414         }
1415         return true
1416 }
1417
1418 // OpenFile opens a new file in the buffer
1419 func (v *View) OpenFile(usePlugin bool) bool {
1420         if v.mainCursor() {
1421                 if usePlugin && !PreActionCall("OpenFile", v) {
1422                         return false
1423                 }
1424
1425                 if v.CanClose() {
1426                         input, canceled := messenger.Prompt("> ", "open ", "Open", CommandCompletion)
1427                         if !canceled {
1428                                 HandleCommand(input)
1429                                 if usePlugin {
1430                                         return PostActionCall("OpenFile", v)
1431                                 }
1432                         }
1433                 }
1434         }
1435         return false
1436 }
1437
1438 // Start moves the viewport to the start of the buffer
1439 func (v *View) Start(usePlugin bool) bool {
1440         if v.mainCursor() {
1441                 if usePlugin && !PreActionCall("Start", v) {
1442                         return false
1443                 }
1444
1445                 v.Topline = 0
1446
1447                 if usePlugin {
1448                         return PostActionCall("Start", v)
1449                 }
1450         }
1451         return false
1452 }
1453
1454 // End moves the viewport to the end of the buffer
1455 func (v *View) End(usePlugin bool) bool {
1456         if v.mainCursor() {
1457                 if usePlugin && !PreActionCall("End", v) {
1458                         return false
1459                 }
1460
1461                 if v.Height > v.Buf.NumLines {
1462                         v.Topline = 0
1463                 } else {
1464                         v.Topline = v.Buf.NumLines - v.Height
1465                 }
1466
1467                 if usePlugin {
1468                         return PostActionCall("End", v)
1469                 }
1470         }
1471         return false
1472 }
1473
1474 // PageUp scrolls the view up a page
1475 func (v *View) PageUp(usePlugin bool) bool {
1476         if v.mainCursor() {
1477                 if usePlugin && !PreActionCall("PageUp", v) {
1478                         return false
1479                 }
1480
1481                 if v.Topline > v.Height {
1482                         v.ScrollUp(v.Height)
1483                 } else {
1484                         v.Topline = 0
1485                 }
1486
1487                 if usePlugin {
1488                         return PostActionCall("PageUp", v)
1489                 }
1490         }
1491         return false
1492 }
1493
1494 // PageDown scrolls the view down a page
1495 func (v *View) PageDown(usePlugin bool) bool {
1496         if v.mainCursor() {
1497                 if usePlugin && !PreActionCall("PageDown", v) {
1498                         return false
1499                 }
1500
1501                 if v.Buf.NumLines-(v.Topline+v.Height) > v.Height {
1502                         v.ScrollDown(v.Height)
1503                 } else if v.Buf.NumLines >= v.Height {
1504                         v.Topline = v.Buf.NumLines - v.Height
1505                 }
1506
1507                 if usePlugin {
1508                         return PostActionCall("PageDown", v)
1509                 }
1510         }
1511         return false
1512 }
1513
1514 // CursorPageUp places the cursor a page up
1515 func (v *View) CursorPageUp(usePlugin bool) bool {
1516         if usePlugin && !PreActionCall("CursorPageUp", v) {
1517                 return false
1518         }
1519
1520         v.deselect(0)
1521
1522         if v.Cursor.HasSelection() {
1523                 v.Cursor.Loc = v.Cursor.CurSelection[0]
1524                 v.Cursor.ResetSelection()
1525                 v.Cursor.StoreVisualX()
1526         }
1527         v.Cursor.UpN(v.Height)
1528
1529         if usePlugin {
1530                 return PostActionCall("CursorPageUp", v)
1531         }
1532         return true
1533 }
1534
1535 // CursorPageDown places the cursor a page up
1536 func (v *View) CursorPageDown(usePlugin bool) bool {
1537         if usePlugin && !PreActionCall("CursorPageDown", v) {
1538                 return false
1539         }
1540
1541         v.deselect(0)
1542
1543         if v.Cursor.HasSelection() {
1544                 v.Cursor.Loc = v.Cursor.CurSelection[1]
1545                 v.Cursor.ResetSelection()
1546                 v.Cursor.StoreVisualX()
1547         }
1548         v.Cursor.DownN(v.Height)
1549
1550         if usePlugin {
1551                 return PostActionCall("CursorPageDown", v)
1552         }
1553         return true
1554 }
1555
1556 // HalfPageUp scrolls the view up half a page
1557 func (v *View) HalfPageUp(usePlugin bool) bool {
1558         if v.mainCursor() {
1559                 if usePlugin && !PreActionCall("HalfPageUp", v) {
1560                         return false
1561                 }
1562
1563                 if v.Topline > v.Height/2 {
1564                         v.ScrollUp(v.Height / 2)
1565                 } else {
1566                         v.Topline = 0
1567                 }
1568
1569                 if usePlugin {
1570                         return PostActionCall("HalfPageUp", v)
1571                 }
1572         }
1573         return false
1574 }
1575
1576 // HalfPageDown scrolls the view down half a page
1577 func (v *View) HalfPageDown(usePlugin bool) bool {
1578         if v.mainCursor() {
1579                 if usePlugin && !PreActionCall("HalfPageDown", v) {
1580                         return false
1581                 }
1582
1583                 if v.Buf.NumLines-(v.Topline+v.Height) > v.Height/2 {
1584                         v.ScrollDown(v.Height / 2)
1585                 } else {
1586                         if v.Buf.NumLines >= v.Height {
1587                                 v.Topline = v.Buf.NumLines - v.Height
1588                         }
1589                 }
1590
1591                 if usePlugin {
1592                         return PostActionCall("HalfPageDown", v)
1593                 }
1594         }
1595         return false
1596 }
1597
1598 // ToggleRuler turns line numbers off and on
1599 func (v *View) ToggleRuler(usePlugin bool) bool {
1600         if v.mainCursor() {
1601                 if usePlugin && !PreActionCall("ToggleRuler", v) {
1602                         return false
1603                 }
1604
1605                 if v.Buf.Settings["ruler"] == false {
1606                         v.Buf.Settings["ruler"] = true
1607                         messenger.Message("Enabled ruler")
1608                 } else {
1609                         v.Buf.Settings["ruler"] = false
1610                         messenger.Message("Disabled ruler")
1611                 }
1612
1613                 if usePlugin {
1614                         return PostActionCall("ToggleRuler", v)
1615                 }
1616         }
1617         return false
1618 }
1619
1620 // JumpLine jumps to a line and moves the view accordingly.
1621 func (v *View) JumpLine(usePlugin bool) bool {
1622         if usePlugin && !PreActionCall("JumpLine", v) {
1623                 return false
1624         }
1625
1626         // Prompt for line number
1627         message := fmt.Sprintf("Jump to line:col (1 - %v) # ", v.Buf.NumLines)
1628         input, canceled := messenger.Prompt(message, "", "LineNumber", NoCompletion)
1629         if canceled {
1630                 return false
1631         }
1632         var lineInt int
1633         var colInt int
1634         var err error
1635         if strings.Contains(input, ":") {
1636                 split := strings.Split(input, ":")
1637                 lineInt, err = strconv.Atoi(split[0])
1638                 if err != nil {
1639                         messenger.Message("Invalid line number")
1640                         return false
1641                 }
1642                 colInt, err = strconv.Atoi(split[1])
1643                 if err != nil {
1644                         messenger.Message("Invalid column number")
1645                         return false
1646                 }
1647         } else {
1648                 lineInt, err = strconv.Atoi(input)
1649                 if err != nil {
1650                         messenger.Message("Invalid line number")
1651                         return false
1652                 }
1653         }
1654         lineInt--
1655         // Move cursor and view if possible.
1656         if lineInt < v.Buf.NumLines && lineInt >= 0 {
1657                 v.Cursor.X = colInt
1658                 v.Cursor.Y = lineInt
1659
1660                 if usePlugin {
1661                         return PostActionCall("JumpLine", v)
1662                 }
1663                 return true
1664         }
1665         messenger.Error("Only ", v.Buf.NumLines, " lines to jump")
1666         return false
1667 }
1668
1669 // ClearStatus clears the messenger bar
1670 func (v *View) ClearStatus(usePlugin bool) bool {
1671         if v.mainCursor() {
1672                 if usePlugin && !PreActionCall("ClearStatus", v) {
1673                         return false
1674                 }
1675
1676                 messenger.Message("")
1677
1678                 if usePlugin {
1679                         return PostActionCall("ClearStatus", v)
1680                 }
1681         }
1682         return false
1683 }
1684
1685 // ToggleHelp toggles the help screen
1686 func (v *View) ToggleHelp(usePlugin bool) bool {
1687         if v.mainCursor() {
1688                 if usePlugin && !PreActionCall("ToggleHelp", v) {
1689                         return false
1690                 }
1691
1692                 if v.Type != vtHelp {
1693                         // Open the default help
1694                         v.openHelp("help")
1695                 } else {
1696                         v.Quit(true)
1697                 }
1698
1699                 if usePlugin {
1700                         return PostActionCall("ToggleHelp", v)
1701                 }
1702         }
1703         return true
1704 }
1705
1706 // ToggleKeyMenu toggles the keymenu option and resizes all tabs
1707 func (v *View) ToggleKeyMenu(usePlugin bool) bool {
1708         if v.mainCursor() {
1709                 if usePlugin && !PreActionCall("ToggleBindings", v) {
1710                         return false
1711                 }
1712
1713                 globalSettings["keymenu"] = !globalSettings["keymenu"].(bool)
1714                 for _, tab := range tabs {
1715                         tab.Resize()
1716                 }
1717
1718                 if usePlugin {
1719                         return PostActionCall("ToggleBindings", v)
1720                 }
1721         }
1722         return true
1723 }
1724
1725 // ShellMode opens a terminal to run a shell command
1726 func (v *View) ShellMode(usePlugin bool) bool {
1727         if v.mainCursor() {
1728                 if usePlugin && !PreActionCall("ShellMode", v) {
1729                         return false
1730                 }
1731
1732                 input, canceled := messenger.Prompt("$ ", "", "Shell", NoCompletion)
1733                 if !canceled {
1734                         // The true here is for openTerm to make the command interactive
1735                         HandleShellCommand(input, true, true)
1736                         if usePlugin {
1737                                 return PostActionCall("ShellMode", v)
1738                         }
1739                 }
1740         }
1741         return false
1742 }
1743
1744 // CommandMode lets the user enter a command
1745 func (v *View) CommandMode(usePlugin bool) bool {
1746         if v.mainCursor() {
1747                 if usePlugin && !PreActionCall("CommandMode", v) {
1748                         return false
1749                 }
1750
1751                 input, canceled := messenger.Prompt("> ", "", "Command", CommandCompletion)
1752                 if !canceled {
1753                         HandleCommand(input)
1754                         if usePlugin {
1755                                 return PostActionCall("CommandMode", v)
1756                         }
1757                 }
1758         }
1759
1760         return false
1761 }
1762
1763 // ToggleOverwriteMode lets the user toggle the text overwrite mode
1764 func (v *View) ToggleOverwriteMode(usePlugin bool) bool {
1765         if v.mainCursor() {
1766                 if usePlugin && !PreActionCall("ToggleOverwriteMode", v) {
1767                         return false
1768                 }
1769
1770                 v.isOverwriteMode = !v.isOverwriteMode
1771
1772                 if usePlugin {
1773                         return PostActionCall("ToggleOverwriteMode", v)
1774                 }
1775         }
1776         return false
1777 }
1778
1779 // Escape leaves current mode
1780 func (v *View) Escape(usePlugin bool) bool {
1781         if v.mainCursor() {
1782                 // check if user is searching, or the last search is still active
1783                 if searching || lastSearch != "" {
1784                         ExitSearch(v)
1785                         return true
1786                 }
1787                 // check if a prompt is shown, hide it and don't quit
1788                 if messenger.hasPrompt {
1789                         messenger.Reset() // FIXME
1790                         return true
1791                 }
1792         }
1793
1794         return false
1795 }
1796
1797 // Quit this will close the current tab or view that is open
1798 func (v *View) Quit(usePlugin bool) bool {
1799         if v.mainCursor() {
1800                 if usePlugin && !PreActionCall("Quit", v) {
1801                         return false
1802                 }
1803
1804                 // Make sure not to quit if there are unsaved changes
1805                 if v.CanClose() {
1806                         v.CloseBuffer()
1807                         if len(tabs[curTab].views) > 1 {
1808                                 v.splitNode.Delete()
1809                                 tabs[v.TabNum].Cleanup()
1810                                 tabs[v.TabNum].Resize()
1811                         } else if len(tabs) > 1 {
1812                                 if len(tabs[v.TabNum].views) == 1 {
1813                                         tabs = tabs[:v.TabNum+copy(tabs[v.TabNum:], tabs[v.TabNum+1:])]
1814                                         for i, t := range tabs {
1815                                                 t.SetNum(i)
1816                                         }
1817                                         if curTab >= len(tabs) {
1818                                                 curTab--
1819                                         }
1820                                         if curTab == 0 {
1821                                                 CurView().ToggleTabbar()
1822                                         }
1823                                 }
1824                         } else {
1825                                 if usePlugin {
1826                                         PostActionCall("Quit", v)
1827                                 }
1828
1829                                 screen.Fini()
1830                                 messenger.SaveHistory()
1831                                 os.Exit(0)
1832                         }
1833                 }
1834
1835                 if usePlugin {
1836                         return PostActionCall("Quit", v)
1837                 }
1838         }
1839         return false
1840 }
1841
1842 // QuitAll quits the whole editor; all splits and tabs
1843 func (v *View) QuitAll(usePlugin bool) bool {
1844         if v.mainCursor() {
1845                 if usePlugin && !PreActionCall("QuitAll", v) {
1846                         return false
1847                 }
1848
1849                 closeAll := true
1850                 for _, tab := range tabs {
1851                         for _, v := range tab.views {
1852                                 if !v.CanClose() {
1853                                         closeAll = false
1854                                 }
1855                         }
1856                 }
1857
1858                 if closeAll {
1859                         // only quit if all of the buffers can be closed and the user confirms that they actually want to quit everything
1860                         shouldQuit, _ := messenger.YesNoPrompt("Do you want to quit micro (all open files will be closed)?")
1861
1862                         if shouldQuit {
1863                                 for _, tab := range tabs {
1864                                         for _, v := range tab.views {
1865                                                 v.CloseBuffer()
1866                                         }
1867                                 }
1868
1869                                 if usePlugin {
1870                                         PostActionCall("QuitAll", v)
1871                                 }
1872
1873                                 screen.Fini()
1874                                 messenger.SaveHistory()
1875                                 os.Exit(0)
1876                         }
1877                 }
1878         }
1879
1880         return false
1881 }
1882
1883 // AddTab adds a new tab with an empty buffer
1884 func (v *View) AddTab(usePlugin bool) bool {
1885         if v.mainCursor() {
1886                 if usePlugin && !PreActionCall("AddTab", v) {
1887                         return false
1888                 }
1889
1890                 tab := NewTabFromView(NewView(NewBufferFromString("", "")))
1891                 tab.SetNum(len(tabs))
1892                 tabs = append(tabs, tab)
1893                 curTab = len(tabs) - 1
1894                 if len(tabs) == 2 {
1895                         for _, t := range tabs {
1896                                 for _, v := range t.views {
1897                                         v.ToggleTabbar()
1898                                 }
1899                         }
1900                 }
1901
1902                 if usePlugin {
1903                         return PostActionCall("AddTab", v)
1904                 }
1905         }
1906         return true
1907 }
1908
1909 // PreviousTab switches to the previous tab in the tab list
1910 func (v *View) PreviousTab(usePlugin bool) bool {
1911         if v.mainCursor() {
1912                 if usePlugin && !PreActionCall("PreviousTab", v) {
1913                         return false
1914                 }
1915
1916                 if curTab > 0 {
1917                         curTab--
1918                 } else if curTab == 0 {
1919                         curTab = len(tabs) - 1
1920                 }
1921
1922                 if usePlugin {
1923                         return PostActionCall("PreviousTab", v)
1924                 }
1925         }
1926         return false
1927 }
1928
1929 // NextTab switches to the next tab in the tab list
1930 func (v *View) NextTab(usePlugin bool) bool {
1931         if v.mainCursor() {
1932                 if usePlugin && !PreActionCall("NextTab", v) {
1933                         return false
1934                 }
1935
1936                 if curTab < len(tabs)-1 {
1937                         curTab++
1938                 } else if curTab == len(tabs)-1 {
1939                         curTab = 0
1940                 }
1941
1942                 if usePlugin {
1943                         return PostActionCall("NextTab", v)
1944                 }
1945         }
1946         return false
1947 }
1948
1949 // VSplitBinding opens an empty vertical split
1950 func (v *View) VSplitBinding(usePlugin bool) bool {
1951         if v.mainCursor() {
1952                 if usePlugin && !PreActionCall("VSplit", v) {
1953                         return false
1954                 }
1955
1956                 v.VSplit(NewBufferFromString("", ""))
1957
1958                 if usePlugin {
1959                         return PostActionCall("VSplit", v)
1960                 }
1961         }
1962         return false
1963 }
1964
1965 // HSplitBinding opens an empty horizontal split
1966 func (v *View) HSplitBinding(usePlugin bool) bool {
1967         if v.mainCursor() {
1968                 if usePlugin && !PreActionCall("HSplit", v) {
1969                         return false
1970                 }
1971
1972                 v.HSplit(NewBufferFromString("", ""))
1973
1974                 if usePlugin {
1975                         return PostActionCall("HSplit", v)
1976                 }
1977         }
1978         return false
1979 }
1980
1981 // Unsplit closes all splits in the current tab except the active one
1982 func (v *View) Unsplit(usePlugin bool) bool {
1983         if v.mainCursor() {
1984                 if usePlugin && !PreActionCall("Unsplit", v) {
1985                         return false
1986                 }
1987
1988                 curView := tabs[curTab].CurView
1989                 for i := len(tabs[curTab].views) - 1; i >= 0; i-- {
1990                         view := tabs[curTab].views[i]
1991                         if view != nil && view.Num != curView {
1992                                 view.Quit(true)
1993                                 // messenger.Message("Quit ", view.Buf.Path)
1994                         }
1995                 }
1996
1997                 if usePlugin {
1998                         return PostActionCall("Unsplit", v)
1999                 }
2000         }
2001         return false
2002 }
2003
2004 // NextSplit changes the view to the next split
2005 func (v *View) NextSplit(usePlugin bool) bool {
2006         if v.mainCursor() {
2007                 if usePlugin && !PreActionCall("NextSplit", v) {
2008                         return false
2009                 }
2010
2011                 tab := tabs[curTab]
2012                 if tab.CurView < len(tab.views)-1 {
2013                         tab.CurView++
2014                 } else {
2015                         tab.CurView = 0
2016                 }
2017
2018                 if usePlugin {
2019                         return PostActionCall("NextSplit", v)
2020                 }
2021         }
2022         return false
2023 }
2024
2025 // PreviousSplit changes the view to the previous split
2026 func (v *View) PreviousSplit(usePlugin bool) bool {
2027         if v.mainCursor() {
2028                 if usePlugin && !PreActionCall("PreviousSplit", v) {
2029                         return false
2030                 }
2031
2032                 tab := tabs[curTab]
2033                 if tab.CurView > 0 {
2034                         tab.CurView--
2035                 } else {
2036                         tab.CurView = len(tab.views) - 1
2037                 }
2038
2039                 if usePlugin {
2040                         return PostActionCall("PreviousSplit", v)
2041                 }
2042         }
2043         return false
2044 }
2045
2046 var curMacro []interface{}
2047 var recordingMacro bool
2048
2049 // ToggleMacro toggles recording of a macro
2050 func (v *View) ToggleMacro(usePlugin bool) bool {
2051         if v.mainCursor() {
2052                 if usePlugin && !PreActionCall("ToggleMacro", v) {
2053                         return false
2054                 }
2055
2056                 recordingMacro = !recordingMacro
2057
2058                 if recordingMacro {
2059                         curMacro = []interface{}{}
2060                         messenger.Message("Recording")
2061                 } else {
2062                         messenger.Message("Stopped recording")
2063                 }
2064
2065                 if usePlugin {
2066                         return PostActionCall("ToggleMacro", v)
2067                 }
2068         }
2069         return true
2070 }
2071
2072 // PlayMacro plays back the most recently recorded macro
2073 func (v *View) PlayMacro(usePlugin bool) bool {
2074         if usePlugin && !PreActionCall("PlayMacro", v) {
2075                 return false
2076         }
2077
2078         for _, action := range curMacro {
2079                 switch t := action.(type) {
2080                 case rune:
2081                         // Insert a character
2082                         if v.Cursor.HasSelection() {
2083                                 v.Cursor.DeleteSelection()
2084                                 v.Cursor.ResetSelection()
2085                         }
2086                         v.Buf.Insert(v.Cursor.Loc, string(t))
2087                         // v.Cursor.Right()
2088
2089                         for pl := range loadedPlugins {
2090                                 _, err := Call(pl+".onRune", string(t), v)
2091                                 if err != nil && !strings.HasPrefix(err.Error(), "function does not exist") {
2092                                         TermMessage(err)
2093                                 }
2094                         }
2095                 case func(*View, bool) bool:
2096                         t(v, true)
2097                 }
2098         }
2099
2100         if usePlugin {
2101                 return PostActionCall("PlayMacro", v)
2102         }
2103         return true
2104 }
2105
2106 // SpawnMultiCursor creates a new multiple cursor at the next occurrence of the current selection or current word
2107 func (v *View) SpawnMultiCursor(usePlugin bool) bool {
2108         spawner := v.Buf.cursors[len(v.Buf.cursors)-1]
2109         // You can only spawn a cursor from the main cursor
2110         if v.Cursor == spawner {
2111                 if usePlugin && !PreActionCall("SpawnMultiCursor", v) {
2112                         return false
2113                 }
2114
2115                 if !spawner.HasSelection() {
2116                         spawner.SelectWord()
2117                 } else {
2118                         c := &Cursor{
2119                                 buf: v.Buf,
2120                         }
2121
2122                         sel := spawner.GetSelection()
2123
2124                         searchStart = spawner.CurSelection[1]
2125                         v.Cursor = c
2126                         Search(regexp.QuoteMeta(sel), v, true)
2127
2128                         for _, cur := range v.Buf.cursors {
2129                                 if c.Loc == cur.Loc {
2130                                         return false
2131                                 }
2132                         }
2133                         v.Buf.cursors = append(v.Buf.cursors, c)
2134                         v.Buf.UpdateCursors()
2135                         v.Relocate()
2136                         v.Cursor = spawner
2137                 }
2138
2139                 if usePlugin {
2140                         PostActionCall("SpawnMultiCursor", v)
2141                 }
2142         }
2143
2144         return false
2145 }
2146
2147 // MouseMultiCursor is a mouse action which puts a new cursor at the mouse position
2148 func (v *View) MouseMultiCursor(usePlugin bool, e *tcell.EventMouse) bool {
2149         if v.Cursor == &v.Buf.Cursor {
2150                 if usePlugin && !PreActionCall("SpawnMultiCursorAtMouse", v, e) {
2151                         return false
2152                 }
2153                 x, y := e.Position()
2154                 x -= v.lineNumOffset - v.leftCol + v.x
2155                 y += v.Topline - v.y
2156
2157                 c := &Cursor{
2158                         buf: v.Buf,
2159                 }
2160                 v.Cursor = c
2161                 v.MoveToMouseClick(x, y)
2162                 v.Relocate()
2163                 v.Cursor = &v.Buf.Cursor
2164
2165                 v.Buf.cursors = append(v.Buf.cursors, c)
2166                 v.Buf.MergeCursors()
2167                 v.Buf.UpdateCursors()
2168
2169                 if usePlugin {
2170                         PostActionCall("SpawnMultiCursorAtMouse", v)
2171                 }
2172         }
2173         return false
2174 }
2175
2176 // SkipMultiCursor moves the current multiple cursor to the next available position
2177 func (v *View) SkipMultiCursor(usePlugin bool) bool {
2178         cursor := v.Buf.cursors[len(v.Buf.cursors)-1]
2179
2180         if v.mainCursor() {
2181                 if usePlugin && !PreActionCall("SkipMultiCursor", v) {
2182                         return false
2183                 }
2184                 sel := cursor.GetSelection()
2185
2186                 searchStart = cursor.CurSelection[1]
2187                 v.Cursor = cursor
2188                 Search(regexp.QuoteMeta(sel), v, true)
2189                 v.Relocate()
2190                 v.Cursor = cursor
2191
2192                 if usePlugin {
2193                         PostActionCall("SkipMultiCursor", v)
2194                 }
2195         }
2196         return false
2197 }
2198
2199 // RemoveMultiCursor removes the latest multiple cursor
2200 func (v *View) RemoveMultiCursor(usePlugin bool) bool {
2201         end := len(v.Buf.cursors)
2202         if end > 1 {
2203                 if v.mainCursor() {
2204                         if usePlugin && !PreActionCall("RemoveMultiCursor", v) {
2205                                 return false
2206                         }
2207
2208                         v.Buf.cursors[end-1] = nil
2209                         v.Buf.cursors = v.Buf.cursors[:end-1]
2210                         v.Buf.UpdateCursors()
2211                         v.Relocate()
2212
2213                         if usePlugin {
2214                                 return PostActionCall("RemoveMultiCursor", v)
2215                         }
2216                         return true
2217                 }
2218         } else {
2219                 v.RemoveAllMultiCursors(usePlugin)
2220         }
2221         return false
2222 }
2223
2224 // RemoveAllMultiCursors removes all cursors except the base cursor
2225 func (v *View) RemoveAllMultiCursors(usePlugin bool) bool {
2226         if v.mainCursor() {
2227                 if usePlugin && !PreActionCall("RemoveAllMultiCursors", v) {
2228                         return false
2229                 }
2230
2231                 v.Buf.clearCursors()
2232                 v.Relocate()
2233
2234                 if usePlugin {
2235                         return PostActionCall("RemoveAllMultiCursors", v)
2236                 }
2237                 return true
2238         }
2239         return false
2240 }