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 // CursorPageUp places the cursor a page up
1519 func (v *View) CursorPageUp(usePlugin bool) bool {
1520 if usePlugin && !PreActionCall("CursorPageUp", v) {
1526 if v.Cursor.HasSelection() {
1527 v.Cursor.Loc = v.Cursor.CurSelection[0]
1528 v.Cursor.ResetSelection()
1529 v.Cursor.StoreVisualX()
1531 v.Cursor.UpN(v.Height)
1534 return PostActionCall("CursorPageUp", v)
1539 // CursorPageDown places the cursor a page up
1540 func (v *View) CursorPageDown(usePlugin bool) bool {
1541 if usePlugin && !PreActionCall("CursorPageDown", v) {
1547 if v.Cursor.HasSelection() {
1548 v.Cursor.Loc = v.Cursor.CurSelection[1]
1549 v.Cursor.ResetSelection()
1550 v.Cursor.StoreVisualX()
1552 v.Cursor.DownN(v.Height)
1555 return PostActionCall("CursorPageDown", v)
1560 // HalfPageUp scrolls the view up half a page
1561 func (v *View) HalfPageUp(usePlugin bool) bool {
1563 if usePlugin && !PreActionCall("HalfPageUp", v) {
1567 if v.Topline > v.Height/2 {
1568 v.ScrollUp(v.Height / 2)
1574 return PostActionCall("HalfPageUp", v)
1580 // HalfPageDown scrolls the view down half a page
1581 func (v *View) HalfPageDown(usePlugin bool) bool {
1583 if usePlugin && !PreActionCall("HalfPageDown", v) {
1587 if v.Buf.NumLines-(v.Topline+v.Height) > v.Height/2 {
1588 v.ScrollDown(v.Height / 2)
1590 if v.Buf.NumLines >= v.Height {
1591 v.Topline = v.Buf.NumLines - v.Height
1596 return PostActionCall("HalfPageDown", v)
1602 // ToggleRuler turns line numbers off and on
1603 func (v *View) ToggleRuler(usePlugin bool) bool {
1605 if usePlugin && !PreActionCall("ToggleRuler", v) {
1609 if v.Buf.Settings["ruler"] == false {
1610 v.Buf.Settings["ruler"] = true
1611 messenger.Message("Enabled ruler")
1613 v.Buf.Settings["ruler"] = false
1614 messenger.Message("Disabled ruler")
1618 return PostActionCall("ToggleRuler", v)
1624 // JumpLine jumps to a line and moves the view accordingly.
1625 func (v *View) JumpLine(usePlugin bool) bool {
1626 if usePlugin && !PreActionCall("JumpLine", v) {
1630 // Prompt for line number
1631 message := fmt.Sprintf("Jump to line:col (1 - %v) # ", v.Buf.NumLines)
1632 input, canceled := messenger.Prompt(message, "", "LineNumber", NoCompletion)
1639 if strings.Contains(input, ":") {
1640 split := strings.Split(input, ":")
1641 lineInt, err = strconv.Atoi(split[0])
1643 messenger.Message("Invalid line number")
1646 colInt, err = strconv.Atoi(split[1])
1648 messenger.Message("Invalid column number")
1652 lineInt, err = strconv.Atoi(input)
1654 messenger.Message("Invalid line number")
1659 // Move cursor and view if possible.
1660 if lineInt < v.Buf.NumLines && lineInt >= 0 {
1662 v.Cursor.Y = lineInt
1665 return PostActionCall("JumpLine", v)
1669 messenger.Error("Only ", v.Buf.NumLines, " lines to jump")
1673 // ClearStatus clears the messenger bar
1674 func (v *View) ClearStatus(usePlugin bool) bool {
1676 if usePlugin && !PreActionCall("ClearStatus", v) {
1680 messenger.Message("")
1683 return PostActionCall("ClearStatus", v)
1689 // ToggleHelp toggles the help screen
1690 func (v *View) ToggleHelp(usePlugin bool) bool {
1692 if usePlugin && !PreActionCall("ToggleHelp", v) {
1696 if v.Type != vtHelp {
1697 // Open the default help
1704 return PostActionCall("ToggleHelp", v)
1710 // ToggleKeyMenu toggles the keymenu option and resizes all tabs
1711 func (v *View) ToggleKeyMenu(usePlugin bool) bool {
1713 if usePlugin && !PreActionCall("ToggleBindings", v) {
1717 globalSettings["keymenu"] = !globalSettings["keymenu"].(bool)
1718 for _, tab := range tabs {
1723 return PostActionCall("ToggleBindings", v)
1729 // ShellMode opens a terminal to run a shell command
1730 func (v *View) ShellMode(usePlugin bool) bool {
1732 if usePlugin && !PreActionCall("ShellMode", v) {
1736 input, canceled := messenger.Prompt("$ ", "", "Shell", NoCompletion)
1738 // The true here is for openTerm to make the command interactive
1739 HandleShellCommand(input, true, true)
1741 return PostActionCall("ShellMode", v)
1748 // CommandMode lets the user enter a command
1749 func (v *View) CommandMode(usePlugin bool) bool {
1751 if usePlugin && !PreActionCall("CommandMode", v) {
1755 input, canceled := messenger.Prompt("> ", "", "Command", CommandCompletion)
1757 HandleCommand(input)
1759 return PostActionCall("CommandMode", v)
1767 // ToggleOverwriteMode lets the user toggle the text overwrite mode
1768 func (v *View) ToggleOverwriteMode(usePlugin bool) bool {
1770 if usePlugin && !PreActionCall("ToggleOverwriteMode", v) {
1774 v.isOverwriteMode = !v.isOverwriteMode
1777 return PostActionCall("ToggleOverwriteMode", v)
1783 // Escape leaves current mode
1784 func (v *View) Escape(usePlugin bool) bool {
1786 // check if user is searching, or the last search is still active
1787 if searching || lastSearch != "" {
1791 // check if a prompt is shown, hide it and don't quit
1792 if messenger.hasPrompt {
1793 messenger.Reset() // FIXME
1801 // Quit this will close the current tab or view that is open
1802 func (v *View) Quit(usePlugin bool) bool {
1804 if usePlugin && !PreActionCall("Quit", v) {
1808 // Make sure not to quit if there are unsaved changes
1811 if len(tabs[curTab].Views) > 1 {
1812 v.splitNode.Delete()
1813 tabs[v.TabNum].Cleanup()
1814 tabs[v.TabNum].Resize()
1815 } else if len(tabs) > 1 {
1816 if len(tabs[v.TabNum].Views) == 1 {
1817 tabs = tabs[:v.TabNum+copy(tabs[v.TabNum:], tabs[v.TabNum+1:])]
1818 for i, t := range tabs {
1821 if curTab >= len(tabs) {
1825 CurView().ToggleTabbar()
1830 PostActionCall("Quit", v)
1834 messenger.SaveHistory()
1840 return PostActionCall("Quit", v)
1846 // QuitAll quits the whole editor; all splits and tabs
1847 func (v *View) QuitAll(usePlugin bool) bool {
1849 if usePlugin && !PreActionCall("QuitAll", v) {
1854 for _, tab := range tabs {
1855 for _, v := range tab.Views {
1863 // only quit if all of the buffers can be closed and the user confirms that they actually want to quit everything
1864 shouldQuit, _ := messenger.YesNoPrompt("Do you want to quit micro (all open files will be closed)?")
1867 for _, tab := range tabs {
1868 for _, v := range tab.Views {
1874 PostActionCall("QuitAll", v)
1878 messenger.SaveHistory()
1887 // AddTab adds a new tab with an empty buffer
1888 func (v *View) AddTab(usePlugin bool) bool {
1890 if usePlugin && !PreActionCall("AddTab", v) {
1894 tab := NewTabFromView(NewView(NewBufferFromString("", "")))
1895 tab.SetNum(len(tabs))
1896 tabs = append(tabs, tab)
1897 curTab = len(tabs) - 1
1899 for _, t := range tabs {
1900 for _, v := range t.Views {
1907 return PostActionCall("AddTab", v)
1913 // PreviousTab switches to the previous tab in the tab list
1914 func (v *View) PreviousTab(usePlugin bool) bool {
1916 if usePlugin && !PreActionCall("PreviousTab", v) {
1922 } else if curTab == 0 {
1923 curTab = len(tabs) - 1
1927 return PostActionCall("PreviousTab", v)
1933 // NextTab switches to the next tab in the tab list
1934 func (v *View) NextTab(usePlugin bool) bool {
1936 if usePlugin && !PreActionCall("NextTab", v) {
1940 if curTab < len(tabs)-1 {
1942 } else if curTab == len(tabs)-1 {
1947 return PostActionCall("NextTab", v)
1953 // VSplitBinding opens an empty vertical split
1954 func (v *View) VSplitBinding(usePlugin bool) bool {
1956 if usePlugin && !PreActionCall("VSplit", v) {
1960 v.VSplit(NewBufferFromString("", ""))
1963 return PostActionCall("VSplit", v)
1969 // HSplitBinding opens an empty horizontal split
1970 func (v *View) HSplitBinding(usePlugin bool) bool {
1972 if usePlugin && !PreActionCall("HSplit", v) {
1976 v.HSplit(NewBufferFromString("", ""))
1979 return PostActionCall("HSplit", v)
1985 // Unsplit closes all splits in the current tab except the active one
1986 func (v *View) Unsplit(usePlugin bool) bool {
1988 if usePlugin && !PreActionCall("Unsplit", v) {
1992 curView := tabs[curTab].CurView
1993 for i := len(tabs[curTab].Views) - 1; i >= 0; i-- {
1994 view := tabs[curTab].Views[i]
1995 if view != nil && view.Num != curView {
1997 // messenger.Message("Quit ", view.Buf.Path)
2002 return PostActionCall("Unsplit", v)
2008 // NextSplit changes the view to the next split
2009 func (v *View) NextSplit(usePlugin bool) bool {
2011 if usePlugin && !PreActionCall("NextSplit", v) {
2016 if tab.CurView < len(tab.Views)-1 {
2023 return PostActionCall("NextSplit", v)
2029 // PreviousSplit changes the view to the previous split
2030 func (v *View) PreviousSplit(usePlugin bool) bool {
2032 if usePlugin && !PreActionCall("PreviousSplit", v) {
2037 if tab.CurView > 0 {
2040 tab.CurView = len(tab.Views) - 1
2044 return PostActionCall("PreviousSplit", v)
2050 var curMacro []interface{}
2051 var recordingMacro bool
2053 // ToggleMacro toggles recording of a macro
2054 func (v *View) ToggleMacro(usePlugin bool) bool {
2056 if usePlugin && !PreActionCall("ToggleMacro", v) {
2060 recordingMacro = !recordingMacro
2063 curMacro = []interface{}{}
2064 messenger.Message("Recording")
2066 messenger.Message("Stopped recording")
2070 return PostActionCall("ToggleMacro", v)
2076 // PlayMacro plays back the most recently recorded macro
2077 func (v *View) PlayMacro(usePlugin bool) bool {
2078 if usePlugin && !PreActionCall("PlayMacro", v) {
2082 for _, action := range curMacro {
2083 switch t := action.(type) {
2085 // Insert a character
2086 if v.Cursor.HasSelection() {
2087 v.Cursor.DeleteSelection()
2088 v.Cursor.ResetSelection()
2090 v.Buf.Insert(v.Cursor.Loc, string(t))
2093 for pl := range loadedPlugins {
2094 _, err := Call(pl+".onRune", string(t), v)
2095 if err != nil && !strings.HasPrefix(err.Error(), "function does not exist") {
2099 case func(*View, bool) bool:
2105 return PostActionCall("PlayMacro", v)
2110 // SpawnMultiCursor creates a new multiple cursor at the next occurrence of the current selection or current word
2111 func (v *View) SpawnMultiCursor(usePlugin bool) bool {
2112 spawner := v.Buf.cursors[len(v.Buf.cursors)-1]
2113 // You can only spawn a cursor from the main cursor
2114 if v.Cursor == spawner {
2115 if usePlugin && !PreActionCall("SpawnMultiCursor", v) {
2119 if !spawner.HasSelection() {
2120 spawner.SelectWord()
2126 sel := spawner.GetSelection()
2128 searchStart = spawner.CurSelection[1]
2130 Search(regexp.QuoteMeta(sel), v, true)
2132 for _, cur := range v.Buf.cursors {
2133 if c.Loc == cur.Loc {
2137 v.Buf.cursors = append(v.Buf.cursors, c)
2138 v.Buf.UpdateCursors()
2144 PostActionCall("SpawnMultiCursor", v)
2151 // SpawnMultiCursorSelect adds a cursor at the beginning of each line of a selection
2152 func (v *View) SpawnMultiCursorSelect(usePlugin bool) bool {
2153 if v.Cursor == &v.Buf.Cursor {
2154 if usePlugin && !PreActionCall("SpawnMultiCursorSelect", v) {
2158 // Avoid cases where multiple cursors already exist, that would create problems
2159 if len(v.Buf.cursors) > 1 {
2166 a, b := v.Cursor.CurSelection[0].Y, v.Cursor.CurSelection[1].Y
2168 startLine, endLine = b, a
2170 startLine, endLine = a, b
2173 if v.Cursor.HasSelection() {
2174 v.Cursor.ResetSelection()
2175 v.Cursor.GotoLoc(Loc{0, startLine})
2177 for i := startLine; i <= endLine; i++ {
2181 c.GotoLoc(Loc{0, i})
2182 v.Buf.cursors = append(v.Buf.cursors, c)
2184 v.Buf.MergeCursors()
2185 v.Buf.UpdateCursors()
2191 PostActionCall("SpawnMultiCursorSelect", v)
2194 messenger.Message("Added cursors from selection")
2199 // MouseMultiCursor is a mouse action which puts a new cursor at the mouse position
2200 func (v *View) MouseMultiCursor(usePlugin bool, e *tcell.EventMouse) bool {
2201 if v.Cursor == &v.Buf.Cursor {
2202 if usePlugin && !PreActionCall("SpawnMultiCursorAtMouse", v, e) {
2205 x, y := e.Position()
2206 x -= v.lineNumOffset - v.leftCol + v.x
2207 y += v.Topline - v.y
2213 v.MoveToMouseClick(x, y)
2215 v.Cursor = &v.Buf.Cursor
2217 v.Buf.cursors = append(v.Buf.cursors, c)
2218 v.Buf.MergeCursors()
2219 v.Buf.UpdateCursors()
2222 PostActionCall("SpawnMultiCursorAtMouse", v)
2228 // SkipMultiCursor moves the current multiple cursor to the next available position
2229 func (v *View) SkipMultiCursor(usePlugin bool) bool {
2230 cursor := v.Buf.cursors[len(v.Buf.cursors)-1]
2233 if usePlugin && !PreActionCall("SkipMultiCursor", v) {
2236 sel := cursor.GetSelection()
2238 searchStart = cursor.CurSelection[1]
2240 Search(regexp.QuoteMeta(sel), v, true)
2245 PostActionCall("SkipMultiCursor", v)
2251 // RemoveMultiCursor removes the latest multiple cursor
2252 func (v *View) RemoveMultiCursor(usePlugin bool) bool {
2253 end := len(v.Buf.cursors)
2256 if usePlugin && !PreActionCall("RemoveMultiCursor", v) {
2260 v.Buf.cursors[end-1] = nil
2261 v.Buf.cursors = v.Buf.cursors[:end-1]
2262 v.Buf.UpdateCursors()
2266 return PostActionCall("RemoveMultiCursor", v)
2271 v.RemoveAllMultiCursors(usePlugin)
2276 // RemoveAllMultiCursors removes all cursors except the base cursor
2277 func (v *View) RemoveAllMultiCursors(usePlugin bool) bool {
2279 if usePlugin && !PreActionCall("RemoveAllMultiCursors", v) {
2283 v.Buf.clearCursors()
2287 return PostActionCall("RemoveAllMultiCursors", v)