10 "github.com/mitchellh/go-homedir"
11 "github.com/yuin/gopher-lua"
12 "github.com/zyedidia/clipboard"
15 // PreActionCall executes the lua pre callback if possible
16 func PreActionCall(funcName string) bool {
18 for _, pl := range loadedPlugins {
19 ret, err := Call(pl+".pre"+funcName, nil)
20 if err != nil && !strings.HasPrefix(err.Error(), "function does not exist") {
24 if ret == lua.LFalse {
31 // PostActionCall executes the lua plugin callback if possible
32 func PostActionCall(funcName string) bool {
34 for _, pl := range loadedPlugins {
35 ret, err := Call(pl+".on"+funcName, nil)
36 if err != nil && !strings.HasPrefix(err.Error(), "function does not exist") {
40 if ret == lua.LFalse {
47 // CursorUp moves the cursor up
48 func (v *View) CursorUp(usePlugin bool) bool {
49 if usePlugin && !PreActionCall("CursorUp") {
53 if v.Cursor.HasSelection() {
54 v.Cursor.Loc = v.Cursor.CurSelection[0]
55 v.Cursor.ResetSelection()
60 return PostActionCall("CursorUp")
65 // CursorDown moves the cursor down
66 func (v *View) CursorDown(usePlugin bool) bool {
67 if usePlugin && !PreActionCall("CursorDown") {
71 if v.Cursor.HasSelection() {
72 v.Cursor.Loc = v.Cursor.CurSelection[1]
73 v.Cursor.ResetSelection()
78 return PostActionCall("CursorDown")
83 // CursorLeft moves the cursor left
84 func (v *View) CursorLeft(usePlugin bool) bool {
85 if usePlugin && !PreActionCall("CursorLeft") {
89 if v.Cursor.HasSelection() {
90 v.Cursor.Loc = v.Cursor.CurSelection[0]
91 v.Cursor.ResetSelection()
97 return PostActionCall("CursorLeft")
102 // CursorRight moves the cursor right
103 func (v *View) CursorRight(usePlugin bool) bool {
104 if usePlugin && !PreActionCall("CursorRight") {
108 if v.Cursor.HasSelection() {
109 v.Cursor.Loc = v.Cursor.CurSelection[1].Move(-1, v.Buf)
110 v.Cursor.ResetSelection()
116 return PostActionCall("CursorRight")
121 // WordRight moves the cursor one word to the right
122 func (v *View) WordRight(usePlugin bool) bool {
123 if usePlugin && !PreActionCall("WordRight") {
130 return PostActionCall("WordRight")
135 // WordLeft moves the cursor one word to the left
136 func (v *View) WordLeft(usePlugin bool) bool {
137 if usePlugin && !PreActionCall("WordLeft") {
144 return PostActionCall("WordLeft")
149 // SelectUp selects up one line
150 func (v *View) SelectUp(usePlugin bool) bool {
151 if usePlugin && !PreActionCall("SelectUp") {
155 if !v.Cursor.HasSelection() {
156 v.Cursor.OrigSelection[0] = v.Cursor.Loc
159 v.Cursor.SelectTo(v.Cursor.Loc)
162 return PostActionCall("SelectUp")
167 // SelectDown selects down one line
168 func (v *View) SelectDown(usePlugin bool) bool {
169 if usePlugin && !PreActionCall("SelectDown") {
173 if !v.Cursor.HasSelection() {
174 v.Cursor.OrigSelection[0] = v.Cursor.Loc
177 v.Cursor.SelectTo(v.Cursor.Loc)
180 return PostActionCall("SelectDown")
185 // SelectLeft selects the character to the left of the cursor
186 func (v *View) SelectLeft(usePlugin bool) bool {
187 if usePlugin && !PreActionCall("SelectLeft") {
192 count := v.Buf.End().Move(-1, v.Buf)
193 if loc.GreaterThan(count) {
196 if !v.Cursor.HasSelection() {
197 v.Cursor.OrigSelection[0] = loc
200 v.Cursor.SelectTo(v.Cursor.Loc)
203 return PostActionCall("SelectLeft")
208 // SelectRight selects the character to the right of the cursor
209 func (v *View) SelectRight(usePlugin bool) bool {
210 if usePlugin && !PreActionCall("SelectRight") {
215 count := v.Buf.End().Move(-1, v.Buf)
216 if loc.GreaterThan(count) {
219 if !v.Cursor.HasSelection() {
220 v.Cursor.OrigSelection[0] = loc
223 v.Cursor.SelectTo(v.Cursor.Loc)
226 return PostActionCall("SelectRight")
231 // SelectWordRight selects the word to the right of the cursor
232 func (v *View) SelectWordRight(usePlugin bool) bool {
233 if usePlugin && !PreActionCall("SelectWordRight") {
237 if !v.Cursor.HasSelection() {
238 v.Cursor.OrigSelection[0] = v.Cursor.Loc
241 v.Cursor.SelectTo(v.Cursor.Loc)
244 return PostActionCall("SelectWordRight")
249 // SelectWordLeft selects the word to the left of the cursor
250 func (v *View) SelectWordLeft(usePlugin bool) bool {
251 if usePlugin && !PreActionCall("SelectWordLeft") {
255 if !v.Cursor.HasSelection() {
256 v.Cursor.OrigSelection[0] = v.Cursor.Loc
259 v.Cursor.SelectTo(v.Cursor.Loc)
262 return PostActionCall("SelectWordLeft")
267 // StartOfLine moves the cursor to the start of the line
268 func (v *View) StartOfLine(usePlugin bool) bool {
269 if usePlugin && !PreActionCall("StartOfLine") {
276 return PostActionCall("StartOfLine")
281 // EndOfLine moves the cursor to the end of the line
282 func (v *View) EndOfLine(usePlugin bool) bool {
283 if usePlugin && !PreActionCall("EndOfLine") {
290 return PostActionCall("EndOfLine")
295 // SelectToStartOfLine selects to the start of the current line
296 func (v *View) SelectToStartOfLine(usePlugin bool) bool {
297 if usePlugin && !PreActionCall("SelectToStartOfLine") {
301 if !v.Cursor.HasSelection() {
302 v.Cursor.OrigSelection[0] = v.Cursor.Loc
305 v.Cursor.SelectTo(v.Cursor.Loc)
308 return PostActionCall("SelectToStartOfLine")
313 // SelectToEndOfLine selects to the end of the current line
314 func (v *View) SelectToEndOfLine(usePlugin bool) bool {
315 if usePlugin && !PreActionCall("SelectToEndOfLine") {
319 if !v.Cursor.HasSelection() {
320 v.Cursor.OrigSelection[0] = v.Cursor.Loc
323 v.Cursor.SelectTo(v.Cursor.Loc)
326 return PostActionCall("SelectToEndOfLine")
331 // CursorStart moves the cursor to the start of the buffer
332 func (v *View) CursorStart(usePlugin bool) bool {
333 if usePlugin && !PreActionCall("CursorStart") {
341 return PostActionCall("CursorStart")
346 // CursorEnd moves the cursor to the end of the buffer
347 func (v *View) CursorEnd(usePlugin bool) bool {
348 if usePlugin && !PreActionCall("CursorEnd") {
352 v.Cursor.Loc = v.Buf.End()
355 return PostActionCall("CursorEnd")
360 // SelectToStart selects the text from the cursor to the start of the buffer
361 func (v *View) SelectToStart(usePlugin bool) bool {
362 if usePlugin && !PreActionCall("SelectToStart") {
366 if !v.Cursor.HasSelection() {
367 v.Cursor.OrigSelection[0] = v.Cursor.Loc
370 v.Cursor.SelectTo(v.Buf.Start())
373 return PostActionCall("SelectToStart")
378 // SelectToEnd selects the text from the cursor to the end of the buffer
379 func (v *View) SelectToEnd(usePlugin bool) bool {
380 if usePlugin && !PreActionCall("SelectToEnd") {
384 if !v.Cursor.HasSelection() {
385 v.Cursor.OrigSelection[0] = v.Cursor.Loc
388 v.Cursor.SelectTo(v.Buf.End())
391 return PostActionCall("SelectToEnd")
396 // InsertSpace inserts a space
397 func (v *View) InsertSpace(usePlugin bool) bool {
398 if usePlugin && !PreActionCall("InsertSpace") {
402 if v.Cursor.HasSelection() {
403 v.Cursor.DeleteSelection()
404 v.Cursor.ResetSelection()
406 v.Buf.Insert(v.Cursor.Loc, " ")
410 return PostActionCall("InsertSpace")
415 // InsertNewline inserts a newline plus possible some whitespace if autoindent is on
416 func (v *View) InsertNewline(usePlugin bool) bool {
417 if usePlugin && !PreActionCall("InsertNewline") {
422 if v.Cursor.HasSelection() {
423 v.Cursor.DeleteSelection()
424 v.Cursor.ResetSelection()
427 v.Buf.Insert(v.Cursor.Loc, "\n")
428 ws := GetLeadingWhitespace(v.Buf.Line(v.Cursor.Y))
431 if settings["autoindent"].(bool) {
432 v.Buf.Insert(v.Cursor.Loc, ws)
433 for i := 0; i < len(ws); i++ {
437 if IsSpacesOrTabs(v.Buf.Line(v.Cursor.Y - 1)) {
438 line := v.Buf.Line(v.Cursor.Y - 1)
439 v.Buf.Remove(Loc{0, v.Cursor.Y - 1}, Loc{Count(line), v.Cursor.Y - 1})
442 v.Cursor.LastVisualX = v.Cursor.GetVisualX()
445 return PostActionCall("InsertNewline")
450 // Backspace deletes the previous character
451 func (v *View) Backspace(usePlugin bool) bool {
452 if usePlugin && !PreActionCall("Backspace") {
456 // Delete a character
457 if v.Cursor.HasSelection() {
458 v.Cursor.DeleteSelection()
459 v.Cursor.ResetSelection()
460 } else if v.Cursor.Loc.GreaterThan(v.Buf.Start()) {
461 // We have to do something a bit hacky here because we want to
462 // delete the line by first moving left and then deleting backwards
463 // but the undo redo would place the cursor in the wrong place
464 // So instead we move left, save the position, move back, delete
465 // and restore the position
467 // If the user is using spaces instead of tabs and they are deleting
468 // whitespace at the start of the line, we should delete as if its a
469 // tab (tabSize number of spaces)
470 lineStart := v.Buf.Line(v.Cursor.Y)[:v.Cursor.X]
471 tabSize := int(settings["tabsize"].(float64))
472 if settings["tabstospaces"].(bool) && IsSpaces(lineStart) && len(lineStart) != 0 && len(lineStart)%tabSize == 0 {
474 v.Cursor.Loc = loc.Move(-tabSize, v.Buf)
475 cx, cy := v.Cursor.X, v.Cursor.Y
477 v.Buf.Remove(loc.Move(-tabSize, v.Buf), loc)
478 v.Cursor.X, v.Cursor.Y = cx, cy
481 cx, cy := v.Cursor.X, v.Cursor.Y
484 v.Buf.Remove(loc.Move(-1, v.Buf), loc)
485 v.Cursor.X, v.Cursor.Y = cx, cy
488 v.Cursor.LastVisualX = v.Cursor.GetVisualX()
491 return PostActionCall("Backspace")
496 // DeleteWordRight deletes the word to the right of the cursor
497 func (v *View) DeleteWordRight(usePlugin bool) bool {
498 if usePlugin && !PreActionCall("DeleteWordRight") {
502 v.SelectWordRight(false)
503 if v.Cursor.HasSelection() {
504 v.Cursor.DeleteSelection()
505 v.Cursor.ResetSelection()
509 return PostActionCall("DeleteWordRight")
514 // DeleteWordLeft deletes the word to the left of the cursor
515 func (v *View) DeleteWordLeft(usePlugin bool) bool {
516 if usePlugin && !PreActionCall("DeleteWordLeft") {
520 v.SelectWordLeft(false)
521 if v.Cursor.HasSelection() {
522 v.Cursor.DeleteSelection()
523 v.Cursor.ResetSelection()
527 return PostActionCall("DeleteWordLeft")
532 // Delete deletes the next character
533 func (v *View) Delete(usePlugin bool) bool {
534 if usePlugin && !PreActionCall("Delete") {
538 if v.Cursor.HasSelection() {
539 v.Cursor.DeleteSelection()
540 v.Cursor.ResetSelection()
543 if loc.LessThan(v.Buf.End()) {
544 v.Buf.Remove(loc, loc.Move(1, v.Buf))
549 return PostActionCall("Delete")
554 // IndentSelection indents the current selection
555 func (v *View) IndentSelection(usePlugin bool) bool {
556 if usePlugin && !PreActionCall("IndentSelection") {
560 if v.Cursor.HasSelection() {
561 start := v.Cursor.CurSelection[0].Y
562 end := v.Cursor.CurSelection[1].Move(-1, v.Buf).Y
563 endX := v.Cursor.CurSelection[1].Move(-1, v.Buf).X
564 for i := start; i <= end; i++ {
565 if settings["tabstospaces"].(bool) {
566 tabsize := int(settings["tabsize"].(float64))
567 v.Buf.Insert(Loc{0, i}, Spaces(tabsize))
569 if v.Cursor.CurSelection[0].X > 0 {
570 v.Cursor.CurSelection[0] = v.Cursor.CurSelection[0].Move(tabsize, v.Buf)
574 v.Cursor.CurSelection[1] = Loc{endX + tabsize + 1, end}
577 v.Buf.Insert(Loc{0, i}, "\t")
579 if v.Cursor.CurSelection[0].X > 0 {
580 v.Cursor.CurSelection[0] = v.Cursor.CurSelection[0].Move(1, v.Buf)
584 v.Cursor.CurSelection[1] = Loc{endX + 2, end}
591 return PostActionCall("IndentSelection")
598 // OutdentSelection takes the current selection and moves it back one indent level
599 func (v *View) OutdentSelection(usePlugin bool) bool {
600 if usePlugin && !PreActionCall("OutdentSelection") {
604 if v.Cursor.HasSelection() {
605 start := v.Cursor.CurSelection[0].Y
606 end := v.Cursor.CurSelection[1].Move(-1, v.Buf).Y
607 endX := v.Cursor.CurSelection[1].Move(-1, v.Buf).X
608 for i := start; i <= end; i++ {
609 if len(GetLeadingWhitespace(v.Buf.Line(i))) > 0 {
610 if settings["tabstospaces"].(bool) {
611 tabsize := int(settings["tabsize"].(float64))
612 for j := 0; j < tabsize; j++ {
613 if len(GetLeadingWhitespace(v.Buf.Line(i))) == 0 {
616 v.Buf.Remove(Loc{0, i}, Loc{1, i})
618 if v.Cursor.CurSelection[0].X > 0 {
619 v.Cursor.CurSelection[0] = v.Cursor.CurSelection[0].Move(-1, v.Buf)
623 v.Cursor.CurSelection[1] = Loc{endX - j, end}
627 v.Buf.Remove(Loc{0, i}, Loc{1, i})
629 if v.Cursor.CurSelection[0].X > 0 {
630 v.Cursor.CurSelection[0] = v.Cursor.CurSelection[0].Move(-1, v.Buf)
634 v.Cursor.CurSelection[1] = Loc{endX, end}
642 return PostActionCall("OutdentSelection")
649 // InsertTab inserts a tab or spaces
650 func (v *View) InsertTab(usePlugin bool) bool {
651 if usePlugin && !PreActionCall("InsertTab") {
655 if v.Cursor.HasSelection() {
659 if settings["tabstospaces"].(bool) {
660 tabSize := int(settings["tabsize"].(float64))
661 v.Buf.Insert(v.Cursor.Loc, Spaces(tabSize))
662 for i := 0; i < tabSize; i++ {
666 v.Buf.Insert(v.Cursor.Loc, "\t")
671 return PostActionCall("InsertTab")
676 // Save the buffer to disk
677 func (v *View) Save(usePlugin bool) bool {
678 if usePlugin && !PreActionCall("Save") {
683 // We can't save the help text
686 // If this is an empty buffer, ask for a filename
687 if v.Buf.Path == "" {
688 filename, canceled := messenger.Prompt("Filename: ", "Save", NoCompletion)
690 v.Buf.Path = filename
691 v.Buf.Name = filename
698 if strings.HasSuffix(err.Error(), "permission denied") {
699 choice, _ := messenger.YesNoPrompt("Permission denied. Do you want to save this file using sudo? (y,n)")
701 err = v.Buf.SaveWithSudo()
703 messenger.Error(err.Error())
706 messenger.Message("Saved " + v.Buf.Path)
711 messenger.Error(err.Error())
714 messenger.Message("Saved " + v.Buf.Path)
718 return PostActionCall("Save")
723 // Find opens a prompt and searches forward for the input
724 func (v *View) Find(usePlugin bool) bool {
725 if usePlugin && !PreActionCall("Find") {
729 if v.Cursor.HasSelection() {
730 searchStart = ToCharPos(v.Cursor.CurSelection[1], v.Buf)
732 searchStart = ToCharPos(v.Cursor.Loc, v.Buf)
737 return PostActionCall("Find")
742 // FindNext searches forwards for the last used search term
743 func (v *View) FindNext(usePlugin bool) bool {
744 if usePlugin && !PreActionCall("FindNext") {
748 if v.Cursor.HasSelection() {
749 searchStart = ToCharPos(v.Cursor.CurSelection[1], v.Buf)
751 searchStart = ToCharPos(v.Cursor.Loc, v.Buf)
753 messenger.Message("Finding: " + lastSearch)
754 Search(lastSearch, v, true)
757 return PostActionCall("FindNext")
762 // FindPrevious searches backwards for the last used search term
763 func (v *View) FindPrevious(usePlugin bool) bool {
764 if usePlugin && !PreActionCall("FindPrevious") {
768 if v.Cursor.HasSelection() {
769 searchStart = ToCharPos(v.Cursor.CurSelection[0], v.Buf)
771 searchStart = ToCharPos(v.Cursor.Loc, v.Buf)
773 messenger.Message("Finding: " + lastSearch)
774 Search(lastSearch, v, false)
777 return PostActionCall("FindPrevious")
782 // Undo undoes the last action
783 func (v *View) Undo(usePlugin bool) bool {
784 if usePlugin && !PreActionCall("Undo") {
789 messenger.Message("Undid action")
792 return PostActionCall("Undo")
797 // Redo redoes the last action
798 func (v *View) Redo(usePlugin bool) bool {
799 if usePlugin && !PreActionCall("Redo") {
804 messenger.Message("Redid action")
807 return PostActionCall("Redo")
812 // Copy the selection to the system clipboard
813 func (v *View) Copy(usePlugin bool) bool {
814 if usePlugin && !PreActionCall("Copy") {
818 if v.Cursor.HasSelection() {
819 clipboard.WriteAll(v.Cursor.GetSelection())
821 messenger.Message("Copied selection")
825 return PostActionCall("Copy")
830 // CutLine cuts the current line to the clipboard
831 func (v *View) CutLine(usePlugin bool) bool {
832 if usePlugin && !PreActionCall("CutLine") {
836 v.Cursor.SelectLine()
837 if !v.Cursor.HasSelection() {
840 if v.freshClip == true {
841 if v.Cursor.HasSelection() {
842 if clip, err := clipboard.ReadAll(); err != nil {
845 clipboard.WriteAll(clip + v.Cursor.GetSelection())
848 } else if time.Since(v.lastCutTime)/time.Second > 10*time.Second || v.freshClip == false {
852 v.lastCutTime = time.Now()
853 v.Cursor.DeleteSelection()
854 v.Cursor.ResetSelection()
855 messenger.Message("Cut line")
858 return PostActionCall("CutLine")
863 // Cut the selection to the system clipboard
864 func (v *View) Cut(usePlugin bool) bool {
865 if usePlugin && !PreActionCall("Cut") {
869 if v.Cursor.HasSelection() {
870 clipboard.WriteAll(v.Cursor.GetSelection())
871 v.Cursor.DeleteSelection()
872 v.Cursor.ResetSelection()
874 messenger.Message("Cut selection")
877 return PostActionCall("Cut")
885 // DuplicateLine duplicates the current line
886 func (v *View) DuplicateLine(usePlugin bool) bool {
887 if usePlugin && !PreActionCall("DuplicateLine") {
892 v.Buf.Insert(v.Cursor.Loc, "\n"+v.Buf.Line(v.Cursor.Y))
894 messenger.Message("Duplicated line")
897 return PostActionCall("DuplicateLine")
902 // DeleteLine deletes the current line
903 func (v *View) DeleteLine(usePlugin bool) bool {
904 if usePlugin && !PreActionCall("DeleteLine") {
908 v.Cursor.SelectLine()
909 if !v.Cursor.HasSelection() {
912 v.Cursor.DeleteSelection()
913 v.Cursor.ResetSelection()
914 messenger.Message("Deleted line")
917 return PostActionCall("DeleteLine")
922 // Paste whatever is in the system clipboard into the buffer
923 // Delete and paste if the user has a selection
924 func (v *View) Paste(usePlugin bool) bool {
925 if usePlugin && !PreActionCall("Paste") {
929 leadingWS := GetLeadingWhitespace(v.Buf.Line(v.Cursor.Y))
931 if v.Cursor.HasSelection() {
932 v.Cursor.DeleteSelection()
933 v.Cursor.ResetSelection()
935 clip, _ := clipboard.ReadAll()
936 clip = strings.Replace(clip, "\n", "\n"+leadingWS, -1)
937 v.Buf.Insert(v.Cursor.Loc, clip)
938 v.Cursor.Loc = v.Cursor.Loc.Move(Count(clip), v.Buf)
940 messenger.Message("Pasted clipboard")
943 return PostActionCall("Paste")
948 // SelectAll selects the entire buffer
949 func (v *View) SelectAll(usePlugin bool) bool {
950 if usePlugin && !PreActionCall("SelectAll") {
954 v.Cursor.CurSelection[0] = v.Buf.Start()
955 v.Cursor.CurSelection[1] = v.Buf.End()
956 // Put the cursor at the beginning
961 return PostActionCall("SelectAll")
966 // OpenFile opens a new file in the buffer
967 func (v *View) OpenFile(usePlugin bool) bool {
968 if usePlugin && !PreActionCall("OpenFile") {
972 if v.CanClose("Continue? (yes, no, save) ") {
973 filename, canceled := messenger.Prompt("File to open: ", "Open", FileCompletion)
977 home, _ := homedir.Dir()
978 filename = strings.Replace(filename, "~", home, 1)
979 file, err := ioutil.ReadFile(filename)
983 // File does not exist -- create an empty buffer with that name
984 buf = NewBuffer([]byte{}, filename)
986 buf = NewBuffer(file, filename)
991 return PostActionCall("OpenFile")
998 // Start moves the viewport to the start of the buffer
999 func (v *View) Start(usePlugin bool) bool {
1000 if usePlugin && !PreActionCall("Start") {
1007 return PostActionCall("Start")
1012 // End moves the viewport to the end of the buffer
1013 func (v *View) End(usePlugin bool) bool {
1014 if usePlugin && !PreActionCall("End") {
1018 if v.height > v.Buf.NumLines {
1021 v.Topline = v.Buf.NumLines - v.height
1025 return PostActionCall("End")
1030 // PageUp scrolls the view up a page
1031 func (v *View) PageUp(usePlugin bool) bool {
1032 if usePlugin && !PreActionCall("PageUp") {
1036 if v.Topline > v.height {
1037 v.ScrollUp(v.height)
1043 return PostActionCall("PageUp")
1048 // PageDown scrolls the view down a page
1049 func (v *View) PageDown(usePlugin bool) bool {
1050 if usePlugin && !PreActionCall("PageDown") {
1054 if v.Buf.NumLines-(v.Topline+v.height) > v.height {
1055 v.ScrollDown(v.height)
1056 } else if v.Buf.NumLines >= v.height {
1057 v.Topline = v.Buf.NumLines - v.height
1061 return PostActionCall("PageDown")
1066 // CursorPageUp places the cursor a page up
1067 func (v *View) CursorPageUp(usePlugin bool) bool {
1068 if usePlugin && !PreActionCall("CursorPageUp") {
1072 if v.Cursor.HasSelection() {
1073 v.Cursor.Loc = v.Cursor.CurSelection[0]
1074 v.Cursor.ResetSelection()
1076 v.Cursor.UpN(v.height)
1079 return PostActionCall("CursorPageUp")
1084 // CursorPageDown places the cursor a page up
1085 func (v *View) CursorPageDown(usePlugin bool) bool {
1086 if usePlugin && !PreActionCall("CursorPageDown") {
1090 if v.Cursor.HasSelection() {
1091 v.Cursor.Loc = v.Cursor.CurSelection[1]
1092 v.Cursor.ResetSelection()
1094 v.Cursor.DownN(v.height)
1097 return PostActionCall("CursorPageDown")
1102 // HalfPageUp scrolls the view up half a page
1103 func (v *View) HalfPageUp(usePlugin bool) bool {
1104 if usePlugin && !PreActionCall("HalfPageUp") {
1108 if v.Topline > v.height/2 {
1109 v.ScrollUp(v.height / 2)
1115 return PostActionCall("HalfPageUp")
1120 // HalfPageDown scrolls the view down half a page
1121 func (v *View) HalfPageDown(usePlugin bool) bool {
1122 if usePlugin && !PreActionCall("HalfPageDown") {
1126 if v.Buf.NumLines-(v.Topline+v.height) > v.height/2 {
1127 v.ScrollDown(v.height / 2)
1129 if v.Buf.NumLines >= v.height {
1130 v.Topline = v.Buf.NumLines - v.height
1135 return PostActionCall("HalfPageDown")
1140 // ToggleRuler turns line numbers off and on
1141 func (v *View) ToggleRuler(usePlugin bool) bool {
1142 if usePlugin && !PreActionCall("ToggleRuler") {
1146 if settings["ruler"] == false {
1147 settings["ruler"] = true
1148 messenger.Message("Enabled ruler")
1150 settings["ruler"] = false
1151 messenger.Message("Disabled ruler")
1155 return PostActionCall("ToggleRuler")
1160 // JumpLine jumps to a line and moves the view accordingly.
1161 func (v *View) JumpLine(usePlugin bool) bool {
1162 if usePlugin && !PreActionCall("JumpLine") {
1166 // Prompt for line number
1167 linestring, canceled := messenger.Prompt("Jump to line # ", "LineNumber", NoCompletion)
1171 lineint, err := strconv.Atoi(linestring)
1172 lineint = lineint - 1 // fix offset
1174 messenger.Error(err) // return errors
1177 // Move cursor and view if possible.
1178 if lineint < v.Buf.NumLines && lineint >= 0 {
1180 v.Cursor.Y = lineint
1183 return PostActionCall("JumpLine")
1187 messenger.Error("Only ", v.Buf.NumLines, " lines to jump")
1191 // ClearStatus clears the messenger bar
1192 func (v *View) ClearStatus(usePlugin bool) bool {
1193 if usePlugin && !PreActionCall("ClearStatus") {
1197 messenger.Message("")
1200 return PostActionCall("ClearStatus")
1205 // ToggleHelp toggles the help screen
1206 func (v *View) ToggleHelp(usePlugin bool) bool {
1207 if usePlugin && !PreActionCall("ToggleHelp") {
1212 // Open the default help
1219 return PostActionCall("ToggleHelp")
1224 // ShellMode opens a terminal to run a shell command
1225 func (v *View) ShellMode(usePlugin bool) bool {
1226 if usePlugin && !PreActionCall("ShellMode") {
1230 input, canceled := messenger.Prompt("$ ", "Shell", NoCompletion)
1232 // The true here is for openTerm to make the command interactive
1233 HandleShellCommand(input, true)
1235 return PostActionCall("ShellMode")
1241 // CommandMode lets the user enter a command
1242 func (v *View) CommandMode(usePlugin bool) bool {
1243 if usePlugin && !PreActionCall("CommandMode") {
1247 input, canceled := messenger.Prompt("> ", "Command", CommandCompletion)
1249 HandleCommand(input)
1251 return PostActionCall("CommandMode")
1258 // Quit quits the editor
1259 // This behavior needs to be changed and should really only quit the editor if this
1261 // However, since micro only supports one view for now, it doesn't really matter
1262 func (v *View) Quit(usePlugin bool) bool {
1263 if usePlugin && !PreActionCall("Quit") {
1267 // Make sure not to quit if there are unsaved changes
1268 if v.CanClose("Quit anyway? (yes, no, save) ") {
1270 if len(tabs[curTab].views) > 1 {
1271 v.splitNode.Delete()
1272 tabs[v.TabNum].Cleanup()
1273 tabs[v.TabNum].Resize()
1274 } else if len(tabs) > 1 {
1275 if len(tabs[v.TabNum].views) == 1 {
1276 tabs = tabs[:v.TabNum+copy(tabs[v.TabNum:], tabs[v.TabNum+1:])]
1277 for i, t := range tabs {
1280 if curTab >= len(tabs) {
1284 // CurView().Resize(screen.Size())
1285 CurView().ToggleTabbar()
1286 CurView().matches = Match(CurView())
1291 PostActionCall("Quit")
1300 return PostActionCall("Quit")
1305 // AddTab adds a new tab with an empty buffer
1306 func (v *View) AddTab(usePlugin bool) bool {
1307 if usePlugin && !PreActionCall("AddTab") {
1311 tab := NewTabFromView(NewView(NewBuffer([]byte{}, "")))
1312 tab.SetNum(len(tabs))
1313 tabs = append(tabs, tab)
1316 for _, t := range tabs {
1317 for _, v := range t.views {
1324 return PostActionCall("AddTab")
1329 // PreviousTab switches to the previous tab in the tab list
1330 func (v *View) PreviousTab(usePlugin bool) bool {
1331 if usePlugin && !PreActionCall("PreviousTab") {
1337 } else if curTab == 0 {
1338 curTab = len(tabs) - 1
1342 return PostActionCall("PreviousTab")
1347 // NextTab switches to the next tab in the tab list
1348 func (v *View) NextTab(usePlugin bool) bool {
1349 if usePlugin && !PreActionCall("NextTab") {
1353 if curTab < len(tabs)-1 {
1355 } else if curTab == len(tabs)-1 {
1360 return PostActionCall("NextTab")
1365 // NextSplit changes the view to the next split
1366 func (v *View) NextSplit(usePlugin bool) bool {
1367 if usePlugin && !PreActionCall("NextSplit") {
1372 if tab.curView < len(tab.views)-1 {
1379 return PostActionCall("NextSplit")
1384 // PreviousSplit changes the view to the previous split
1385 func (v *View) PreviousSplit(usePlugin bool) bool {
1386 if usePlugin && !PreActionCall("PreviousSplit") {
1391 if tab.curView > 0 {
1394 tab.curView = len(tab.views) - 1
1398 return PostActionCall("PreviousSplit")
1403 // None is no action