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