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