12 "github.com/yuin/gopher-lua"
13 "github.com/zyedidia/clipboard"
14 "github.com/zyedidia/micro/cmd/micro/shellwords"
15 "github.com/zyedidia/tcell"
18 // PreActionCall executes the lua pre callback if possible
19 func PreActionCall(funcName string, view *View, args ...interface{}) bool {
21 for pl := range loadedPlugins {
22 ret, err := Call(pl+".pre"+funcName, append([]interface{}{view}, args...)...)
23 if err != nil && !strings.HasPrefix(err.Error(), "function does not exist") {
27 if ret == lua.LFalse {
34 // PostActionCall executes the lua plugin callback if possible
35 func PostActionCall(funcName string, view *View, args ...interface{}) bool {
37 for pl := range loadedPlugins {
38 ret, err := Call(pl+".on"+funcName, append([]interface{}{view}, args...)...)
39 if err != nil && !strings.HasPrefix(err.Error(), "function does not exist") {
43 if ret == lua.LFalse {
50 func (v *View) deselect(index int) bool {
51 if v.Cursor.HasSelection() {
52 v.Cursor.Loc = v.Cursor.CurSelection[index]
53 v.Cursor.ResetSelection()
54 v.Cursor.StoreVisualX()
60 // MousePress is the event that should happen when a normal click happens
61 // This is almost always bound to left click
62 func (v *View) MousePress(usePlugin bool, e *tcell.EventMouse) bool {
63 if usePlugin && !PreActionCall("MousePress", v, e) {
68 x -= v.lineNumOffset - v.leftCol + v.x
71 // This is usually bound to left click
72 v.MoveToMouseClick(x, y)
74 if len(v.Buf.cursors) > 1 {
75 for i := 1; i < len(v.Buf.cursors); i++ {
76 v.Buf.cursors[i] = nil
78 v.Buf.cursors = v.Buf.cursors[:1]
80 v.Cursor.ResetSelection()
83 if time.Since(v.lastClickTime)/time.Millisecond < doubleClickThreshold && (x == v.lastLoc.X && y == v.lastLoc.Y) {
86 v.lastClickTime = time.Now()
92 v.Cursor.CopySelection("primary")
95 v.lastClickTime = time.Now()
100 v.Cursor.SelectWord()
101 v.Cursor.CopySelection("primary")
104 v.doubleClick = false
105 v.tripleClick = false
106 v.lastClickTime = time.Now()
108 v.Cursor.OrigSelection[0] = v.Cursor.Loc
109 v.Cursor.CurSelection[0] = v.Cursor.Loc
110 v.Cursor.CurSelection[1] = v.Cursor.Loc
112 v.mouseReleased = false
113 } else if !v.mouseReleased {
115 v.Cursor.AddLineToSelection()
116 } else if v.doubleClick {
117 v.Cursor.AddWordToSelection()
119 v.Cursor.SetSelectionEnd(v.Cursor.Loc)
120 v.Cursor.CopySelection("primary")
124 v.lastLoc = Loc{x, y}
127 PostActionCall("MousePress", v, e)
132 // ScrollUpAction scrolls the view up
133 func (v *View) ScrollUpAction(usePlugin bool) bool {
135 if usePlugin && !PreActionCall("ScrollUp", v) {
139 scrollspeed := int(v.Buf.Settings["scrollspeed"].(float64))
140 v.ScrollUp(scrollspeed)
143 PostActionCall("ScrollUp", v)
149 // ScrollDownAction scrolls the view up
150 func (v *View) ScrollDownAction(usePlugin bool) bool {
152 if usePlugin && !PreActionCall("ScrollDown", v) {
156 scrollspeed := int(v.Buf.Settings["scrollspeed"].(float64))
157 v.ScrollDown(scrollspeed)
160 PostActionCall("ScrollDown", v)
166 // Center centers the view on the cursor
167 func (v *View) Center(usePlugin bool) bool {
168 if usePlugin && !PreActionCall("Center", v) {
172 v.Topline = v.Cursor.Y - v.Height/2
173 if v.Topline+v.Height > v.Buf.NumLines {
174 v.Topline = v.Buf.NumLines - v.Height
181 return PostActionCall("Center", v)
186 // CursorUp moves the cursor up
187 func (v *View) CursorUp(usePlugin bool) bool {
188 if usePlugin && !PreActionCall("CursorUp", v) {
196 return PostActionCall("CursorUp", v)
201 // CursorDown moves the cursor down
202 func (v *View) CursorDown(usePlugin bool) bool {
203 if usePlugin && !PreActionCall("CursorDown", v) {
211 return PostActionCall("CursorDown", v)
216 // CursorLeft moves the cursor left
217 func (v *View) CursorLeft(usePlugin bool) bool {
218 if usePlugin && !PreActionCall("CursorLeft", v) {
222 if v.Cursor.HasSelection() {
223 v.Cursor.Loc = v.Cursor.CurSelection[0]
224 v.Cursor.ResetSelection()
225 v.Cursor.StoreVisualX()
227 tabstospaces := v.Buf.Settings["tabstospaces"].(bool)
228 tabmovement := v.Buf.Settings["tabmovement"].(bool)
229 if tabstospaces && tabmovement {
230 tabsize := int(v.Buf.Settings["tabsize"].(float64))
231 line := v.Buf.Line(v.Cursor.Y)
232 if v.Cursor.X-tabsize >= 0 && line[v.Cursor.X-tabsize:v.Cursor.X] == Spaces(tabsize) && IsStrWhitespace(line[0:v.Cursor.X-tabsize]) {
233 for i := 0; i < tabsize; i++ {
245 return PostActionCall("CursorLeft", v)
250 // CursorRight moves the cursor right
251 func (v *View) CursorRight(usePlugin bool) bool {
252 if usePlugin && !PreActionCall("CursorRight", v) {
256 if v.Cursor.HasSelection() {
257 v.Cursor.Loc = v.Cursor.CurSelection[1]
258 v.Cursor.ResetSelection()
259 v.Cursor.StoreVisualX()
261 tabstospaces := v.Buf.Settings["tabstospaces"].(bool)
262 tabmovement := v.Buf.Settings["tabmovement"].(bool)
263 if tabstospaces && tabmovement {
264 tabsize := int(v.Buf.Settings["tabsize"].(float64))
265 line := v.Buf.Line(v.Cursor.Y)
266 if v.Cursor.X+tabsize < Count(line) && line[v.Cursor.X:v.Cursor.X+tabsize] == Spaces(tabsize) && IsStrWhitespace(line[0:v.Cursor.X]) {
267 for i := 0; i < tabsize; i++ {
279 return PostActionCall("CursorRight", v)
284 // WordRight moves the cursor one word to the right
285 func (v *View) WordRight(usePlugin bool) bool {
286 if usePlugin && !PreActionCall("WordRight", v) {
293 return PostActionCall("WordRight", v)
298 // WordLeft moves the cursor one word to the left
299 func (v *View) WordLeft(usePlugin bool) bool {
300 if usePlugin && !PreActionCall("WordLeft", v) {
307 return PostActionCall("WordLeft", v)
312 // SelectUp selects up one line
313 func (v *View) SelectUp(usePlugin bool) bool {
314 if usePlugin && !PreActionCall("SelectUp", v) {
318 if !v.Cursor.HasSelection() {
319 v.Cursor.OrigSelection[0] = v.Cursor.Loc
322 v.Cursor.SelectTo(v.Cursor.Loc)
325 return PostActionCall("SelectUp", v)
330 // SelectDown selects down one line
331 func (v *View) SelectDown(usePlugin bool) bool {
332 if usePlugin && !PreActionCall("SelectDown", v) {
336 if !v.Cursor.HasSelection() {
337 v.Cursor.OrigSelection[0] = v.Cursor.Loc
340 v.Cursor.SelectTo(v.Cursor.Loc)
343 return PostActionCall("SelectDown", v)
348 // SelectLeft selects the character to the left of the cursor
349 func (v *View) SelectLeft(usePlugin bool) bool {
350 if usePlugin && !PreActionCall("SelectLeft", v) {
356 if loc.GreaterThan(count) {
359 if !v.Cursor.HasSelection() {
360 v.Cursor.OrigSelection[0] = loc
363 v.Cursor.SelectTo(v.Cursor.Loc)
366 return PostActionCall("SelectLeft", v)
371 // SelectRight selects the character to the right of the cursor
372 func (v *View) SelectRight(usePlugin bool) bool {
373 if usePlugin && !PreActionCall("SelectRight", v) {
379 if loc.GreaterThan(count) {
382 if !v.Cursor.HasSelection() {
383 v.Cursor.OrigSelection[0] = loc
386 v.Cursor.SelectTo(v.Cursor.Loc)
389 return PostActionCall("SelectRight", v)
394 // SelectWordRight selects the word to the right of the cursor
395 func (v *View) SelectWordRight(usePlugin bool) bool {
396 if usePlugin && !PreActionCall("SelectWordRight", v) {
400 if !v.Cursor.HasSelection() {
401 v.Cursor.OrigSelection[0] = v.Cursor.Loc
404 v.Cursor.SelectTo(v.Cursor.Loc)
407 return PostActionCall("SelectWordRight", v)
412 // SelectWordLeft selects the word to the left of the cursor
413 func (v *View) SelectWordLeft(usePlugin bool) bool {
414 if usePlugin && !PreActionCall("SelectWordLeft", v) {
418 if !v.Cursor.HasSelection() {
419 v.Cursor.OrigSelection[0] = v.Cursor.Loc
422 v.Cursor.SelectTo(v.Cursor.Loc)
425 return PostActionCall("SelectWordLeft", v)
430 // StartOfLine moves the cursor to the start of the line
431 func (v *View) StartOfLine(usePlugin bool) bool {
432 if usePlugin && !PreActionCall("StartOfLine", v) {
441 v.Cursor.StartOfText()
445 return PostActionCall("StartOfLine", v)
450 // EndOfLine moves the cursor to the end of the line
451 func (v *View) EndOfLine(usePlugin bool) bool {
452 if usePlugin && !PreActionCall("EndOfLine", v) {
461 return PostActionCall("EndOfLine", v)
466 // SelectLine selects the entire current line
467 func (v *View) SelectLine(usePlugin bool) bool {
468 if usePlugin && !PreActionCall("SelectLine", v) {
472 v.Cursor.SelectLine()
475 return PostActionCall("SelectLine", v)
480 // SelectToStartOfLine selects to the start of the current line
481 func (v *View) SelectToStartOfLine(usePlugin bool) bool {
482 if usePlugin && !PreActionCall("SelectToStartOfLine", v) {
486 if !v.Cursor.HasSelection() {
487 v.Cursor.OrigSelection[0] = v.Cursor.Loc
490 v.Cursor.SelectTo(v.Cursor.Loc)
493 return PostActionCall("SelectToStartOfLine", v)
498 // SelectToEndOfLine selects to the end of the current line
499 func (v *View) SelectToEndOfLine(usePlugin bool) bool {
500 if usePlugin && !PreActionCall("SelectToEndOfLine", v) {
504 if !v.Cursor.HasSelection() {
505 v.Cursor.OrigSelection[0] = v.Cursor.Loc
508 v.Cursor.SelectTo(v.Cursor.Loc)
511 return PostActionCall("SelectToEndOfLine", v)
516 // ParagraphPrevious moves the cursor to the previous empty line, or beginning of the buffer if there's none
517 func (v *View) ParagraphPrevious(usePlugin bool) bool {
518 if usePlugin && !PreActionCall("ParagraphPrevious", v) {
522 for line = v.Cursor.Y; line > 0; line-- {
523 if len(v.Buf.lines[line].data) == 0 && line != v.Cursor.Y {
529 // If no empty line found. move cursor to end of buffer
531 v.Cursor.Loc = v.Buf.Start()
535 return PostActionCall("ParagraphPrevious", v)
540 // ParagraphNext moves the cursor to the next empty line, or end of the buffer if there's none
541 func (v *View) ParagraphNext(usePlugin bool) bool {
542 if usePlugin && !PreActionCall("ParagraphNext", v) {
547 for line = v.Cursor.Y; line < len(v.Buf.lines); line++ {
548 if len(v.Buf.lines[line].data) == 0 && line != v.Cursor.Y {
554 // If no empty line found. move cursor to end of buffer
555 if line == len(v.Buf.lines) {
556 v.Cursor.Loc = v.Buf.End()
560 return PostActionCall("ParagraphNext", v)
565 // Retab changes all tabs to spaces or all spaces to tabs depending
566 // on the user's settings
567 func (v *View) Retab(usePlugin bool) bool {
568 if usePlugin && !PreActionCall("Retab", v) {
572 toSpaces := v.Buf.Settings["tabstospaces"].(bool)
573 tabsize := int(v.Buf.Settings["tabsize"].(float64))
576 for i := 0; i < v.Buf.NumLines; i++ {
579 ws := GetLeadingWhitespace(l)
582 ws = strings.Replace(ws, "\t", Spaces(tabsize), -1)
584 ws = strings.Replace(ws, Spaces(tabsize), "\t", -1)
588 l = strings.TrimLeft(l, " \t")
589 v.Buf.lines[i].data = []byte(ws + l)
593 v.Buf.IsModified = dirty
596 return PostActionCall("Retab", v)
601 // CursorStart moves the cursor to the start of the buffer
602 func (v *View) CursorStart(usePlugin bool) bool {
603 if usePlugin && !PreActionCall("CursorStart", v) {
613 return PostActionCall("CursorStart", v)
618 // CursorEnd moves the cursor to the end of the buffer
619 func (v *View) CursorEnd(usePlugin bool) bool {
620 if usePlugin && !PreActionCall("CursorEnd", v) {
626 v.Cursor.Loc = v.Buf.End()
627 v.Cursor.StoreVisualX()
630 return PostActionCall("CursorEnd", v)
635 // SelectToStart selects the text from the cursor to the start of the buffer
636 func (v *View) SelectToStart(usePlugin bool) bool {
637 if usePlugin && !PreActionCall("SelectToStart", v) {
641 if !v.Cursor.HasSelection() {
642 v.Cursor.OrigSelection[0] = v.Cursor.Loc
645 v.Cursor.SelectTo(v.Buf.Start())
648 return PostActionCall("SelectToStart", v)
653 // SelectToEnd selects the text from the cursor to the end of the buffer
654 func (v *View) SelectToEnd(usePlugin bool) bool {
655 if usePlugin && !PreActionCall("SelectToEnd", v) {
659 if !v.Cursor.HasSelection() {
660 v.Cursor.OrigSelection[0] = v.Cursor.Loc
663 v.Cursor.SelectTo(v.Buf.End())
666 return PostActionCall("SelectToEnd", v)
671 // InsertSpace inserts a space
672 func (v *View) InsertSpace(usePlugin bool) bool {
673 if usePlugin && !PreActionCall("InsertSpace", v) {
677 if v.Cursor.HasSelection() {
678 v.Cursor.DeleteSelection()
679 v.Cursor.ResetSelection()
681 v.Buf.Insert(v.Cursor.Loc, " ")
685 return PostActionCall("InsertSpace", v)
690 // InsertNewline inserts a newline plus possible some whitespace if autoindent is on
691 func (v *View) InsertNewline(usePlugin bool) bool {
692 if usePlugin && !PreActionCall("InsertNewline", v) {
697 if v.Cursor.HasSelection() {
698 v.Cursor.DeleteSelection()
699 v.Cursor.ResetSelection()
702 ws := GetLeadingWhitespace(v.Buf.Line(v.Cursor.Y))
704 v.Buf.Insert(v.Cursor.Loc, "\n")
707 if v.Buf.Settings["autoindent"].(bool) {
711 v.Buf.Insert(v.Cursor.Loc, ws)
712 // for i := 0; i < len(ws); i++ {
716 // Remove the whitespaces if keepautoindent setting is off
717 if IsSpacesOrTabs(v.Buf.Line(v.Cursor.Y-1)) && !v.Buf.Settings["keepautoindent"].(bool) {
718 line := v.Buf.Line(v.Cursor.Y - 1)
719 v.Buf.Remove(Loc{0, v.Cursor.Y - 1}, Loc{Count(line), v.Cursor.Y - 1})
722 v.Cursor.LastVisualX = v.Cursor.GetVisualX()
725 return PostActionCall("InsertNewline", v)
730 // Backspace deletes the previous character
731 func (v *View) Backspace(usePlugin bool) bool {
732 if usePlugin && !PreActionCall("Backspace", v) {
736 // Delete a character
737 if v.Cursor.HasSelection() {
738 v.Cursor.DeleteSelection()
739 v.Cursor.ResetSelection()
740 } else if v.Cursor.Loc.GreaterThan(v.Buf.Start()) {
741 // We have to do something a bit hacky here because we want to
742 // delete the line by first moving left and then deleting backwards
743 // but the undo redo would place the cursor in the wrong place
744 // So instead we move left, save the position, move back, delete
745 // and restore the position
747 // If the user is using spaces instead of tabs and they are deleting
748 // whitespace at the start of the line, we should delete as if it's a
749 // tab (tabSize number of spaces)
750 lineStart := sliceEnd(v.Buf.LineBytes(v.Cursor.Y), v.Cursor.X)
751 tabSize := int(v.Buf.Settings["tabsize"].(float64))
752 if v.Buf.Settings["tabstospaces"].(bool) && IsSpaces(lineStart) && utf8.RuneCount(lineStart) != 0 && utf8.RuneCount(lineStart)%tabSize == 0 {
754 v.Buf.Remove(loc.Move(-tabSize, v.Buf), loc)
757 v.Buf.Remove(loc.Move(-1, v.Buf), loc)
760 v.Cursor.LastVisualX = v.Cursor.GetVisualX()
763 return PostActionCall("Backspace", v)
768 // DeleteWordRight deletes the word to the right of the cursor
769 func (v *View) DeleteWordRight(usePlugin bool) bool {
770 if usePlugin && !PreActionCall("DeleteWordRight", v) {
774 v.SelectWordRight(false)
775 if v.Cursor.HasSelection() {
776 v.Cursor.DeleteSelection()
777 v.Cursor.ResetSelection()
781 return PostActionCall("DeleteWordRight", v)
786 // DeleteWordLeft deletes the word to the left of the cursor
787 func (v *View) DeleteWordLeft(usePlugin bool) bool {
788 if usePlugin && !PreActionCall("DeleteWordLeft", v) {
792 v.SelectWordLeft(false)
793 if v.Cursor.HasSelection() {
794 v.Cursor.DeleteSelection()
795 v.Cursor.ResetSelection()
799 return PostActionCall("DeleteWordLeft", v)
804 // Delete deletes the next character
805 func (v *View) Delete(usePlugin bool) bool {
806 if usePlugin && !PreActionCall("Delete", v) {
810 if v.Cursor.HasSelection() {
811 v.Cursor.DeleteSelection()
812 v.Cursor.ResetSelection()
815 if loc.LessThan(v.Buf.End()) {
816 v.Buf.Remove(loc, loc.Move(1, v.Buf))
821 return PostActionCall("Delete", v)
826 // IndentSelection indents the current selection
827 func (v *View) IndentSelection(usePlugin bool) bool {
828 if usePlugin && !PreActionCall("IndentSelection", v) {
832 if v.Cursor.HasSelection() {
833 start := v.Cursor.CurSelection[0]
834 end := v.Cursor.CurSelection[1]
836 start, end = end, start
837 v.Cursor.SetSelectionStart(start)
838 v.Cursor.SetSelectionEnd(end)
842 endY := end.Move(-1, v.Buf).Y
843 endX := end.Move(-1, v.Buf).X
844 tabsize := len(v.Buf.IndentString())
845 for y := startY; y <= endY; y++ {
846 v.Buf.Insert(Loc{0, y}, v.Buf.IndentString())
847 if y == startY && start.X > 0 {
848 v.Cursor.SetSelectionStart(start.Move(tabsize, v.Buf))
851 v.Cursor.SetSelectionEnd(Loc{endX + tabsize + 1, endY})
857 return PostActionCall("IndentSelection", v)
864 // OutdentLine moves the current line back one indentation
865 func (v *View) OutdentLine(usePlugin bool) bool {
866 if usePlugin && !PreActionCall("OutdentLine", v) {
870 if v.Cursor.HasSelection() {
874 for x := 0; x < len(v.Buf.IndentString()); x++ {
875 if len(GetLeadingWhitespace(v.Buf.Line(v.Cursor.Y))) == 0 {
878 v.Buf.Remove(Loc{0, v.Cursor.Y}, Loc{1, v.Cursor.Y})
883 return PostActionCall("OutdentLine", v)
888 // OutdentSelection takes the current selection and moves it back one indent level
889 func (v *View) OutdentSelection(usePlugin bool) bool {
890 if usePlugin && !PreActionCall("OutdentSelection", v) {
894 if v.Cursor.HasSelection() {
895 start := v.Cursor.CurSelection[0]
896 end := v.Cursor.CurSelection[1]
898 start, end = end, start
899 v.Cursor.SetSelectionStart(start)
900 v.Cursor.SetSelectionEnd(end)
904 endY := end.Move(-1, v.Buf).Y
905 for y := startY; y <= endY; y++ {
906 for x := 0; x < len(v.Buf.IndentString()); x++ {
907 if len(GetLeadingWhitespace(v.Buf.Line(y))) == 0 {
910 v.Buf.Remove(Loc{0, y}, Loc{1, y})
916 return PostActionCall("OutdentSelection", v)
923 // InsertTab inserts a tab or spaces
924 func (v *View) InsertTab(usePlugin bool) bool {
925 if usePlugin && !PreActionCall("InsertTab", v) {
929 if v.Cursor.HasSelection() {
933 tabBytes := len(v.Buf.IndentString())
934 bytesUntilIndent := tabBytes - (v.Cursor.GetVisualX() % tabBytes)
935 v.Buf.Insert(v.Cursor.Loc, v.Buf.IndentString()[:bytesUntilIndent])
936 // for i := 0; i < bytesUntilIndent; i++ {
941 return PostActionCall("InsertTab", v)
946 // SaveAll saves all open buffers
947 func (v *View) SaveAll(usePlugin bool) bool {
949 if usePlugin && !PreActionCall("SaveAll", v) {
953 for _, t := range tabs {
954 for _, v := range t.Views {
960 return PostActionCall("SaveAll", v)
966 // Save the buffer to disk
967 func (v *View) Save(usePlugin bool) bool {
969 if usePlugin && !PreActionCall("Save", v) {
973 if v.Type.Scratch == true {
974 // We can't save any view type with scratch set. eg help and log text
977 // If this is an empty buffer, ask for a filename
978 if v.Buf.Path == "" {
981 v.saveToFile(v.Buf.Path)
985 return PostActionCall("Save", v)
991 // This function saves the buffer to `filename` and changes the buffer's path and name
992 // to `filename` if the save is successful
993 func (v *View) saveToFile(filename string) {
994 err := v.Buf.SaveAs(filename)
996 if strings.HasSuffix(err.Error(), "permission denied") {
997 choice, _ := messenger.YesNoPrompt("Permission denied. Do you want to save this file using sudo? (y,n)")
999 err = v.Buf.SaveAsWithSudo(filename)
1001 messenger.Error(err.Error())
1003 v.Buf.Path = filename
1004 v.Buf.name = filename
1005 messenger.Message("Saved " + filename)
1011 messenger.Error(err.Error())
1014 v.Buf.Path = filename
1015 v.Buf.name = filename
1016 messenger.Message("Saved " + filename)
1020 // SaveAs saves the buffer to disk with the given name
1021 func (v *View) SaveAs(usePlugin bool) bool {
1023 if usePlugin && !PreActionCall("SaveAs", v) {
1027 filename, canceled := messenger.Prompt("Filename: ", "", "Save", NoCompletion)
1029 // the filename might or might not be quoted, so unquote first then join the strings.
1030 args, err := shellwords.Split(filename)
1031 filename = strings.Join(args, " ")
1033 messenger.Error("Error parsing arguments: ", err)
1036 v.saveToFile(filename)
1040 PostActionCall("SaveAs", v)
1046 // Find opens a prompt and searches forward for the input
1047 func (v *View) Find(usePlugin bool) bool {
1049 if usePlugin && !PreActionCall("Find", v) {
1054 if v.Cursor.HasSelection() {
1055 searchStart = v.Cursor.CurSelection[1]
1056 searchStart = v.Cursor.CurSelection[1]
1057 searchStr = v.Cursor.GetSelection()
1059 searchStart = v.Cursor.Loc
1061 BeginSearch(searchStr)
1064 return PostActionCall("Find", v)
1070 // FindNext searches forwards for the last used search term
1071 func (v *View) FindNext(usePlugin bool) bool {
1072 if usePlugin && !PreActionCall("FindNext", v) {
1076 if v.Cursor.HasSelection() {
1077 searchStart = v.Cursor.CurSelection[1]
1078 // lastSearch = v.Cursor.GetSelection()
1080 searchStart = v.Cursor.Loc
1082 if lastSearch == "" {
1085 messenger.Message("Finding: " + lastSearch)
1086 Search(lastSearch, v, true)
1089 return PostActionCall("FindNext", v)
1094 // FindPrevious searches backwards for the last used search term
1095 func (v *View) FindPrevious(usePlugin bool) bool {
1096 if usePlugin && !PreActionCall("FindPrevious", v) {
1100 if v.Cursor.HasSelection() {
1101 searchStart = v.Cursor.CurSelection[0]
1103 searchStart = v.Cursor.Loc
1105 messenger.Message("Finding: " + lastSearch)
1106 Search(lastSearch, v, false)
1109 return PostActionCall("FindPrevious", v)
1114 // Undo undoes the last action
1115 func (v *View) Undo(usePlugin bool) bool {
1116 if usePlugin && !PreActionCall("Undo", v) {
1120 if v.Buf.curCursor == 0 {
1121 v.Buf.clearCursors()
1125 messenger.Message("Undid action")
1128 return PostActionCall("Undo", v)
1133 // Redo redoes the last action
1134 func (v *View) Redo(usePlugin bool) bool {
1135 if usePlugin && !PreActionCall("Redo", v) {
1139 if v.Buf.curCursor == 0 {
1140 v.Buf.clearCursors()
1144 messenger.Message("Redid action")
1147 return PostActionCall("Redo", v)
1152 // Copy the selection to the system clipboard
1153 func (v *View) Copy(usePlugin bool) bool {
1155 if usePlugin && !PreActionCall("Copy", v) {
1159 if v.Cursor.HasSelection() {
1160 v.Cursor.CopySelection("clipboard")
1162 messenger.Message("Copied selection")
1166 return PostActionCall("Copy", v)
1172 // CutLine cuts the current line to the clipboard
1173 func (v *View) CutLine(usePlugin bool) bool {
1174 if usePlugin && !PreActionCall("CutLine", v) {
1178 v.Cursor.SelectLine()
1179 if !v.Cursor.HasSelection() {
1182 if v.freshClip == true {
1183 if v.Cursor.HasSelection() {
1184 if clip, err := clipboard.ReadAll("clipboard"); err != nil {
1185 messenger.Error(err)
1187 clipboard.WriteAll(clip+v.Cursor.GetSelection(), "clipboard")
1190 } else if time.Since(v.lastCutTime)/time.Second > 10*time.Second || v.freshClip == false {
1194 v.lastCutTime = time.Now()
1195 v.Cursor.DeleteSelection()
1196 v.Cursor.ResetSelection()
1197 messenger.Message("Cut line")
1200 return PostActionCall("CutLine", v)
1205 // Cut the selection to the system clipboard
1206 func (v *View) Cut(usePlugin bool) bool {
1207 if usePlugin && !PreActionCall("Cut", v) {
1211 if v.Cursor.HasSelection() {
1212 v.Cursor.CopySelection("clipboard")
1213 v.Cursor.DeleteSelection()
1214 v.Cursor.ResetSelection()
1216 messenger.Message("Cut selection")
1219 return PostActionCall("Cut", v)
1223 return v.CutLine(usePlugin)
1227 // DuplicateLine duplicates the current line or selection
1228 func (v *View) DuplicateLine(usePlugin bool) bool {
1229 if usePlugin && !PreActionCall("DuplicateLine", v) {
1233 if v.Cursor.HasSelection() {
1234 v.Buf.Insert(v.Cursor.CurSelection[1], v.Cursor.GetSelection())
1237 v.Buf.Insert(v.Cursor.Loc, "\n"+v.Buf.Line(v.Cursor.Y))
1241 messenger.Message("Duplicated line")
1244 return PostActionCall("DuplicateLine", v)
1249 // DeleteLine deletes the current line
1250 func (v *View) DeleteLine(usePlugin bool) bool {
1251 if usePlugin && !PreActionCall("DeleteLine", v) {
1255 v.Cursor.SelectLine()
1256 if !v.Cursor.HasSelection() {
1259 v.Cursor.DeleteSelection()
1260 v.Cursor.ResetSelection()
1261 messenger.Message("Deleted line")
1264 return PostActionCall("DeleteLine", v)
1269 // MoveLinesUp moves up the current line or selected lines if any
1270 func (v *View) MoveLinesUp(usePlugin bool) bool {
1271 if usePlugin && !PreActionCall("MoveLinesUp", v) {
1275 if v.Cursor.HasSelection() {
1276 if v.Cursor.CurSelection[0].Y == 0 {
1277 messenger.Message("Can not move further up")
1280 start := v.Cursor.CurSelection[0].Y
1281 end := v.Cursor.CurSelection[1].Y
1283 end, start = start, end
1290 v.Cursor.CurSelection[1].Y -= 1
1291 messenger.Message("Moved up selected line(s)")
1293 if v.Cursor.Loc.Y == 0 {
1294 messenger.Message("Can not move further up")
1301 messenger.Message("Moved up current line")
1303 v.Buf.IsModified = true
1306 return PostActionCall("MoveLinesUp", v)
1311 // MoveLinesDown moves down the current line or selected lines if any
1312 func (v *View) MoveLinesDown(usePlugin bool) bool {
1313 if usePlugin && !PreActionCall("MoveLinesDown", v) {
1317 if v.Cursor.HasSelection() {
1318 if v.Cursor.CurSelection[1].Y >= len(v.Buf.lines) {
1319 messenger.Message("Can not move further down")
1322 start := v.Cursor.CurSelection[0].Y
1323 end := v.Cursor.CurSelection[1].Y
1325 end, start = start, end
1328 v.Buf.MoveLinesDown(
1332 messenger.Message("Moved down selected line(s)")
1334 if v.Cursor.Loc.Y >= len(v.Buf.lines)-1 {
1335 messenger.Message("Can not move further down")
1338 v.Buf.MoveLinesDown(
1342 messenger.Message("Moved down current line")
1344 v.Buf.IsModified = true
1347 return PostActionCall("MoveLinesDown", v)
1352 // Paste whatever is in the system clipboard into the buffer
1353 // Delete and paste if the user has a selection
1354 func (v *View) Paste(usePlugin bool) bool {
1355 if usePlugin && !PreActionCall("Paste", v) {
1359 clip, _ := clipboard.ReadAll("clipboard")
1363 return PostActionCall("Paste", v)
1368 // PastePrimary pastes from the primary clipboard (only use on linux)
1369 func (v *View) PastePrimary(usePlugin bool) bool {
1370 if usePlugin && !PreActionCall("Paste", v) {
1374 clip, _ := clipboard.ReadAll("primary")
1378 return PostActionCall("Paste", v)
1383 // JumpToMatchingBrace moves the cursor to the matching brace if it is
1384 // currently on a brace
1385 func (v *View) JumpToMatchingBrace(usePlugin bool) bool {
1386 if usePlugin && !PreActionCall("JumpToMatchingBrace", v) {
1390 for _, bp := range bracePairs {
1391 r := v.Cursor.RuneUnder(v.Cursor.X)
1392 if r == bp[0] || r == bp[1] {
1393 matchingBrace := v.Buf.FindMatchingBrace(bp, v.Cursor.Loc)
1394 v.Cursor.GotoLoc(matchingBrace)
1399 return PostActionCall("JumpToMatchingBrace", v)
1404 // SelectAll selects the entire buffer
1405 func (v *View) SelectAll(usePlugin bool) bool {
1406 if usePlugin && !PreActionCall("SelectAll", v) {
1410 v.Cursor.SetSelectionStart(v.Buf.Start())
1411 v.Cursor.SetSelectionEnd(v.Buf.End())
1412 // Put the cursor at the beginning
1417 return PostActionCall("SelectAll", v)
1422 // OpenFile opens a new file in the buffer
1423 func (v *View) OpenFile(usePlugin bool) bool {
1425 if usePlugin && !PreActionCall("OpenFile", v) {
1430 input, canceled := messenger.Prompt("> ", "open ", "Open", CommandCompletion)
1432 HandleCommand(input)
1434 return PostActionCall("OpenFile", v)
1442 // Start moves the viewport to the start of the buffer
1443 func (v *View) Start(usePlugin bool) bool {
1445 if usePlugin && !PreActionCall("Start", v) {
1452 return PostActionCall("Start", v)
1458 // End moves the viewport to the end of the buffer
1459 func (v *View) End(usePlugin bool) bool {
1461 if usePlugin && !PreActionCall("End", v) {
1465 if v.Height > v.Buf.NumLines {
1468 v.Topline = v.Buf.NumLines - v.Height
1472 return PostActionCall("End", v)
1478 // PageUp scrolls the view up a page
1479 func (v *View) PageUp(usePlugin bool) bool {
1481 if usePlugin && !PreActionCall("PageUp", v) {
1485 if v.Topline > v.Height {
1486 v.ScrollUp(v.Height)
1492 return PostActionCall("PageUp", v)
1498 // PageDown scrolls the view down a page
1499 func (v *View) PageDown(usePlugin bool) bool {
1501 if usePlugin && !PreActionCall("PageDown", v) {
1505 if v.Buf.NumLines-(v.Topline+v.Height) > v.Height {
1506 v.ScrollDown(v.Height)
1507 } else if v.Buf.NumLines >= v.Height {
1508 v.Topline = v.Buf.NumLines - v.Height
1512 return PostActionCall("PageDown", v)
1518 // SelectPageUp selects up one page
1519 func (v *View) SelectPageUp(usePlugin bool) bool {
1520 if usePlugin && !PreActionCall("SelectPageUp", v) {
1524 if !v.Cursor.HasSelection() {
1525 v.Cursor.OrigSelection[0] = v.Cursor.Loc
1527 v.Cursor.UpN(v.Height)
1528 v.Cursor.SelectTo(v.Cursor.Loc)
1531 return PostActionCall("SelectPageUp", v)
1536 // SelectPageDown selects down one page
1537 func (v *View) SelectPageDown(usePlugin bool) bool {
1538 if usePlugin && !PreActionCall("SelectPageDown", v) {
1542 if !v.Cursor.HasSelection() {
1543 v.Cursor.OrigSelection[0] = v.Cursor.Loc
1545 v.Cursor.DownN(v.Height)
1546 v.Cursor.SelectTo(v.Cursor.Loc)
1549 return PostActionCall("SelectPageDown", v)
1554 // CursorPageUp places the cursor a page up
1555 func (v *View) CursorPageUp(usePlugin bool) bool {
1556 if usePlugin && !PreActionCall("CursorPageUp", v) {
1562 if v.Cursor.HasSelection() {
1563 v.Cursor.Loc = v.Cursor.CurSelection[0]
1564 v.Cursor.ResetSelection()
1565 v.Cursor.StoreVisualX()
1567 v.Cursor.UpN(v.Height)
1570 return PostActionCall("CursorPageUp", v)
1575 // CursorPageDown places the cursor a page up
1576 func (v *View) CursorPageDown(usePlugin bool) bool {
1577 if usePlugin && !PreActionCall("CursorPageDown", v) {
1583 if v.Cursor.HasSelection() {
1584 v.Cursor.Loc = v.Cursor.CurSelection[1]
1585 v.Cursor.ResetSelection()
1586 v.Cursor.StoreVisualX()
1588 v.Cursor.DownN(v.Height)
1591 return PostActionCall("CursorPageDown", v)
1596 // HalfPageUp scrolls the view up half a page
1597 func (v *View) HalfPageUp(usePlugin bool) bool {
1599 if usePlugin && !PreActionCall("HalfPageUp", v) {
1603 if v.Topline > v.Height/2 {
1604 v.ScrollUp(v.Height / 2)
1610 return PostActionCall("HalfPageUp", v)
1616 // HalfPageDown scrolls the view down half a page
1617 func (v *View) HalfPageDown(usePlugin bool) bool {
1619 if usePlugin && !PreActionCall("HalfPageDown", v) {
1623 if v.Buf.NumLines-(v.Topline+v.Height) > v.Height/2 {
1624 v.ScrollDown(v.Height / 2)
1626 if v.Buf.NumLines >= v.Height {
1627 v.Topline = v.Buf.NumLines - v.Height
1632 return PostActionCall("HalfPageDown", v)
1638 // ToggleRuler turns line numbers off and on
1639 func (v *View) ToggleRuler(usePlugin bool) bool {
1641 if usePlugin && !PreActionCall("ToggleRuler", v) {
1645 if v.Buf.Settings["ruler"] == false {
1646 v.Buf.Settings["ruler"] = true
1647 messenger.Message("Enabled ruler")
1649 v.Buf.Settings["ruler"] = false
1650 messenger.Message("Disabled ruler")
1654 return PostActionCall("ToggleRuler", v)
1660 // JumpLine jumps to a line and moves the view accordingly.
1661 func (v *View) JumpLine(usePlugin bool) bool {
1662 if usePlugin && !PreActionCall("JumpLine", v) {
1666 // Prompt for line number
1667 message := fmt.Sprintf("Jump to line:col (1 - %v) # ", v.Buf.NumLines)
1668 input, canceled := messenger.Prompt(message, "", "LineNumber", NoCompletion)
1675 if strings.Contains(input, ":") {
1676 split := strings.Split(input, ":")
1677 lineInt, err = strconv.Atoi(split[0])
1679 messenger.Message("Invalid line number")
1682 colInt, err = strconv.Atoi(split[1])
1684 messenger.Message("Invalid column number")
1688 lineInt, err = strconv.Atoi(input)
1690 messenger.Message("Invalid line number")
1695 // Move cursor and view if possible.
1696 if lineInt < v.Buf.NumLines && lineInt >= 0 {
1698 v.Cursor.Y = lineInt
1701 return PostActionCall("JumpLine", v)
1705 messenger.Error("Only ", v.Buf.NumLines, " lines to jump")
1709 // ClearStatus clears the messenger bar
1710 func (v *View) ClearStatus(usePlugin bool) bool {
1712 if usePlugin && !PreActionCall("ClearStatus", v) {
1716 messenger.Message("")
1719 return PostActionCall("ClearStatus", v)
1725 // ToggleHelp toggles the help screen
1726 func (v *View) ToggleHelp(usePlugin bool) bool {
1728 if usePlugin && !PreActionCall("ToggleHelp", v) {
1732 if v.Type != vtHelp {
1733 // Open the default help
1740 return PostActionCall("ToggleHelp", v)
1746 // ToggleKeyMenu toggles the keymenu option and resizes all tabs
1747 func (v *View) ToggleKeyMenu(usePlugin bool) bool {
1749 if usePlugin && !PreActionCall("ToggleBindings", v) {
1753 globalSettings["keymenu"] = !globalSettings["keymenu"].(bool)
1754 for _, tab := range tabs {
1759 return PostActionCall("ToggleBindings", v)
1765 // ShellMode opens a terminal to run a shell command
1766 func (v *View) ShellMode(usePlugin bool) bool {
1768 if usePlugin && !PreActionCall("ShellMode", v) {
1772 input, canceled := messenger.Prompt("$ ", "", "Shell", NoCompletion)
1774 // The true here is for openTerm to make the command interactive
1775 HandleShellCommand(input, true, true)
1777 return PostActionCall("ShellMode", v)
1784 // CommandMode lets the user enter a command
1785 func (v *View) CommandMode(usePlugin bool) bool {
1787 if usePlugin && !PreActionCall("CommandMode", v) {
1791 input, canceled := messenger.Prompt("> ", "", "Command", CommandCompletion)
1793 HandleCommand(input)
1795 return PostActionCall("CommandMode", v)
1803 // ToggleOverwriteMode lets the user toggle the text overwrite mode
1804 func (v *View) ToggleOverwriteMode(usePlugin bool) bool {
1806 if usePlugin && !PreActionCall("ToggleOverwriteMode", v) {
1810 v.isOverwriteMode = !v.isOverwriteMode
1813 return PostActionCall("ToggleOverwriteMode", v)
1819 // Escape leaves current mode
1820 func (v *View) Escape(usePlugin bool) bool {
1822 // check if user is searching, or the last search is still active
1823 if searching || lastSearch != "" {
1827 // check if a prompt is shown, hide it and don't quit
1828 if messenger.hasPrompt {
1829 messenger.Reset() // FIXME
1837 // Quit this will close the current tab or view that is open
1838 func (v *View) Quit(usePlugin bool) bool {
1840 if usePlugin && !PreActionCall("Quit", v) {
1844 // Make sure not to quit if there are unsaved changes
1847 if len(tabs[curTab].Views) > 1 {
1848 v.splitNode.Delete()
1849 tabs[v.TabNum].Cleanup()
1850 tabs[v.TabNum].Resize()
1851 } else if len(tabs) > 1 {
1852 if len(tabs[v.TabNum].Views) == 1 {
1853 tabs = tabs[:v.TabNum+copy(tabs[v.TabNum:], tabs[v.TabNum+1:])]
1854 for i, t := range tabs {
1857 if curTab >= len(tabs) {
1861 CurView().ToggleTabbar()
1866 PostActionCall("Quit", v)
1870 messenger.SaveHistory()
1876 return PostActionCall("Quit", v)
1882 // QuitAll quits the whole editor; all splits and tabs
1883 func (v *View) QuitAll(usePlugin bool) bool {
1885 if usePlugin && !PreActionCall("QuitAll", v) {
1890 for _, tab := range tabs {
1891 for _, v := range tab.Views {
1899 // only quit if all of the buffers can be closed and the user confirms that they actually want to quit everything
1900 shouldQuit, _ := messenger.YesNoPrompt("Do you want to quit micro (all open files will be closed)?")
1903 for _, tab := range tabs {
1904 for _, v := range tab.Views {
1910 PostActionCall("QuitAll", v)
1914 messenger.SaveHistory()
1923 // AddTab adds a new tab with an empty buffer
1924 func (v *View) AddTab(usePlugin bool) bool {
1926 if usePlugin && !PreActionCall("AddTab", v) {
1930 tab := NewTabFromView(NewView(NewBufferFromString("", "")))
1931 tab.SetNum(len(tabs))
1932 tabs = append(tabs, tab)
1933 curTab = len(tabs) - 1
1935 for _, t := range tabs {
1936 for _, v := range t.Views {
1943 return PostActionCall("AddTab", v)
1949 // PreviousTab switches to the previous tab in the tab list
1950 func (v *View) PreviousTab(usePlugin bool) bool {
1952 if usePlugin && !PreActionCall("PreviousTab", v) {
1958 } else if curTab == 0 {
1959 curTab = len(tabs) - 1
1963 return PostActionCall("PreviousTab", v)
1969 // NextTab switches to the next tab in the tab list
1970 func (v *View) NextTab(usePlugin bool) bool {
1972 if usePlugin && !PreActionCall("NextTab", v) {
1976 if curTab < len(tabs)-1 {
1978 } else if curTab == len(tabs)-1 {
1983 return PostActionCall("NextTab", v)
1989 // VSplitBinding opens an empty vertical split
1990 func (v *View) VSplitBinding(usePlugin bool) bool {
1992 if usePlugin && !PreActionCall("VSplit", v) {
1996 v.VSplit(NewBufferFromString("", ""))
1999 return PostActionCall("VSplit", v)
2005 // HSplitBinding opens an empty horizontal split
2006 func (v *View) HSplitBinding(usePlugin bool) bool {
2008 if usePlugin && !PreActionCall("HSplit", v) {
2012 v.HSplit(NewBufferFromString("", ""))
2015 return PostActionCall("HSplit", v)
2021 // Unsplit closes all splits in the current tab except the active one
2022 func (v *View) Unsplit(usePlugin bool) bool {
2024 if usePlugin && !PreActionCall("Unsplit", v) {
2028 curView := tabs[curTab].CurView
2029 for i := len(tabs[curTab].Views) - 1; i >= 0; i-- {
2030 view := tabs[curTab].Views[i]
2031 if view != nil && view.Num != curView {
2033 // messenger.Message("Quit ", view.Buf.Path)
2038 return PostActionCall("Unsplit", v)
2044 // NextSplit changes the view to the next split
2045 func (v *View) NextSplit(usePlugin bool) bool {
2047 if usePlugin && !PreActionCall("NextSplit", v) {
2052 if tab.CurView < len(tab.Views)-1 {
2059 return PostActionCall("NextSplit", v)
2065 // PreviousSplit changes the view to the previous split
2066 func (v *View) PreviousSplit(usePlugin bool) bool {
2068 if usePlugin && !PreActionCall("PreviousSplit", v) {
2073 if tab.CurView > 0 {
2076 tab.CurView = len(tab.Views) - 1
2080 return PostActionCall("PreviousSplit", v)
2086 var curMacro []interface{}
2087 var recordingMacro bool
2089 // ToggleMacro toggles recording of a macro
2090 func (v *View) ToggleMacro(usePlugin bool) bool {
2092 if usePlugin && !PreActionCall("ToggleMacro", v) {
2096 recordingMacro = !recordingMacro
2099 curMacro = []interface{}{}
2100 messenger.Message("Recording")
2102 messenger.Message("Stopped recording")
2106 return PostActionCall("ToggleMacro", v)
2112 // PlayMacro plays back the most recently recorded macro
2113 func (v *View) PlayMacro(usePlugin bool) bool {
2114 if usePlugin && !PreActionCall("PlayMacro", v) {
2118 for _, action := range curMacro {
2119 switch t := action.(type) {
2121 // Insert a character
2122 if v.Cursor.HasSelection() {
2123 v.Cursor.DeleteSelection()
2124 v.Cursor.ResetSelection()
2126 v.Buf.Insert(v.Cursor.Loc, string(t))
2129 for pl := range loadedPlugins {
2130 _, err := Call(pl+".onRune", string(t), v)
2131 if err != nil && !strings.HasPrefix(err.Error(), "function does not exist") {
2135 case func(*View, bool) bool:
2141 return PostActionCall("PlayMacro", v)
2146 // SpawnMultiCursor creates a new multiple cursor at the next occurrence of the current selection or current word
2147 func (v *View) SpawnMultiCursor(usePlugin bool) bool {
2148 spawner := v.Buf.cursors[len(v.Buf.cursors)-1]
2149 // You can only spawn a cursor from the main cursor
2150 if v.Cursor == spawner {
2151 if usePlugin && !PreActionCall("SpawnMultiCursor", v) {
2155 if !spawner.HasSelection() {
2156 spawner.SelectWord()
2162 sel := spawner.GetSelection()
2164 searchStart = spawner.CurSelection[1]
2166 Search(regexp.QuoteMeta(sel), v, true)
2168 for _, cur := range v.Buf.cursors {
2169 if c.Loc == cur.Loc {
2173 v.Buf.cursors = append(v.Buf.cursors, c)
2174 v.Buf.UpdateCursors()
2180 PostActionCall("SpawnMultiCursor", v)
2187 // SpawnMultiCursorSelect adds a cursor at the beginning of each line of a selection
2188 func (v *View) SpawnMultiCursorSelect(usePlugin bool) bool {
2189 if v.Cursor == &v.Buf.Cursor {
2190 if usePlugin && !PreActionCall("SpawnMultiCursorSelect", v) {
2194 // Avoid cases where multiple cursors already exist, that would create problems
2195 if len(v.Buf.cursors) > 1 {
2202 a, b := v.Cursor.CurSelection[0].Y, v.Cursor.CurSelection[1].Y
2204 startLine, endLine = b, a
2206 startLine, endLine = a, b
2209 if v.Cursor.HasSelection() {
2210 v.Cursor.ResetSelection()
2211 v.Cursor.GotoLoc(Loc{0, startLine})
2213 for i := startLine; i <= endLine; i++ {
2217 c.GotoLoc(Loc{0, i})
2218 v.Buf.cursors = append(v.Buf.cursors, c)
2220 v.Buf.MergeCursors()
2221 v.Buf.UpdateCursors()
2227 PostActionCall("SpawnMultiCursorSelect", v)
2230 messenger.Message("Added cursors from selection")
2235 // MouseMultiCursor is a mouse action which puts a new cursor at the mouse position
2236 func (v *View) MouseMultiCursor(usePlugin bool, e *tcell.EventMouse) bool {
2237 if v.Cursor == &v.Buf.Cursor {
2238 if usePlugin && !PreActionCall("SpawnMultiCursorAtMouse", v, e) {
2241 x, y := e.Position()
2242 x -= v.lineNumOffset - v.leftCol + v.x
2243 y += v.Topline - v.y
2249 v.MoveToMouseClick(x, y)
2251 v.Cursor = &v.Buf.Cursor
2253 v.Buf.cursors = append(v.Buf.cursors, c)
2254 v.Buf.MergeCursors()
2255 v.Buf.UpdateCursors()
2258 PostActionCall("SpawnMultiCursorAtMouse", v)
2264 // SkipMultiCursor moves the current multiple cursor to the next available position
2265 func (v *View) SkipMultiCursor(usePlugin bool) bool {
2266 cursor := v.Buf.cursors[len(v.Buf.cursors)-1]
2269 if usePlugin && !PreActionCall("SkipMultiCursor", v) {
2272 sel := cursor.GetSelection()
2274 searchStart = cursor.CurSelection[1]
2276 Search(regexp.QuoteMeta(sel), v, true)
2281 PostActionCall("SkipMultiCursor", v)
2287 // RemoveMultiCursor removes the latest multiple cursor
2288 func (v *View) RemoveMultiCursor(usePlugin bool) bool {
2289 end := len(v.Buf.cursors)
2292 if usePlugin && !PreActionCall("RemoveMultiCursor", v) {
2296 v.Buf.cursors[end-1] = nil
2297 v.Buf.cursors = v.Buf.cursors[:end-1]
2298 v.Buf.UpdateCursors()
2302 return PostActionCall("RemoveMultiCursor", v)
2307 v.RemoveAllMultiCursors(usePlugin)
2312 // RemoveAllMultiCursors removes all cursors except the base cursor
2313 func (v *View) RemoveAllMultiCursors(usePlugin bool) bool {
2315 if usePlugin && !PreActionCall("RemoveAllMultiCursors", v) {
2319 v.Buf.clearCursors()
2323 return PostActionCall("RemoveAllMultiCursors", v)