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