9 "github.com/yuin/gopher-lua"
10 "github.com/zyedidia/clipboard"
13 // PreActionCall executes the lua pre callback if possible
14 func PreActionCall(funcName string, view *View) bool {
16 for pl := range loadedPlugins {
17 ret, err := Call(pl+".pre"+funcName, view)
18 if err != nil && !strings.HasPrefix(err.Error(), "function does not exist") {
22 if ret == lua.LFalse {
29 // PostActionCall executes the lua plugin callback if possible
30 func PostActionCall(funcName string, view *View) bool {
32 for pl := range loadedPlugins {
33 ret, err := Call(pl+".on"+funcName, view)
34 if err != nil && !strings.HasPrefix(err.Error(), "function does not exist") {
38 if ret == lua.LFalse {
45 func (v *View) deselect(index int) bool {
46 if v.Cursor.HasSelection() {
47 v.Cursor.Loc = v.Cursor.CurSelection[index]
48 v.Cursor.ResetSelection()
54 // Center centers the view on the cursor
55 func (v *View) Center(usePlugin bool) bool {
56 if usePlugin && !PreActionCall("Center", v) {
60 v.Topline = v.Cursor.Y - v.Height/2
61 if v.Topline+v.Height > v.Buf.NumLines {
62 v.Topline = v.Buf.NumLines - v.Height
69 return PostActionCall("Center", v)
74 // CursorUp moves the cursor up
75 func (v *View) CursorUp(usePlugin bool) bool {
76 if usePlugin && !PreActionCall("CursorUp", v) {
84 return PostActionCall("CursorUp", v)
89 // CursorDown moves the cursor down
90 func (v *View) CursorDown(usePlugin bool) bool {
91 if usePlugin && !PreActionCall("CursorDown", v) {
99 return PostActionCall("CursorDown", v)
104 // CursorLeft moves the cursor left
105 func (v *View) CursorLeft(usePlugin bool) bool {
106 if usePlugin && !PreActionCall("CursorLeft", v) {
110 if v.Cursor.HasSelection() {
111 v.Cursor.Loc = v.Cursor.CurSelection[0]
112 v.Cursor.ResetSelection()
118 return PostActionCall("CursorLeft", v)
123 // CursorRight moves the cursor right
124 func (v *View) CursorRight(usePlugin bool) bool {
125 if usePlugin && !PreActionCall("CursorRight", v) {
129 if v.Cursor.HasSelection() {
130 v.Cursor.Loc = v.Cursor.CurSelection[1].Move(-1, v.Buf)
131 v.Cursor.ResetSelection()
137 return PostActionCall("CursorRight", v)
142 // WordRight moves the cursor one word to the right
143 func (v *View) WordRight(usePlugin bool) bool {
144 if usePlugin && !PreActionCall("WordRight", v) {
151 return PostActionCall("WordRight", v)
156 // WordLeft moves the cursor one word to the left
157 func (v *View) WordLeft(usePlugin bool) bool {
158 if usePlugin && !PreActionCall("WordLeft", v) {
165 return PostActionCall("WordLeft", v)
170 // SelectUp selects up one line
171 func (v *View) SelectUp(usePlugin bool) bool {
172 if usePlugin && !PreActionCall("SelectUp", v) {
176 if !v.Cursor.HasSelection() {
177 v.Cursor.OrigSelection[0] = v.Cursor.Loc
180 v.Cursor.SelectTo(v.Cursor.Loc)
183 return PostActionCall("SelectUp", v)
188 // SelectDown selects down one line
189 func (v *View) SelectDown(usePlugin bool) bool {
190 if usePlugin && !PreActionCall("SelectDown", v) {
194 if !v.Cursor.HasSelection() {
195 v.Cursor.OrigSelection[0] = v.Cursor.Loc
198 v.Cursor.SelectTo(v.Cursor.Loc)
201 return PostActionCall("SelectDown", v)
206 // SelectLeft selects the character to the left of the cursor
207 func (v *View) SelectLeft(usePlugin bool) bool {
208 if usePlugin && !PreActionCall("SelectLeft", v) {
213 count := v.Buf.End().Move(-1, v.Buf)
214 if loc.GreaterThan(count) {
217 if !v.Cursor.HasSelection() {
218 v.Cursor.OrigSelection[0] = loc
221 v.Cursor.SelectTo(v.Cursor.Loc)
224 return PostActionCall("SelectLeft", v)
229 // SelectRight selects the character to the right of the cursor
230 func (v *View) SelectRight(usePlugin bool) bool {
231 if usePlugin && !PreActionCall("SelectRight", v) {
236 count := v.Buf.End().Move(-1, v.Buf)
237 if loc.GreaterThan(count) {
240 if !v.Cursor.HasSelection() {
241 v.Cursor.OrigSelection[0] = loc
244 v.Cursor.SelectTo(v.Cursor.Loc)
247 return PostActionCall("SelectRight", v)
252 // SelectWordRight selects the word to the right of the cursor
253 func (v *View) SelectWordRight(usePlugin bool) bool {
254 if usePlugin && !PreActionCall("SelectWordRight", v) {
258 if !v.Cursor.HasSelection() {
259 v.Cursor.OrigSelection[0] = v.Cursor.Loc
262 v.Cursor.SelectTo(v.Cursor.Loc)
265 return PostActionCall("SelectWordRight", v)
270 // SelectWordLeft selects the word to the left of the cursor
271 func (v *View) SelectWordLeft(usePlugin bool) bool {
272 if usePlugin && !PreActionCall("SelectWordLeft", v) {
276 if !v.Cursor.HasSelection() {
277 v.Cursor.OrigSelection[0] = v.Cursor.Loc
280 v.Cursor.SelectTo(v.Cursor.Loc)
283 return PostActionCall("SelectWordLeft", v)
288 // StartOfLine moves the cursor to the start of the line
289 func (v *View) StartOfLine(usePlugin bool) bool {
290 if usePlugin && !PreActionCall("StartOfLine", v) {
299 return PostActionCall("StartOfLine", v)
304 // EndOfLine moves the cursor to the end of the line
305 func (v *View) EndOfLine(usePlugin bool) bool {
306 if usePlugin && !PreActionCall("EndOfLine", v) {
315 return PostActionCall("EndOfLine", v)
320 // SelectToStartOfLine selects to the start of the current line
321 func (v *View) SelectToStartOfLine(usePlugin bool) bool {
322 if usePlugin && !PreActionCall("SelectToStartOfLine", v) {
326 if !v.Cursor.HasSelection() {
327 v.Cursor.OrigSelection[0] = v.Cursor.Loc
330 v.Cursor.SelectTo(v.Cursor.Loc)
333 return PostActionCall("SelectToStartOfLine", v)
338 // SelectToEndOfLine selects to the end of the current line
339 func (v *View) SelectToEndOfLine(usePlugin bool) bool {
340 if usePlugin && !PreActionCall("SelectToEndOfLine", v) {
344 if !v.Cursor.HasSelection() {
345 v.Cursor.OrigSelection[0] = v.Cursor.Loc
348 v.Cursor.SelectTo(v.Cursor.Loc)
351 return PostActionCall("SelectToEndOfLine", v)
356 // CursorStart moves the cursor to the start of the buffer
357 func (v *View) CursorStart(usePlugin bool) bool {
358 if usePlugin && !PreActionCall("CursorStart", v) {
368 return PostActionCall("CursorStart", v)
373 // CursorEnd moves the cursor to the end of the buffer
374 func (v *View) CursorEnd(usePlugin bool) bool {
375 if usePlugin && !PreActionCall("CursorEnd", v) {
381 v.Cursor.Loc = v.Buf.End()
384 return PostActionCall("CursorEnd", v)
389 // SelectToStart selects the text from the cursor to the start of the buffer
390 func (v *View) SelectToStart(usePlugin bool) bool {
391 if usePlugin && !PreActionCall("SelectToStart", v) {
395 if !v.Cursor.HasSelection() {
396 v.Cursor.OrigSelection[0] = v.Cursor.Loc
399 v.Cursor.SelectTo(v.Buf.Start())
402 return PostActionCall("SelectToStart", v)
407 // SelectToEnd selects the text from the cursor to the end of the buffer
408 func (v *View) SelectToEnd(usePlugin bool) bool {
409 if usePlugin && !PreActionCall("SelectToEnd", v) {
413 if !v.Cursor.HasSelection() {
414 v.Cursor.OrigSelection[0] = v.Cursor.Loc
417 v.Cursor.SelectTo(v.Buf.End())
420 return PostActionCall("SelectToEnd", v)
425 // InsertSpace inserts a space
426 func (v *View) InsertSpace(usePlugin bool) bool {
427 if usePlugin && !PreActionCall("InsertSpace", v) {
431 if v.Cursor.HasSelection() {
432 v.Cursor.DeleteSelection()
433 v.Cursor.ResetSelection()
435 v.Buf.Insert(v.Cursor.Loc, " ")
439 return PostActionCall("InsertSpace", v)
444 // InsertNewline inserts a newline plus possible some whitespace if autoindent is on
445 func (v *View) InsertNewline(usePlugin bool) bool {
446 if usePlugin && !PreActionCall("InsertNewline", v) {
451 if v.Cursor.HasSelection() {
452 v.Cursor.DeleteSelection()
453 v.Cursor.ResetSelection()
456 v.Buf.Insert(v.Cursor.Loc, "\n")
457 ws := GetLeadingWhitespace(v.Buf.Line(v.Cursor.Y))
460 if v.Buf.Settings["autoindent"].(bool) {
461 v.Buf.Insert(v.Cursor.Loc, ws)
462 for i := 0; i < len(ws); i++ {
466 // Remove the whitespaces if keepautoindent setting is off
467 if IsSpacesOrTabs(v.Buf.Line(v.Cursor.Y-1)) && !v.Buf.Settings["keepautoindent"].(bool) {
468 line := v.Buf.Line(v.Cursor.Y - 1)
469 v.Buf.Remove(Loc{0, v.Cursor.Y - 1}, Loc{Count(line), v.Cursor.Y - 1})
472 v.Cursor.LastVisualX = v.Cursor.GetVisualX()
475 return PostActionCall("InsertNewline", v)
480 // Backspace deletes the previous character
481 func (v *View) Backspace(usePlugin bool) bool {
482 if usePlugin && !PreActionCall("Backspace", v) {
486 // Delete a character
487 if v.Cursor.HasSelection() {
488 v.Cursor.DeleteSelection()
489 v.Cursor.ResetSelection()
490 } else if v.Cursor.Loc.GreaterThan(v.Buf.Start()) {
491 // We have to do something a bit hacky here because we want to
492 // delete the line by first moving left and then deleting backwards
493 // but the undo redo would place the cursor in the wrong place
494 // So instead we move left, save the position, move back, delete
495 // and restore the position
497 // If the user is using spaces instead of tabs and they are deleting
498 // whitespace at the start of the line, we should delete as if it's a
499 // tab (tabSize number of spaces)
500 lineStart := v.Buf.Line(v.Cursor.Y)[:v.Cursor.X]
501 tabSize := int(v.Buf.Settings["tabsize"].(float64))
502 if v.Buf.Settings["tabstospaces"].(bool) && IsSpaces(lineStart) && len(lineStart) != 0 && len(lineStart)%tabSize == 0 {
504 v.Cursor.Loc = loc.Move(-tabSize, v.Buf)
505 cx, cy := v.Cursor.X, v.Cursor.Y
507 v.Buf.Remove(loc.Move(-tabSize, v.Buf), loc)
508 v.Cursor.X, v.Cursor.Y = cx, cy
511 cx, cy := v.Cursor.X, v.Cursor.Y
514 v.Buf.Remove(loc.Move(-1, v.Buf), loc)
515 v.Cursor.X, v.Cursor.Y = cx, cy
518 v.Cursor.LastVisualX = v.Cursor.GetVisualX()
521 return PostActionCall("Backspace", v)
526 // DeleteWordRight deletes the word to the right of the cursor
527 func (v *View) DeleteWordRight(usePlugin bool) bool {
528 if usePlugin && !PreActionCall("DeleteWordRight", v) {
532 v.SelectWordRight(false)
533 if v.Cursor.HasSelection() {
534 v.Cursor.DeleteSelection()
535 v.Cursor.ResetSelection()
539 return PostActionCall("DeleteWordRight", v)
544 // DeleteWordLeft deletes the word to the left of the cursor
545 func (v *View) DeleteWordLeft(usePlugin bool) bool {
546 if usePlugin && !PreActionCall("DeleteWordLeft", v) {
550 v.SelectWordLeft(false)
551 if v.Cursor.HasSelection() {
552 v.Cursor.DeleteSelection()
553 v.Cursor.ResetSelection()
557 return PostActionCall("DeleteWordLeft", v)
562 // Delete deletes the next character
563 func (v *View) Delete(usePlugin bool) bool {
564 if usePlugin && !PreActionCall("Delete", v) {
568 if v.Cursor.HasSelection() {
569 v.Cursor.DeleteSelection()
570 v.Cursor.ResetSelection()
573 if loc.LessThan(v.Buf.End()) {
574 v.Buf.Remove(loc, loc.Move(1, v.Buf))
579 return PostActionCall("Delete", v)
584 // IndentSelection indents the current selection
585 func (v *View) IndentSelection(usePlugin bool) bool {
586 if usePlugin && !PreActionCall("IndentSelection", v) {
590 if v.Cursor.HasSelection() {
591 start := v.Cursor.CurSelection[0]
592 end := v.Cursor.CurSelection[1]
594 start, end = end, start
598 endY := end.Move(-1, v.Buf).Y
599 endX := end.Move(-1, v.Buf).X
600 for y := startY; y <= endY; y++ {
601 tabsize := len(v.Buf.IndentString())
602 v.Buf.Insert(Loc{0, y}, v.Buf.IndentString())
603 if y == startY && start.X > 0 {
604 v.Cursor.SetSelectionStart(start.Move(tabsize, v.Buf))
607 v.Cursor.SetSelectionEnd(Loc{endX + tabsize + 1, endY})
613 return PostActionCall("IndentSelection", v)
620 // OutdentLine moves the current line back one indentation
621 func (v *View) OutdentLine(usePlugin bool) bool {
622 if usePlugin && !PreActionCall("OutdentLine", v) {
626 if v.Cursor.HasSelection() {
630 for x := 0; x < len(v.Buf.IndentString()); x++ {
631 if len(GetLeadingWhitespace(v.Buf.Line(v.Cursor.Y))) == 0 {
634 v.Buf.Remove(Loc{0, v.Cursor.Y}, Loc{1, v.Cursor.Y})
640 return PostActionCall("OutdentLine", v)
645 // OutdentSelection takes the current selection and moves it back one indent level
646 func (v *View) OutdentSelection(usePlugin bool) bool {
647 if usePlugin && !PreActionCall("OutdentSelection", v) {
651 if v.Cursor.HasSelection() {
652 start := v.Cursor.CurSelection[0]
653 end := v.Cursor.CurSelection[1]
655 start, end = end, start
659 endY := end.Move(-1, v.Buf).Y
660 endX := end.Move(-1, v.Buf).X
661 for y := startY; y <= endY; y++ {
662 for x := 0; x < len(v.Buf.IndentString()); x++ {
663 if len(GetLeadingWhitespace(v.Buf.Line(y))) == 0 {
666 v.Buf.Remove(Loc{0, y}, Loc{1, y})
667 if y == startY && start.X > 0 {
668 v.Cursor.SetSelectionStart(start.Move(-1, v.Buf))
671 v.Cursor.SetSelectionEnd(Loc{endX - x, endY})
678 return PostActionCall("OutdentSelection", v)
685 // InsertTab inserts a tab or spaces
686 func (v *View) InsertTab(usePlugin bool) bool {
687 if usePlugin && !PreActionCall("InsertTab", v) {
691 if v.Cursor.HasSelection() {
695 tabBytes := len(v.Buf.IndentString())
696 bytesUntilIndent := tabBytes - (v.Cursor.GetVisualX() % tabBytes)
697 v.Buf.Insert(v.Cursor.Loc, v.Buf.IndentString()[:bytesUntilIndent])
698 for i := 0; i < bytesUntilIndent; i++ {
703 return PostActionCall("InsertTab", v)
708 // Save the buffer to disk
709 func (v *View) Save(usePlugin bool) bool {
710 if usePlugin && !PreActionCall("Save", v) {
714 if v.Type == vtHelp {
715 // We can't save the help text
718 // If this is an empty buffer, ask for a filename
719 if v.Buf.Path == "" {
724 if strings.HasSuffix(err.Error(), "permission denied") {
725 choice, _ := messenger.YesNoPrompt("Permission denied. Do you want to save this file using sudo? (y,n)")
727 err = v.Buf.SaveWithSudo()
729 messenger.Error(err.Error())
732 messenger.Message("Saved " + v.Buf.Path)
737 messenger.Error(err.Error())
740 messenger.Message("Saved " + v.Buf.Path)
744 return PostActionCall("Save", v)
749 // SaveAs saves the buffer to disk with the given name
750 func (v *View) SaveAs(usePlugin bool) bool {
751 filename, canceled := messenger.Prompt("Filename: ", "", "Save", NoCompletion)
753 // the filename might or might not be quoted, so unquote first then join the strings.
754 filename = strings.Join(SplitCommandArgs(filename), " ")
755 v.Buf.Path = filename
756 v.Buf.name = filename
764 // Find opens a prompt and searches forward for the input
765 func (v *View) Find(usePlugin bool) bool {
766 if usePlugin && !PreActionCall("Find", v) {
771 if v.Cursor.HasSelection() {
772 searchStart = ToCharPos(v.Cursor.CurSelection[1], v.Buf)
773 searchStart = ToCharPos(v.Cursor.CurSelection[1], v.Buf)
774 searchStr = v.Cursor.GetSelection()
776 searchStart = ToCharPos(v.Cursor.Loc, v.Buf)
778 BeginSearch(searchStr)
781 return PostActionCall("Find", v)
786 // FindNext searches forwards for the last used search term
787 func (v *View) FindNext(usePlugin bool) bool {
788 if usePlugin && !PreActionCall("FindNext", v) {
792 if v.Cursor.HasSelection() {
793 searchStart = ToCharPos(v.Cursor.CurSelection[1], v.Buf)
794 // lastSearch = v.Cursor.GetSelection()
796 searchStart = ToCharPos(v.Cursor.Loc, v.Buf)
798 if lastSearch == "" {
801 messenger.Message("Finding: " + lastSearch)
802 Search(lastSearch, v, true)
805 return PostActionCall("FindNext", v)
810 // FindPrevious searches backwards for the last used search term
811 func (v *View) FindPrevious(usePlugin bool) bool {
812 if usePlugin && !PreActionCall("FindPrevious", v) {
816 if v.Cursor.HasSelection() {
817 searchStart = ToCharPos(v.Cursor.CurSelection[0], v.Buf)
819 searchStart = ToCharPos(v.Cursor.Loc, v.Buf)
821 messenger.Message("Finding: " + lastSearch)
822 Search(lastSearch, v, false)
825 return PostActionCall("FindPrevious", v)
830 // Undo undoes the last action
831 func (v *View) Undo(usePlugin bool) bool {
832 if usePlugin && !PreActionCall("Undo", v) {
837 messenger.Message("Undid action")
840 return PostActionCall("Undo", v)
845 // Redo redoes the last action
846 func (v *View) Redo(usePlugin bool) bool {
847 if usePlugin && !PreActionCall("Redo", v) {
852 messenger.Message("Redid action")
855 return PostActionCall("Redo", v)
860 // Copy the selection to the system clipboard
861 func (v *View) Copy(usePlugin bool) bool {
862 if usePlugin && !PreActionCall("Copy", v) {
866 if v.Cursor.HasSelection() {
867 v.Cursor.CopySelection("clipboard")
869 messenger.Message("Copied selection")
873 return PostActionCall("Copy", v)
878 // CutLine cuts the current line to the clipboard
879 func (v *View) CutLine(usePlugin bool) bool {
880 if usePlugin && !PreActionCall("CutLine", v) {
884 v.Cursor.SelectLine()
885 if !v.Cursor.HasSelection() {
888 if v.freshClip == true {
889 if v.Cursor.HasSelection() {
890 if clip, err := clipboard.ReadAll("clipboard"); err != nil {
893 clipboard.WriteAll(clip+v.Cursor.GetSelection(), "clipboard")
896 } else if time.Since(v.lastCutTime)/time.Second > 10*time.Second || v.freshClip == false {
900 v.lastCutTime = time.Now()
901 v.Cursor.DeleteSelection()
902 v.Cursor.ResetSelection()
903 messenger.Message("Cut line")
906 return PostActionCall("CutLine", v)
911 // Cut the selection to the system clipboard
912 func (v *View) Cut(usePlugin bool) bool {
913 if usePlugin && !PreActionCall("Cut", v) {
917 if v.Cursor.HasSelection() {
918 v.Cursor.CopySelection("clipboard")
919 v.Cursor.DeleteSelection()
920 v.Cursor.ResetSelection()
922 messenger.Message("Cut selection")
925 return PostActionCall("Cut", v)
933 // DuplicateLine duplicates the current line or selection
934 func (v *View) DuplicateLine(usePlugin bool) bool {
935 if usePlugin && !PreActionCall("DuplicateLine", v) {
939 if v.Cursor.HasSelection() {
940 v.Buf.Insert(v.Cursor.CurSelection[1], v.Cursor.GetSelection())
943 v.Buf.Insert(v.Cursor.Loc, "\n"+v.Buf.Line(v.Cursor.Y))
947 messenger.Message("Duplicated line")
950 return PostActionCall("DuplicateLine", v)
955 // DeleteLine deletes the current line
956 func (v *View) DeleteLine(usePlugin bool) bool {
957 if usePlugin && !PreActionCall("DeleteLine", v) {
961 v.Cursor.SelectLine()
962 if !v.Cursor.HasSelection() {
965 v.Cursor.DeleteSelection()
966 v.Cursor.ResetSelection()
967 messenger.Message("Deleted line")
970 return PostActionCall("DeleteLine", v)
975 // MoveLinesUp moves up the current line or selected lines if any
976 func (v *View) MoveLinesUp(usePlugin bool) bool {
977 if usePlugin && !PreActionCall("MoveLinesUp", v) {
981 if v.Cursor.HasSelection() {
982 if v.Cursor.CurSelection[0].Y == 0 {
983 messenger.Message("Can not move further up")
987 v.Cursor.CurSelection[0].Y,
988 v.Cursor.CurSelection[1].Y,
991 v.Cursor.CurSelection[0].Y -= 1
992 v.Cursor.CurSelection[1].Y -= 1
993 messenger.Message("Moved up selected line(s)")
995 if v.Cursor.Loc.Y == 0 {
996 messenger.Message("Can not move further up")
1004 messenger.Message("Moved up current line")
1006 v.Buf.IsModified = true
1009 return PostActionCall("MoveLinesUp", v)
1014 // MoveLinesDown moves down the current line or selected lines if any
1015 func (v *View) MoveLinesDown(usePlugin bool) bool {
1016 if usePlugin && !PreActionCall("MoveLinesDown", v) {
1020 if v.Cursor.HasSelection() {
1021 if v.Cursor.CurSelection[1].Y >= len(v.Buf.lines) {
1022 messenger.Message("Can not move further down")
1025 v.Buf.MoveLinesDown(
1026 v.Cursor.CurSelection[0].Y,
1027 v.Cursor.CurSelection[1].Y,
1030 v.Cursor.CurSelection[0].Y += 1
1031 v.Cursor.CurSelection[1].Y += 1
1032 messenger.Message("Moved down selected line(s)")
1034 if v.Cursor.Loc.Y >= len(v.Buf.lines)-1 {
1035 messenger.Message("Can not move further down")
1038 v.Buf.MoveLinesDown(
1043 messenger.Message("Moved down current line")
1045 v.Buf.IsModified = true
1048 return PostActionCall("MoveLinesDown", v)
1053 // Paste whatever is in the system clipboard into the buffer
1054 // Delete and paste if the user has a selection
1055 func (v *View) Paste(usePlugin bool) bool {
1056 if usePlugin && !PreActionCall("Paste", v) {
1060 clip, _ := clipboard.ReadAll("clipboard")
1064 return PostActionCall("Paste", v)
1069 // PastePrimary pastes from the primary clipboard (only use on linux)
1070 func (v *View) PastePrimary(usePlugin bool) bool {
1071 if usePlugin && !PreActionCall("Paste", v) {
1075 clip, _ := clipboard.ReadAll("primary")
1079 return PostActionCall("Paste", v)
1084 // SelectAll selects the entire buffer
1085 func (v *View) SelectAll(usePlugin bool) bool {
1086 if usePlugin && !PreActionCall("SelectAll", v) {
1090 v.Cursor.SetSelectionStart(v.Buf.Start())
1091 v.Cursor.SetSelectionEnd(v.Buf.End())
1092 // Put the cursor at the beginning
1097 return PostActionCall("SelectAll", v)
1102 // OpenFile opens a new file in the buffer
1103 func (v *View) OpenFile(usePlugin bool) bool {
1104 if usePlugin && !PreActionCall("OpenFile", v) {
1109 input, canceled := messenger.Prompt("> ", "open ", "Open", CommandCompletion)
1111 HandleCommand(input)
1113 return PostActionCall("OpenFile", v)
1120 // Start moves the viewport to the start of the buffer
1121 func (v *View) Start(usePlugin bool) bool {
1122 if usePlugin && !PreActionCall("Start", v) {
1129 return PostActionCall("Start", v)
1134 // End moves the viewport to the end of the buffer
1135 func (v *View) End(usePlugin bool) bool {
1136 if usePlugin && !PreActionCall("End", v) {
1140 if v.Height > v.Buf.NumLines {
1143 v.Topline = v.Buf.NumLines - v.Height
1147 return PostActionCall("End", v)
1152 // PageUp scrolls the view up a page
1153 func (v *View) PageUp(usePlugin bool) bool {
1154 if usePlugin && !PreActionCall("PageUp", v) {
1158 if v.Topline > v.Height {
1159 v.ScrollUp(v.Height)
1165 return PostActionCall("PageUp", v)
1170 // PageDown scrolls the view down a page
1171 func (v *View) PageDown(usePlugin bool) bool {
1172 if usePlugin && !PreActionCall("PageDown", v) {
1176 if v.Buf.NumLines-(v.Topline+v.Height) > v.Height {
1177 v.ScrollDown(v.Height)
1178 } else if v.Buf.NumLines >= v.Height {
1179 v.Topline = v.Buf.NumLines - v.Height
1183 return PostActionCall("PageDown", v)
1188 // CursorPageUp places the cursor a page up
1189 func (v *View) CursorPageUp(usePlugin bool) bool {
1190 if usePlugin && !PreActionCall("CursorPageUp", v) {
1196 if v.Cursor.HasSelection() {
1197 v.Cursor.Loc = v.Cursor.CurSelection[0]
1198 v.Cursor.ResetSelection()
1200 v.Cursor.UpN(v.Height)
1203 return PostActionCall("CursorPageUp", v)
1208 // CursorPageDown places the cursor a page up
1209 func (v *View) CursorPageDown(usePlugin bool) bool {
1210 if usePlugin && !PreActionCall("CursorPageDown", v) {
1216 if v.Cursor.HasSelection() {
1217 v.Cursor.Loc = v.Cursor.CurSelection[1]
1218 v.Cursor.ResetSelection()
1220 v.Cursor.DownN(v.Height)
1223 return PostActionCall("CursorPageDown", v)
1228 // HalfPageUp scrolls the view up half a page
1229 func (v *View) HalfPageUp(usePlugin bool) bool {
1230 if usePlugin && !PreActionCall("HalfPageUp", v) {
1234 if v.Topline > v.Height/2 {
1235 v.ScrollUp(v.Height / 2)
1241 return PostActionCall("HalfPageUp", v)
1246 // HalfPageDown scrolls the view down half a page
1247 func (v *View) HalfPageDown(usePlugin bool) bool {
1248 if usePlugin && !PreActionCall("HalfPageDown", v) {
1252 if v.Buf.NumLines-(v.Topline+v.Height) > v.Height/2 {
1253 v.ScrollDown(v.Height / 2)
1255 if v.Buf.NumLines >= v.Height {
1256 v.Topline = v.Buf.NumLines - v.Height
1261 return PostActionCall("HalfPageDown", v)
1266 // ToggleRuler turns line numbers off and on
1267 func (v *View) ToggleRuler(usePlugin bool) bool {
1268 if usePlugin && !PreActionCall("ToggleRuler", v) {
1272 if v.Buf.Settings["ruler"] == false {
1273 v.Buf.Settings["ruler"] = true
1274 messenger.Message("Enabled ruler")
1276 v.Buf.Settings["ruler"] = false
1277 messenger.Message("Disabled ruler")
1281 return PostActionCall("ToggleRuler", v)
1286 // JumpLine jumps to a line and moves the view accordingly.
1287 func (v *View) JumpLine(usePlugin bool) bool {
1288 if usePlugin && !PreActionCall("JumpLine", v) {
1292 // Prompt for line number
1293 linestring, canceled := messenger.Prompt("Jump to line # ", "", "LineNumber", NoCompletion)
1297 lineint, err := strconv.Atoi(linestring)
1298 lineint = lineint - 1 // fix offset
1300 messenger.Error(err) // return errors
1303 // Move cursor and view if possible.
1304 if lineint < v.Buf.NumLines && lineint >= 0 {
1306 v.Cursor.Y = lineint
1309 return PostActionCall("JumpLine", v)
1313 messenger.Error("Only ", v.Buf.NumLines, " lines to jump")
1317 // ClearStatus clears the messenger bar
1318 func (v *View) ClearStatus(usePlugin bool) bool {
1319 if usePlugin && !PreActionCall("ClearStatus", v) {
1323 messenger.Message("")
1326 return PostActionCall("ClearStatus", v)
1331 // ToggleHelp toggles the help screen
1332 func (v *View) ToggleHelp(usePlugin bool) bool {
1333 if usePlugin && !PreActionCall("ToggleHelp", v) {
1337 if v.Type != vtHelp {
1338 // Open the default help
1345 return PostActionCall("ToggleHelp", v)
1350 // ShellMode opens a terminal to run a shell command
1351 func (v *View) ShellMode(usePlugin bool) bool {
1352 if usePlugin && !PreActionCall("ShellMode", v) {
1356 input, canceled := messenger.Prompt("$ ", "", "Shell", NoCompletion)
1358 // The true here is for openTerm to make the command interactive
1359 HandleShellCommand(input, true, true)
1361 return PostActionCall("ShellMode", v)
1367 // CommandMode lets the user enter a command
1368 func (v *View) CommandMode(usePlugin bool) bool {
1369 if usePlugin && !PreActionCall("CommandMode", v) {
1373 input, canceled := messenger.Prompt("> ", "", "Command", CommandCompletion)
1375 HandleCommand(input)
1377 return PostActionCall("CommandMode", v)
1384 // Escape leaves current mode
1385 func (v *View) Escape(usePlugin bool) bool {
1386 // check if user is searching, or the last search is still active
1387 if searching || lastSearch != "" {
1391 // check if a prompt is shown, hide it and don't quit
1392 if messenger.hasPrompt {
1393 messenger.Reset() // FIXME
1400 // Quit this will close the current tab or view that is open
1401 func (v *View) Quit(usePlugin bool) bool {
1402 if usePlugin && !PreActionCall("Quit", v) {
1406 // Make sure not to quit if there are unsaved changes
1409 if len(tabs[curTab].views) > 1 {
1410 v.splitNode.Delete()
1411 tabs[v.TabNum].Cleanup()
1412 tabs[v.TabNum].Resize()
1413 } else if len(tabs) > 1 {
1414 if len(tabs[v.TabNum].views) == 1 {
1415 tabs = tabs[:v.TabNum+copy(tabs[v.TabNum:], tabs[v.TabNum+1:])]
1416 for i, t := range tabs {
1419 if curTab >= len(tabs) {
1423 CurView().ToggleTabbar()
1428 PostActionCall("Quit", v)
1437 return PostActionCall("Quit", v)
1442 // QuitAll quits the whole editor; all splits and tabs
1443 func (v *View) QuitAll(usePlugin bool) bool {
1444 if usePlugin && !PreActionCall("QuitAll", v) {
1449 for _, tab := range tabs {
1450 for _, v := range tab.views {
1458 // only quit if all of the buffers can be closed and the user confirms that they actually want to quit everything
1459 shouldQuit, _ := messenger.YesNoPrompt("Do you want to quit micro (all open files will be closed)?")
1462 for _, tab := range tabs {
1463 for _, v := range tab.views {
1469 PostActionCall("QuitAll", v)
1480 // AddTab adds a new tab with an empty buffer
1481 func (v *View) AddTab(usePlugin bool) bool {
1482 if usePlugin && !PreActionCall("AddTab", v) {
1486 tab := NewTabFromView(NewView(NewBuffer(strings.NewReader(""), "")))
1487 tab.SetNum(len(tabs))
1488 tabs = append(tabs, tab)
1489 curTab = len(tabs) - 1
1491 for _, t := range tabs {
1492 for _, v := range t.views {
1499 return PostActionCall("AddTab", v)
1504 // PreviousTab switches to the previous tab in the tab list
1505 func (v *View) PreviousTab(usePlugin bool) bool {
1506 if usePlugin && !PreActionCall("PreviousTab", v) {
1512 } else if curTab == 0 {
1513 curTab = len(tabs) - 1
1517 return PostActionCall("PreviousTab", v)
1522 // NextTab switches to the next tab in the tab list
1523 func (v *View) NextTab(usePlugin bool) bool {
1524 if usePlugin && !PreActionCall("NextTab", v) {
1528 if curTab < len(tabs)-1 {
1530 } else if curTab == len(tabs)-1 {
1535 return PostActionCall("NextTab", v)
1540 // VSplitBinding opens an empty vertical split
1541 func (v *View) VSplitBinding(usePlugin bool) bool {
1542 if usePlugin && !PreActionCall("VSplit", v) {
1546 v.VSplit(NewBuffer(strings.NewReader(""), ""))
1549 return PostActionCall("VSplit", v)
1554 // HSplitBinding opens an empty horizontal split
1555 func (v *View) HSplitBinding(usePlugin bool) bool {
1556 if usePlugin && !PreActionCall("HSplit", v) {
1560 v.HSplit(NewBuffer(strings.NewReader(""), ""))
1563 return PostActionCall("HSplit", v)
1568 // Unsplit closes all splits in the current tab except the active one
1569 func (v *View) Unsplit(usePlugin bool) bool {
1570 if usePlugin && !PreActionCall("Unsplit", v) {
1574 curView := tabs[curTab].CurView
1575 for i := len(tabs[curTab].views) - 1; i >= 0; i-- {
1576 view := tabs[curTab].views[i]
1577 if view != nil && view.Num != curView {
1579 // messenger.Message("Quit ", view.Buf.Path)
1584 return PostActionCall("Unsplit", v)
1589 // NextSplit changes the view to the next split
1590 func (v *View) NextSplit(usePlugin bool) bool {
1591 if usePlugin && !PreActionCall("NextSplit", v) {
1596 if tab.CurView < len(tab.views)-1 {
1603 return PostActionCall("NextSplit", v)
1608 // PreviousSplit changes the view to the previous split
1609 func (v *View) PreviousSplit(usePlugin bool) bool {
1610 if usePlugin && !PreActionCall("PreviousSplit", v) {
1615 if tab.CurView > 0 {
1618 tab.CurView = len(tab.views) - 1
1622 return PostActionCall("PreviousSplit", v)
1627 var curMacro []interface{}
1628 var recordingMacro bool
1630 // ToggleMacro toggles recording of a macro
1631 func (v *View) ToggleMacro(usePlugin bool) bool {
1632 if usePlugin && !PreActionCall("ToggleMacro", v) {
1636 recordingMacro = !recordingMacro
1639 curMacro = []interface{}{}
1640 messenger.Message("Recording")
1642 messenger.Message("Stopped recording")
1646 return PostActionCall("ToggleMacro", v)
1651 // PlayMacro plays back the most recently recorded macro
1652 func (v *View) PlayMacro(usePlugin bool) bool {
1653 if usePlugin && !PreActionCall("PlayMacro", v) {
1657 for _, action := range curMacro {
1658 switch t := action.(type) {
1660 // Insert a character
1661 if v.Cursor.HasSelection() {
1662 v.Cursor.DeleteSelection()
1663 v.Cursor.ResetSelection()
1665 v.Buf.Insert(v.Cursor.Loc, string(t))
1668 for pl := range loadedPlugins {
1669 _, err := Call(pl+".onRune", string(t), v)
1670 if err != nil && !strings.HasPrefix(err.Error(), "function does not exist") {
1674 case func(*View, bool) bool:
1680 return PostActionCall("PlayMacro", v)
1685 // None is no action