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 if IsSpacesOrTabs(v.Buf.Line(v.Cursor.Y - 1)) {
467 line := v.Buf.Line(v.Cursor.Y - 1)
468 v.Buf.Remove(Loc{0, v.Cursor.Y - 1}, Loc{Count(line), v.Cursor.Y - 1})
471 v.Cursor.LastVisualX = v.Cursor.GetVisualX()
474 return PostActionCall("InsertNewline", v)
479 // Backspace deletes the previous character
480 func (v *View) Backspace(usePlugin bool) bool {
481 if usePlugin && !PreActionCall("Backspace", v) {
485 // Delete a character
486 if v.Cursor.HasSelection() {
487 v.Cursor.DeleteSelection()
488 v.Cursor.ResetSelection()
489 } else if v.Cursor.Loc.GreaterThan(v.Buf.Start()) {
490 // We have to do something a bit hacky here because we want to
491 // delete the line by first moving left and then deleting backwards
492 // but the undo redo would place the cursor in the wrong place
493 // So instead we move left, save the position, move back, delete
494 // and restore the position
496 // If the user is using spaces instead of tabs and they are deleting
497 // whitespace at the start of the line, we should delete as if it's a
498 // tab (tabSize number of spaces)
499 lineStart := v.Buf.Line(v.Cursor.Y)[:v.Cursor.X]
500 tabSize := int(v.Buf.Settings["tabsize"].(float64))
501 if v.Buf.Settings["tabstospaces"].(bool) && IsSpaces(lineStart) && len(lineStart) != 0 && len(lineStart)%tabSize == 0 {
503 v.Cursor.Loc = loc.Move(-tabSize, v.Buf)
504 cx, cy := v.Cursor.X, v.Cursor.Y
506 v.Buf.Remove(loc.Move(-tabSize, v.Buf), loc)
507 v.Cursor.X, v.Cursor.Y = cx, cy
510 cx, cy := v.Cursor.X, v.Cursor.Y
513 v.Buf.Remove(loc.Move(-1, v.Buf), loc)
514 v.Cursor.X, v.Cursor.Y = cx, cy
517 v.Cursor.LastVisualX = v.Cursor.GetVisualX()
520 return PostActionCall("Backspace", v)
525 // DeleteWordRight deletes the word to the right of the cursor
526 func (v *View) DeleteWordRight(usePlugin bool) bool {
527 if usePlugin && !PreActionCall("DeleteWordRight", v) {
531 v.SelectWordRight(false)
532 if v.Cursor.HasSelection() {
533 v.Cursor.DeleteSelection()
534 v.Cursor.ResetSelection()
538 return PostActionCall("DeleteWordRight", v)
543 // DeleteWordLeft deletes the word to the left of the cursor
544 func (v *View) DeleteWordLeft(usePlugin bool) bool {
545 if usePlugin && !PreActionCall("DeleteWordLeft", v) {
549 v.SelectWordLeft(false)
550 if v.Cursor.HasSelection() {
551 v.Cursor.DeleteSelection()
552 v.Cursor.ResetSelection()
556 return PostActionCall("DeleteWordLeft", v)
561 // Delete deletes the next character
562 func (v *View) Delete(usePlugin bool) bool {
563 if usePlugin && !PreActionCall("Delete", v) {
567 if v.Cursor.HasSelection() {
568 v.Cursor.DeleteSelection()
569 v.Cursor.ResetSelection()
572 if loc.LessThan(v.Buf.End()) {
573 v.Buf.Remove(loc, loc.Move(1, v.Buf))
578 return PostActionCall("Delete", v)
583 // IndentSelection indents the current selection
584 func (v *View) IndentSelection(usePlugin bool) bool {
585 if usePlugin && !PreActionCall("IndentSelection", v) {
589 if v.Cursor.HasSelection() {
590 startY := v.Cursor.CurSelection[0].Y
591 endY := v.Cursor.CurSelection[1].Move(-1, v.Buf).Y
592 endX := v.Cursor.CurSelection[1].Move(-1, v.Buf).X
593 for y := startY; y <= endY; y++ {
594 tabsize := len(v.Buf.IndentString())
595 v.Buf.Insert(Loc{0, y}, v.Buf.IndentString())
596 if y == startY && v.Cursor.CurSelection[0].X > 0 {
597 v.Cursor.SetSelectionStart(v.Cursor.CurSelection[0].Move(tabsize, v.Buf))
600 v.Cursor.SetSelectionEnd(Loc{endX + tabsize + 1, endY})
606 return PostActionCall("IndentSelection", v)
613 // OutdentLine moves the current line back one indentation
614 func (v *View) OutdentLine(usePlugin bool) bool {
615 if usePlugin && !PreActionCall("OutdentLine", v) {
619 if v.Cursor.HasSelection() {
623 for x := 0; x < len(v.Buf.IndentString()); x++ {
624 if len(GetLeadingWhitespace(v.Buf.Line(v.Cursor.Y))) == 0 {
627 v.Buf.Remove(Loc{0, v.Cursor.Y}, Loc{1, v.Cursor.Y})
633 return PostActionCall("OutdentLine", v)
638 // OutdentSelection takes the current selection and moves it back one indent level
639 func (v *View) OutdentSelection(usePlugin bool) bool {
640 if usePlugin && !PreActionCall("OutdentSelection", v) {
644 if v.Cursor.HasSelection() {
645 startY := v.Cursor.CurSelection[0].Y
646 endY := v.Cursor.CurSelection[1].Move(-1, v.Buf).Y
647 endX := v.Cursor.CurSelection[1].Move(-1, v.Buf).X
648 for y := startY; y <= endY; y++ {
649 for x := 0; x < len(v.Buf.IndentString()); x++ {
650 if len(GetLeadingWhitespace(v.Buf.Line(y))) == 0 {
653 v.Buf.Remove(Loc{0, y}, Loc{1, y})
654 if y == startY && v.Cursor.CurSelection[0].X > 0 {
655 v.Cursor.SetSelectionStart(v.Cursor.CurSelection[0].Move(-1, v.Buf))
658 v.Cursor.SetSelectionEnd(Loc{endX - x, endY})
665 return PostActionCall("OutdentSelection", v)
672 // InsertTab inserts a tab or spaces
673 func (v *View) InsertTab(usePlugin bool) bool {
674 if usePlugin && !PreActionCall("InsertTab", v) {
678 if v.Cursor.HasSelection() {
682 tabBytes := len(v.Buf.IndentString())
683 bytesUntilIndent := tabBytes - (v.Cursor.GetVisualX() % tabBytes)
684 v.Buf.Insert(v.Cursor.Loc, v.Buf.IndentString()[:bytesUntilIndent])
685 for i := 0; i < bytesUntilIndent; i++ {
690 return PostActionCall("InsertTab", v)
695 // Save the buffer to disk
696 func (v *View) Save(usePlugin bool) bool {
697 if usePlugin && !PreActionCall("Save", v) {
701 if v.Type == vtHelp {
702 // We can't save the help text
705 // If this is an empty buffer, ask for a filename
706 if v.Buf.Path == "" {
711 if strings.HasSuffix(err.Error(), "permission denied") {
712 choice, _ := messenger.YesNoPrompt("Permission denied. Do you want to save this file using sudo? (y,n)")
714 err = v.Buf.SaveWithSudo()
716 messenger.Error(err.Error())
719 messenger.Message("Saved " + v.Buf.Path)
724 messenger.Error(err.Error())
727 messenger.Message("Saved " + v.Buf.Path)
731 return PostActionCall("Save", v)
736 // SaveAs saves the buffer to disk with the given name
737 func (v *View) SaveAs(usePlugin bool) bool {
738 filename, canceled := messenger.Prompt("Filename: ", "", "Save", NoCompletion)
740 // the filename might or might not be quoted, so unquote first then join the strings.
741 filename = strings.Join(SplitCommandArgs(filename), " ")
742 v.Buf.Path = filename
743 v.Buf.name = filename
751 // Find opens a prompt and searches forward for the input
752 func (v *View) Find(usePlugin bool) bool {
753 if usePlugin && !PreActionCall("Find", v) {
758 if v.Cursor.HasSelection() {
759 searchStart = ToCharPos(v.Cursor.CurSelection[1], v.Buf)
760 searchStart = ToCharPos(v.Cursor.CurSelection[1], v.Buf)
761 searchStr = v.Cursor.GetSelection()
763 searchStart = ToCharPos(v.Cursor.Loc, v.Buf)
765 BeginSearch(searchStr)
768 return PostActionCall("Find", v)
773 // FindNext searches forwards for the last used search term
774 func (v *View) FindNext(usePlugin bool) bool {
775 if usePlugin && !PreActionCall("FindNext", v) {
779 if v.Cursor.HasSelection() {
780 searchStart = ToCharPos(v.Cursor.CurSelection[1], v.Buf)
781 lastSearch = v.Cursor.GetSelection()
783 searchStart = ToCharPos(v.Cursor.Loc, v.Buf)
785 if lastSearch == "" {
788 messenger.Message("Finding: " + lastSearch)
789 Search(lastSearch, v, true)
792 return PostActionCall("FindNext", v)
797 // FindPrevious searches backwards for the last used search term
798 func (v *View) FindPrevious(usePlugin bool) bool {
799 if usePlugin && !PreActionCall("FindPrevious", v) {
803 if v.Cursor.HasSelection() {
804 searchStart = ToCharPos(v.Cursor.CurSelection[0], v.Buf)
806 searchStart = ToCharPos(v.Cursor.Loc, v.Buf)
808 messenger.Message("Finding: " + lastSearch)
809 Search(lastSearch, v, false)
812 return PostActionCall("FindPrevious", v)
817 // Undo undoes the last action
818 func (v *View) Undo(usePlugin bool) bool {
819 if usePlugin && !PreActionCall("Undo", v) {
824 messenger.Message("Undid action")
827 return PostActionCall("Undo", v)
832 // Redo redoes the last action
833 func (v *View) Redo(usePlugin bool) bool {
834 if usePlugin && !PreActionCall("Redo", v) {
839 messenger.Message("Redid action")
842 return PostActionCall("Redo", v)
847 // Copy the selection to the system clipboard
848 func (v *View) Copy(usePlugin bool) bool {
849 if usePlugin && !PreActionCall("Copy", v) {
853 if v.Cursor.HasSelection() {
854 clipboard.WriteAll(v.Cursor.GetSelection(), "clipboard")
856 messenger.Message("Copied selection")
860 return PostActionCall("Copy", v)
865 // CutLine cuts the current line to the clipboard
866 func (v *View) CutLine(usePlugin bool) bool {
867 if usePlugin && !PreActionCall("CutLine", v) {
871 v.Cursor.SelectLine()
872 if !v.Cursor.HasSelection() {
875 if v.freshClip == true {
876 if v.Cursor.HasSelection() {
877 if clip, err := clipboard.ReadAll("clipboard"); err != nil {
880 clipboard.WriteAll(clip+v.Cursor.GetSelection(), "clipboard")
883 } else if time.Since(v.lastCutTime)/time.Second > 10*time.Second || v.freshClip == false {
887 v.lastCutTime = time.Now()
888 v.Cursor.DeleteSelection()
889 v.Cursor.ResetSelection()
890 messenger.Message("Cut line")
893 return PostActionCall("CutLine", v)
898 // Cut the selection to the system clipboard
899 func (v *View) Cut(usePlugin bool) bool {
900 if usePlugin && !PreActionCall("Cut", v) {
904 if v.Cursor.HasSelection() {
905 clipboard.WriteAll(v.Cursor.GetSelection(), "clipboard")
906 v.Cursor.DeleteSelection()
907 v.Cursor.ResetSelection()
909 messenger.Message("Cut selection")
912 return PostActionCall("Cut", v)
920 // DuplicateLine duplicates the current line or selection
921 func (v *View) DuplicateLine(usePlugin bool) bool {
922 if usePlugin && !PreActionCall("DuplicateLine", v) {
926 if v.Cursor.HasSelection() {
927 v.Buf.Insert(v.Cursor.CurSelection[1], v.Cursor.GetSelection())
930 v.Buf.Insert(v.Cursor.Loc, "\n"+v.Buf.Line(v.Cursor.Y))
934 messenger.Message("Duplicated line")
937 return PostActionCall("DuplicateLine", v)
942 // DeleteLine deletes the current line
943 func (v *View) DeleteLine(usePlugin bool) bool {
944 if usePlugin && !PreActionCall("DeleteLine", v) {
948 v.Cursor.SelectLine()
949 if !v.Cursor.HasSelection() {
952 v.Cursor.DeleteSelection()
953 v.Cursor.ResetSelection()
954 messenger.Message("Deleted line")
957 return PostActionCall("DeleteLine", v)
962 // MoveLinesUp moves up the current line or selected lines if any
963 func (v *View) MoveLinesUp(usePlugin bool) bool {
964 if usePlugin && !PreActionCall("MoveLinesUp", v) {
968 if v.Cursor.HasSelection() {
969 if v.Cursor.CurSelection[0].Y == 0 {
970 messenger.Message("Can not move further up")
974 v.Cursor.CurSelection[0].Y,
975 v.Cursor.CurSelection[1].Y,
978 v.Cursor.CurSelection[0].Y -= 1
979 v.Cursor.CurSelection[1].Y -= 1
980 messenger.Message("Moved up selected line(s)")
982 if v.Cursor.Loc.Y == 0 {
983 messenger.Message("Can not move further up")
991 messenger.Message("Moved up current line")
993 v.Buf.IsModified = true
996 return PostActionCall("MoveLinesUp", v)
1001 // MoveLinesDown moves down the current line or selected lines if any
1002 func (v *View) MoveLinesDown(usePlugin bool) bool {
1003 if usePlugin && !PreActionCall("MoveLinesDown", v) {
1007 if v.Cursor.HasSelection() {
1008 if v.Cursor.CurSelection[1].Y >= len(v.Buf.lines) {
1009 messenger.Message("Can not move further down")
1012 v.Buf.MoveLinesDown(
1013 v.Cursor.CurSelection[0].Y,
1014 v.Cursor.CurSelection[1].Y,
1017 v.Cursor.CurSelection[0].Y += 1
1018 v.Cursor.CurSelection[1].Y += 1
1019 messenger.Message("Moved down selected line(s)")
1021 if v.Cursor.Loc.Y >= len(v.Buf.lines)-1 {
1022 messenger.Message("Can not move further down")
1025 v.Buf.MoveLinesDown(
1030 messenger.Message("Moved down current line")
1032 v.Buf.IsModified = true
1035 return PostActionCall("MoveLinesDown", v)
1040 // Paste whatever is in the system clipboard into the buffer
1041 // Delete and paste if the user has a selection
1042 func (v *View) Paste(usePlugin bool) bool {
1043 if usePlugin && !PreActionCall("Paste", v) {
1047 clip, _ := clipboard.ReadAll("clipboard")
1051 return PostActionCall("Paste", v)
1056 // PastePrimary pastes from the primary clipboard (only use on linux)
1057 func (v *View) PastePrimary(usePlugin bool) bool {
1058 if usePlugin && !PreActionCall("Paste", v) {
1062 clip, _ := clipboard.ReadAll("primary")
1066 return PostActionCall("Paste", v)
1071 // SelectAll selects the entire buffer
1072 func (v *View) SelectAll(usePlugin bool) bool {
1073 if usePlugin && !PreActionCall("SelectAll", v) {
1077 v.Cursor.SetSelectionStart(v.Buf.Start())
1078 v.Cursor.SetSelectionEnd(v.Buf.End())
1079 // Put the cursor at the beginning
1084 return PostActionCall("SelectAll", v)
1089 // OpenFile opens a new file in the buffer
1090 func (v *View) OpenFile(usePlugin bool) bool {
1091 if usePlugin && !PreActionCall("OpenFile", v) {
1096 input, canceled := messenger.Prompt("> ", "open ", "Open", CommandCompletion)
1098 HandleCommand(input)
1100 return PostActionCall("OpenFile", v)
1107 // Start moves the viewport to the start of the buffer
1108 func (v *View) Start(usePlugin bool) bool {
1109 if usePlugin && !PreActionCall("Start", v) {
1116 return PostActionCall("Start", v)
1121 // End moves the viewport to the end of the buffer
1122 func (v *View) End(usePlugin bool) bool {
1123 if usePlugin && !PreActionCall("End", v) {
1127 if v.Height > v.Buf.NumLines {
1130 v.Topline = v.Buf.NumLines - v.Height
1134 return PostActionCall("End", v)
1139 // PageUp scrolls the view up a page
1140 func (v *View) PageUp(usePlugin bool) bool {
1141 if usePlugin && !PreActionCall("PageUp", v) {
1145 if v.Topline > v.Height {
1146 v.ScrollUp(v.Height)
1152 return PostActionCall("PageUp", v)
1157 // PageDown scrolls the view down a page
1158 func (v *View) PageDown(usePlugin bool) bool {
1159 if usePlugin && !PreActionCall("PageDown", v) {
1163 if v.Buf.NumLines-(v.Topline+v.Height) > v.Height {
1164 v.ScrollDown(v.Height)
1165 } else if v.Buf.NumLines >= v.Height {
1166 v.Topline = v.Buf.NumLines - v.Height
1170 return PostActionCall("PageDown", v)
1175 // CursorPageUp places the cursor a page up
1176 func (v *View) CursorPageUp(usePlugin bool) bool {
1177 if usePlugin && !PreActionCall("CursorPageUp", v) {
1183 if v.Cursor.HasSelection() {
1184 v.Cursor.Loc = v.Cursor.CurSelection[0]
1185 v.Cursor.ResetSelection()
1187 v.Cursor.UpN(v.Height)
1190 return PostActionCall("CursorPageUp", v)
1195 // CursorPageDown places the cursor a page up
1196 func (v *View) CursorPageDown(usePlugin bool) bool {
1197 if usePlugin && !PreActionCall("CursorPageDown", v) {
1203 if v.Cursor.HasSelection() {
1204 v.Cursor.Loc = v.Cursor.CurSelection[1]
1205 v.Cursor.ResetSelection()
1207 v.Cursor.DownN(v.Height)
1210 return PostActionCall("CursorPageDown", v)
1215 // HalfPageUp scrolls the view up half a page
1216 func (v *View) HalfPageUp(usePlugin bool) bool {
1217 if usePlugin && !PreActionCall("HalfPageUp", v) {
1221 if v.Topline > v.Height/2 {
1222 v.ScrollUp(v.Height / 2)
1228 return PostActionCall("HalfPageUp", v)
1233 // HalfPageDown scrolls the view down half a page
1234 func (v *View) HalfPageDown(usePlugin bool) bool {
1235 if usePlugin && !PreActionCall("HalfPageDown", v) {
1239 if v.Buf.NumLines-(v.Topline+v.Height) > v.Height/2 {
1240 v.ScrollDown(v.Height / 2)
1242 if v.Buf.NumLines >= v.Height {
1243 v.Topline = v.Buf.NumLines - v.Height
1248 return PostActionCall("HalfPageDown", v)
1253 // ToggleRuler turns line numbers off and on
1254 func (v *View) ToggleRuler(usePlugin bool) bool {
1255 if usePlugin && !PreActionCall("ToggleRuler", v) {
1259 if v.Buf.Settings["ruler"] == false {
1260 v.Buf.Settings["ruler"] = true
1261 messenger.Message("Enabled ruler")
1263 v.Buf.Settings["ruler"] = false
1264 messenger.Message("Disabled ruler")
1268 return PostActionCall("ToggleRuler", v)
1273 // JumpLine jumps to a line and moves the view accordingly.
1274 func (v *View) JumpLine(usePlugin bool) bool {
1275 if usePlugin && !PreActionCall("JumpLine", v) {
1279 // Prompt for line number
1280 linestring, canceled := messenger.Prompt("Jump to line # ", "", "LineNumber", NoCompletion)
1284 lineint, err := strconv.Atoi(linestring)
1285 lineint = lineint - 1 // fix offset
1287 messenger.Error(err) // return errors
1290 // Move cursor and view if possible.
1291 if lineint < v.Buf.NumLines && lineint >= 0 {
1293 v.Cursor.Y = lineint
1296 return PostActionCall("JumpLine", v)
1300 messenger.Error("Only ", v.Buf.NumLines, " lines to jump")
1304 // ClearStatus clears the messenger bar
1305 func (v *View) ClearStatus(usePlugin bool) bool {
1306 if usePlugin && !PreActionCall("ClearStatus", v) {
1310 messenger.Message("")
1313 return PostActionCall("ClearStatus", v)
1318 // ToggleHelp toggles the help screen
1319 func (v *View) ToggleHelp(usePlugin bool) bool {
1320 if usePlugin && !PreActionCall("ToggleHelp", v) {
1324 if v.Type != vtHelp {
1325 // Open the default help
1332 return PostActionCall("ToggleHelp", v)
1337 // ShellMode opens a terminal to run a shell command
1338 func (v *View) ShellMode(usePlugin bool) bool {
1339 if usePlugin && !PreActionCall("ShellMode", v) {
1343 input, canceled := messenger.Prompt("$ ", "", "Shell", NoCompletion)
1345 // The true here is for openTerm to make the command interactive
1346 HandleShellCommand(input, true, true)
1348 return PostActionCall("ShellMode", v)
1354 // CommandMode lets the user enter a command
1355 func (v *View) CommandMode(usePlugin bool) bool {
1356 if usePlugin && !PreActionCall("CommandMode", v) {
1360 input, canceled := messenger.Prompt("> ", "", "Command", CommandCompletion)
1362 HandleCommand(input)
1364 return PostActionCall("CommandMode", v)
1371 // Escape leaves current mode / quits the editor
1372 func (v *View) Escape(usePlugin bool) bool {
1373 // check if user is searching, or the last search is still active
1374 if searching || lastSearch != "" {
1378 // check if a prompt is shown, hide it and don't quit
1379 if messenger.hasPrompt {
1380 messenger.Reset() // FIXME
1383 return v.Quit(usePlugin)
1386 // Quit quits the editor
1387 // This behavior needs to be changed and should really only quit the editor if this
1389 // However, since micro only supports one view for now, it doesn't really matter
1390 func (v *View) Quit(usePlugin bool) bool {
1391 if usePlugin && !PreActionCall("Quit", v) {
1395 // Make sure not to quit if there are unsaved changes
1398 if len(tabs[curTab].views) > 1 {
1399 v.splitNode.Delete()
1400 tabs[v.TabNum].Cleanup()
1401 tabs[v.TabNum].Resize()
1402 } else if len(tabs) > 1 {
1403 if len(tabs[v.TabNum].views) == 1 {
1404 tabs = tabs[:v.TabNum+copy(tabs[v.TabNum:], tabs[v.TabNum+1:])]
1405 for i, t := range tabs {
1408 if curTab >= len(tabs) {
1412 // CurView().Resize(screen.Size())
1413 CurView().ToggleTabbar()
1414 CurView().matches = Match(CurView())
1419 PostActionCall("Quit", v)
1428 return PostActionCall("Quit", v)
1433 // QuitAll quits the whole editor; all splits and tabs
1434 func (v *View) QuitAll(usePlugin bool) bool {
1435 if usePlugin && !PreActionCall("QuitAll", v) {
1440 for _, tab := range tabs {
1441 for _, v := range tab.views {
1449 for _, tab := range tabs {
1450 for _, v := range tab.views {
1456 PostActionCall("QuitAll", v)
1466 // AddTab adds a new tab with an empty buffer
1467 func (v *View) AddTab(usePlugin bool) bool {
1468 if usePlugin && !PreActionCall("AddTab", v) {
1472 tab := NewTabFromView(NewView(NewBuffer(strings.NewReader(""), "")))
1473 tab.SetNum(len(tabs))
1474 tabs = append(tabs, tab)
1477 for _, t := range tabs {
1478 for _, v := range t.views {
1485 return PostActionCall("AddTab", v)
1490 // PreviousTab switches to the previous tab in the tab list
1491 func (v *View) PreviousTab(usePlugin bool) bool {
1492 if usePlugin && !PreActionCall("PreviousTab", v) {
1498 } else if curTab == 0 {
1499 curTab = len(tabs) - 1
1503 return PostActionCall("PreviousTab", v)
1508 // NextTab switches to the next tab in the tab list
1509 func (v *View) NextTab(usePlugin bool) bool {
1510 if usePlugin && !PreActionCall("NextTab", v) {
1514 if curTab < len(tabs)-1 {
1516 } else if curTab == len(tabs)-1 {
1521 return PostActionCall("NextTab", v)
1526 // VSplitBinding opens an empty vertical split
1527 func (v *View) VSplitBinding(usePlugin bool) bool {
1528 if usePlugin && !PreActionCall("VSplit", v) {
1532 v.VSplit(NewBuffer(strings.NewReader(""), ""))
1535 return PostActionCall("VSplit", v)
1540 // HSplitBinding opens an empty horizontal split
1541 func (v *View) HSplitBinding(usePlugin bool) bool {
1542 if usePlugin && !PreActionCall("HSplit", v) {
1546 v.HSplit(NewBuffer(strings.NewReader(""), ""))
1549 return PostActionCall("HSplit", v)
1554 // Unsplit closes all splits in the current tab except the active one
1555 func (v *View) Unsplit(usePlugin bool) bool {
1556 if usePlugin && !PreActionCall("Unsplit", v) {
1560 curView := tabs[curTab].CurView
1561 for i := len(tabs[curTab].views) - 1; i >= 0; i-- {
1562 view := tabs[curTab].views[i]
1563 if view != nil && view.Num != curView {
1565 // messenger.Message("Quit ", view.Buf.Path)
1570 return PostActionCall("Unsplit", v)
1575 // NextSplit changes the view to the next split
1576 func (v *View) NextSplit(usePlugin bool) bool {
1577 if usePlugin && !PreActionCall("NextSplit", v) {
1582 if tab.CurView < len(tab.views)-1 {
1589 return PostActionCall("NextSplit", v)
1594 // PreviousSplit changes the view to the previous split
1595 func (v *View) PreviousSplit(usePlugin bool) bool {
1596 if usePlugin && !PreActionCall("PreviousSplit", v) {
1601 if tab.CurView > 0 {
1604 tab.CurView = len(tab.views) - 1
1608 return PostActionCall("PreviousSplit", v)
1613 var curMacro []interface{}
1614 var recordingMacro bool
1616 // ToggleMacro toggles recording of a macro
1617 func (v *View) ToggleMacro(usePlugin bool) bool {
1618 if usePlugin && !PreActionCall("ToggleMacro", v) {
1622 recordingMacro = !recordingMacro
1625 curMacro = []interface{}{}
1626 messenger.Message("Recording")
1628 messenger.Message("Stopped recording")
1632 return PostActionCall("ToggleMacro", v)
1637 // PlayMacro plays back the most recently recorded macro
1638 func (v *View) PlayMacro(usePlugin bool) bool {
1639 if usePlugin && !PreActionCall("PlayMacro", v) {
1643 for _, action := range curMacro {
1644 switch t := action.(type) {
1646 // Insert a character
1647 if v.Cursor.HasSelection() {
1648 v.Cursor.DeleteSelection()
1649 v.Cursor.ResetSelection()
1651 v.Buf.Insert(v.Cursor.Loc, string(t))
1654 for _, pl := range loadedPlugins {
1655 _, err := Call(pl+".onRune", string(t), v)
1656 if err != nil && !strings.HasPrefix(err.Error(), "function does not exist") {
1660 case func(*View, bool) bool:
1666 return PostActionCall("PlayMacro", v)
1671 // None is no action