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, view *View) bool {
18 for _, pl := range loadedPlugins {
19 ret, err := Call(pl+".pre"+funcName, view)
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, view *View) bool {
34 for _, pl := range loadedPlugins {
35 ret, err := Call(pl+".on"+funcName, view)
36 if err != nil && !strings.HasPrefix(err.Error(), "function does not exist") {
40 if ret == lua.LFalse {
47 // Center centers the view on the cursor
48 func (v *View) Center(usePlugin bool) bool {
49 if usePlugin && !PreActionCall("Center", v) {
53 v.Topline = v.Cursor.Y - v.height/2
54 if v.Topline+v.height > v.Buf.NumLines {
55 v.Topline = v.Buf.NumLines - v.height
62 return PostActionCall("Center", v)
67 // CursorUp moves the cursor up
68 func (v *View) CursorUp(usePlugin bool) bool {
69 if usePlugin && !PreActionCall("CursorUp", v) {
73 if v.Cursor.HasSelection() {
74 v.Cursor.Loc = v.Cursor.CurSelection[0]
75 v.Cursor.ResetSelection()
80 return PostActionCall("CursorUp", v)
85 // CursorDown moves the cursor down
86 func (v *View) CursorDown(usePlugin bool) bool {
87 if usePlugin && !PreActionCall("CursorDown", v) {
91 if v.Cursor.HasSelection() {
92 v.Cursor.Loc = v.Cursor.CurSelection[1]
93 v.Cursor.ResetSelection()
98 return PostActionCall("CursorDown", v)
103 // CursorLeft moves the cursor left
104 func (v *View) CursorLeft(usePlugin bool) bool {
105 if usePlugin && !PreActionCall("CursorLeft", v) {
109 if v.Cursor.HasSelection() {
110 v.Cursor.Loc = v.Cursor.CurSelection[0]
111 v.Cursor.ResetSelection()
117 return PostActionCall("CursorLeft", v)
122 // CursorRight moves the cursor right
123 func (v *View) CursorRight(usePlugin bool) bool {
124 if usePlugin && !PreActionCall("CursorRight", v) {
128 if v.Cursor.HasSelection() {
129 v.Cursor.Loc = v.Cursor.CurSelection[1].Move(-1, v.Buf)
130 v.Cursor.ResetSelection()
136 return PostActionCall("CursorRight", v)
141 // WordRight moves the cursor one word to the right
142 func (v *View) WordRight(usePlugin bool) bool {
143 if usePlugin && !PreActionCall("WordRight", v) {
150 return PostActionCall("WordRight", v)
155 // WordLeft moves the cursor one word to the left
156 func (v *View) WordLeft(usePlugin bool) bool {
157 if usePlugin && !PreActionCall("WordLeft", v) {
164 return PostActionCall("WordLeft", v)
169 // SelectUp selects up one line
170 func (v *View) SelectUp(usePlugin bool) bool {
171 if usePlugin && !PreActionCall("SelectUp", v) {
175 if !v.Cursor.HasSelection() {
176 v.Cursor.OrigSelection[0] = v.Cursor.Loc
179 v.Cursor.SelectTo(v.Cursor.Loc)
182 return PostActionCall("SelectUp", v)
187 // SelectDown selects down one line
188 func (v *View) SelectDown(usePlugin bool) bool {
189 if usePlugin && !PreActionCall("SelectDown", v) {
193 if !v.Cursor.HasSelection() {
194 v.Cursor.OrigSelection[0] = v.Cursor.Loc
197 v.Cursor.SelectTo(v.Cursor.Loc)
200 return PostActionCall("SelectDown", v)
205 // SelectLeft selects the character to the left of the cursor
206 func (v *View) SelectLeft(usePlugin bool) bool {
207 if usePlugin && !PreActionCall("SelectLeft", v) {
212 count := v.Buf.End().Move(-1, v.Buf)
213 if loc.GreaterThan(count) {
216 if !v.Cursor.HasSelection() {
217 v.Cursor.OrigSelection[0] = loc
220 v.Cursor.SelectTo(v.Cursor.Loc)
223 return PostActionCall("SelectLeft", v)
228 // SelectRight selects the character to the right of the cursor
229 func (v *View) SelectRight(usePlugin bool) bool {
230 if usePlugin && !PreActionCall("SelectRight", v) {
235 count := v.Buf.End().Move(-1, v.Buf)
236 if loc.GreaterThan(count) {
239 if !v.Cursor.HasSelection() {
240 v.Cursor.OrigSelection[0] = loc
243 v.Cursor.SelectTo(v.Cursor.Loc)
246 return PostActionCall("SelectRight", v)
251 // SelectWordRight selects the word to the right of the cursor
252 func (v *View) SelectWordRight(usePlugin bool) bool {
253 if usePlugin && !PreActionCall("SelectWordRight", v) {
257 if !v.Cursor.HasSelection() {
258 v.Cursor.OrigSelection[0] = v.Cursor.Loc
261 v.Cursor.SelectTo(v.Cursor.Loc)
264 return PostActionCall("SelectWordRight", v)
269 // SelectWordLeft selects the word to the left of the cursor
270 func (v *View) SelectWordLeft(usePlugin bool) bool {
271 if usePlugin && !PreActionCall("SelectWordLeft", v) {
275 if !v.Cursor.HasSelection() {
276 v.Cursor.OrigSelection[0] = v.Cursor.Loc
279 v.Cursor.SelectTo(v.Cursor.Loc)
282 return PostActionCall("SelectWordLeft", v)
287 // StartOfLine moves the cursor to the start of the line
288 func (v *View) StartOfLine(usePlugin bool) bool {
289 if usePlugin && !PreActionCall("StartOfLine", v) {
296 return PostActionCall("StartOfLine", v)
301 // EndOfLine moves the cursor to the end of the line
302 func (v *View) EndOfLine(usePlugin bool) bool {
303 if usePlugin && !PreActionCall("EndOfLine", v) {
310 return PostActionCall("EndOfLine", v)
315 // SelectToStartOfLine selects to the start of the current line
316 func (v *View) SelectToStartOfLine(usePlugin bool) bool {
317 if usePlugin && !PreActionCall("SelectToStartOfLine", v) {
321 if !v.Cursor.HasSelection() {
322 v.Cursor.OrigSelection[0] = v.Cursor.Loc
325 v.Cursor.SelectTo(v.Cursor.Loc)
328 return PostActionCall("SelectToStartOfLine", v)
333 // SelectToEndOfLine selects to the end of the current line
334 func (v *View) SelectToEndOfLine(usePlugin bool) bool {
335 if usePlugin && !PreActionCall("SelectToEndOfLine", v) {
339 if !v.Cursor.HasSelection() {
340 v.Cursor.OrigSelection[0] = v.Cursor.Loc
343 v.Cursor.SelectTo(v.Cursor.Loc)
346 return PostActionCall("SelectToEndOfLine", v)
351 // CursorStart moves the cursor to the start of the buffer
352 func (v *View) CursorStart(usePlugin bool) bool {
353 if usePlugin && !PreActionCall("CursorStart", v) {
361 return PostActionCall("CursorStart", v)
366 // CursorEnd moves the cursor to the end of the buffer
367 func (v *View) CursorEnd(usePlugin bool) bool {
368 if usePlugin && !PreActionCall("CursorEnd", v) {
372 v.Cursor.Loc = v.Buf.End()
375 return PostActionCall("CursorEnd", v)
380 // SelectToStart selects the text from the cursor to the start of the buffer
381 func (v *View) SelectToStart(usePlugin bool) bool {
382 if usePlugin && !PreActionCall("SelectToStart", v) {
386 if !v.Cursor.HasSelection() {
387 v.Cursor.OrigSelection[0] = v.Cursor.Loc
390 v.Cursor.SelectTo(v.Buf.Start())
393 return PostActionCall("SelectToStart", v)
398 // SelectToEnd selects the text from the cursor to the end of the buffer
399 func (v *View) SelectToEnd(usePlugin bool) bool {
400 if usePlugin && !PreActionCall("SelectToEnd", v) {
404 if !v.Cursor.HasSelection() {
405 v.Cursor.OrigSelection[0] = v.Cursor.Loc
408 v.Cursor.SelectTo(v.Buf.End())
411 return PostActionCall("SelectToEnd", v)
416 // InsertSpace inserts a space
417 func (v *View) InsertSpace(usePlugin bool) bool {
418 if usePlugin && !PreActionCall("InsertSpace", v) {
422 if v.Cursor.HasSelection() {
423 v.Cursor.DeleteSelection()
424 v.Cursor.ResetSelection()
426 v.Buf.Insert(v.Cursor.Loc, " ")
430 return PostActionCall("InsertSpace", v)
435 // InsertNewline inserts a newline plus possible some whitespace if autoindent is on
436 func (v *View) InsertNewline(usePlugin bool) bool {
437 if usePlugin && !PreActionCall("InsertNewline", v) {
442 if v.Cursor.HasSelection() {
443 v.Cursor.DeleteSelection()
444 v.Cursor.ResetSelection()
447 v.Buf.Insert(v.Cursor.Loc, "\n")
448 ws := GetLeadingWhitespace(v.Buf.Line(v.Cursor.Y))
451 if v.Buf.Settings["autoindent"].(bool) {
452 v.Buf.Insert(v.Cursor.Loc, ws)
453 for i := 0; i < len(ws); i++ {
457 if IsSpacesOrTabs(v.Buf.Line(v.Cursor.Y - 1)) {
458 line := v.Buf.Line(v.Cursor.Y - 1)
459 v.Buf.Remove(Loc{0, v.Cursor.Y - 1}, Loc{Count(line), v.Cursor.Y - 1})
462 v.Cursor.LastVisualX = v.Cursor.GetVisualX()
465 return PostActionCall("InsertNewline", v)
470 // Backspace deletes the previous character
471 func (v *View) Backspace(usePlugin bool) bool {
472 if usePlugin && !PreActionCall("Backspace", v) {
476 // Delete a character
477 if v.Cursor.HasSelection() {
478 v.Cursor.DeleteSelection()
479 v.Cursor.ResetSelection()
480 } else if v.Cursor.Loc.GreaterThan(v.Buf.Start()) {
481 // We have to do something a bit hacky here because we want to
482 // delete the line by first moving left and then deleting backwards
483 // but the undo redo would place the cursor in the wrong place
484 // So instead we move left, save the position, move back, delete
485 // and restore the position
487 // If the user is using spaces instead of tabs and they are deleting
488 // whitespace at the start of the line, we should delete as if its a
489 // tab (tabSize number of spaces)
490 lineStart := v.Buf.Line(v.Cursor.Y)[:v.Cursor.X]
491 tabSize := int(v.Buf.Settings["tabsize"].(float64))
492 if v.Buf.Settings["tabstospaces"].(bool) && IsSpaces(lineStart) && len(lineStart) != 0 && len(lineStart)%tabSize == 0 {
494 v.Cursor.Loc = loc.Move(-tabSize, v.Buf)
495 cx, cy := v.Cursor.X, v.Cursor.Y
497 v.Buf.Remove(loc.Move(-tabSize, v.Buf), loc)
498 v.Cursor.X, v.Cursor.Y = cx, cy
501 cx, cy := v.Cursor.X, v.Cursor.Y
504 v.Buf.Remove(loc.Move(-1, v.Buf), loc)
505 v.Cursor.X, v.Cursor.Y = cx, cy
508 v.Cursor.LastVisualX = v.Cursor.GetVisualX()
511 return PostActionCall("Backspace", v)
516 // DeleteWordRight deletes the word to the right of the cursor
517 func (v *View) DeleteWordRight(usePlugin bool) bool {
518 if usePlugin && !PreActionCall("DeleteWordRight", v) {
522 v.SelectWordRight(false)
523 if v.Cursor.HasSelection() {
524 v.Cursor.DeleteSelection()
525 v.Cursor.ResetSelection()
529 return PostActionCall("DeleteWordRight", v)
534 // DeleteWordLeft deletes the word to the left of the cursor
535 func (v *View) DeleteWordLeft(usePlugin bool) bool {
536 if usePlugin && !PreActionCall("DeleteWordLeft", v) {
540 v.SelectWordLeft(false)
541 if v.Cursor.HasSelection() {
542 v.Cursor.DeleteSelection()
543 v.Cursor.ResetSelection()
547 return PostActionCall("DeleteWordLeft", v)
552 // Delete deletes the next character
553 func (v *View) Delete(usePlugin bool) bool {
554 if usePlugin && !PreActionCall("Delete", v) {
558 if v.Cursor.HasSelection() {
559 v.Cursor.DeleteSelection()
560 v.Cursor.ResetSelection()
563 if loc.LessThan(v.Buf.End()) {
564 v.Buf.Remove(loc, loc.Move(1, v.Buf))
569 return PostActionCall("Delete", v)
574 // IndentSelection indents the current selection
575 func (v *View) IndentSelection(usePlugin bool) bool {
576 if usePlugin && !PreActionCall("IndentSelection", v) {
580 if v.Cursor.HasSelection() {
581 start := v.Cursor.CurSelection[0].Y
582 end := v.Cursor.CurSelection[1].Move(-1, v.Buf).Y
583 endX := v.Cursor.CurSelection[1].Move(-1, v.Buf).X
584 for i := start; i <= end; i++ {
585 if v.Buf.Settings["tabstospaces"].(bool) {
586 tabsize := int(v.Buf.Settings["tabsize"].(float64))
587 v.Buf.Insert(Loc{0, i}, Spaces(tabsize))
589 if v.Cursor.CurSelection[0].X > 0 {
590 v.Cursor.CurSelection[0] = v.Cursor.CurSelection[0].Move(tabsize, v.Buf)
594 v.Cursor.CurSelection[1] = Loc{endX + tabsize + 1, end}
597 v.Buf.Insert(Loc{0, i}, "\t")
599 if v.Cursor.CurSelection[0].X > 0 {
600 v.Cursor.CurSelection[0] = v.Cursor.CurSelection[0].Move(1, v.Buf)
604 v.Cursor.CurSelection[1] = Loc{endX + 2, end}
611 return PostActionCall("IndentSelection", v)
618 // OutdentSelection takes the current selection and moves it back one indent level
619 func (v *View) OutdentSelection(usePlugin bool) bool {
620 if usePlugin && !PreActionCall("OutdentSelection", v) {
624 if v.Cursor.HasSelection() {
625 start := v.Cursor.CurSelection[0].Y
626 end := v.Cursor.CurSelection[1].Move(-1, v.Buf).Y
627 endX := v.Cursor.CurSelection[1].Move(-1, v.Buf).X
628 for i := start; i <= end; i++ {
629 if len(GetLeadingWhitespace(v.Buf.Line(i))) > 0 {
630 if v.Buf.Settings["tabstospaces"].(bool) {
631 tabsize := int(v.Buf.Settings["tabsize"].(float64))
632 for j := 0; j < tabsize; j++ {
633 if len(GetLeadingWhitespace(v.Buf.Line(i))) == 0 {
636 v.Buf.Remove(Loc{0, i}, Loc{1, i})
638 if v.Cursor.CurSelection[0].X > 0 {
639 v.Cursor.CurSelection[0] = v.Cursor.CurSelection[0].Move(-1, v.Buf)
643 v.Cursor.CurSelection[1] = Loc{endX - j, end}
647 v.Buf.Remove(Loc{0, i}, Loc{1, i})
649 if v.Cursor.CurSelection[0].X > 0 {
650 v.Cursor.CurSelection[0] = v.Cursor.CurSelection[0].Move(-1, v.Buf)
654 v.Cursor.CurSelection[1] = Loc{endX, end}
662 return PostActionCall("OutdentSelection", v)
669 // InsertTab inserts a tab or spaces
670 func (v *View) InsertTab(usePlugin bool) bool {
671 if usePlugin && !PreActionCall("InsertTab", v) {
675 if v.Cursor.HasSelection() {
679 if v.Buf.Settings["tabstospaces"].(bool) {
680 tabSize := int(v.Buf.Settings["tabsize"].(float64))
681 v.Buf.Insert(v.Cursor.Loc, Spaces(tabSize))
682 for i := 0; i < tabSize; i++ {
686 v.Buf.Insert(v.Cursor.Loc, "\t")
691 return PostActionCall("InsertTab", v)
696 // Save the buffer to disk
697 func (v *View) Save(usePlugin bool) bool {
698 if usePlugin && !PreActionCall("Save", v) {
703 // We can't save the help text
706 // If this is an empty buffer, ask for a filename
707 if v.Buf.Path == "" {
708 filename, canceled := messenger.Prompt("Filename: ", "Save", NoCompletion)
710 v.Buf.Path = filename
711 v.Buf.Name = filename
718 if strings.HasSuffix(err.Error(), "permission denied") {
719 choice, _ := messenger.YesNoPrompt("Permission denied. Do you want to save this file using sudo? (y,n)")
721 err = v.Buf.SaveWithSudo()
723 messenger.Error(err.Error())
726 messenger.Message("Saved " + v.Buf.Path)
731 messenger.Error(err.Error())
734 messenger.Message("Saved " + v.Buf.Path)
738 return PostActionCall("Save", v)
743 // Find opens a prompt and searches forward for the input
744 func (v *View) Find(usePlugin bool) bool {
745 if usePlugin && !PreActionCall("Find", v) {
749 if v.Cursor.HasSelection() {
750 searchStart = ToCharPos(v.Cursor.CurSelection[1], v.Buf)
752 searchStart = ToCharPos(v.Cursor.Loc, v.Buf)
757 return PostActionCall("Find", v)
762 // FindNext searches forwards for the last used search term
763 func (v *View) FindNext(usePlugin bool) bool {
764 if usePlugin && !PreActionCall("FindNext", v) {
768 if v.Cursor.HasSelection() {
769 searchStart = ToCharPos(v.Cursor.CurSelection[1], v.Buf)
771 searchStart = ToCharPos(v.Cursor.Loc, v.Buf)
773 messenger.Message("Finding: " + lastSearch)
774 Search(lastSearch, v, true)
777 return PostActionCall("FindNext", v)
782 // FindPrevious searches backwards for the last used search term
783 func (v *View) FindPrevious(usePlugin bool) bool {
784 if usePlugin && !PreActionCall("FindPrevious", v) {
788 if v.Cursor.HasSelection() {
789 searchStart = ToCharPos(v.Cursor.CurSelection[0], v.Buf)
791 searchStart = ToCharPos(v.Cursor.Loc, v.Buf)
793 messenger.Message("Finding: " + lastSearch)
794 Search(lastSearch, v, false)
797 return PostActionCall("FindPrevious", v)
802 // Undo undoes the last action
803 func (v *View) Undo(usePlugin bool) bool {
804 if usePlugin && !PreActionCall("Undo", v) {
809 messenger.Message("Undid action")
812 return PostActionCall("Undo", v)
817 // Redo redoes the last action
818 func (v *View) Redo(usePlugin bool) bool {
819 if usePlugin && !PreActionCall("Redo", v) {
824 messenger.Message("Redid action")
827 return PostActionCall("Redo", v)
832 // Copy the selection to the system clipboard
833 func (v *View) Copy(usePlugin bool) bool {
834 if usePlugin && !PreActionCall("Copy", v) {
838 if v.Cursor.HasSelection() {
839 clipboard.WriteAll(v.Cursor.GetSelection())
841 messenger.Message("Copied selection")
845 return PostActionCall("Copy", v)
850 // CutLine cuts the current line to the clipboard
851 func (v *View) CutLine(usePlugin bool) bool {
852 if usePlugin && !PreActionCall("CutLine", v) {
856 v.Cursor.SelectLine()
857 if !v.Cursor.HasSelection() {
860 if v.freshClip == true {
861 if v.Cursor.HasSelection() {
862 if clip, err := clipboard.ReadAll(); err != nil {
865 clipboard.WriteAll(clip + v.Cursor.GetSelection())
868 } else if time.Since(v.lastCutTime)/time.Second > 10*time.Second || v.freshClip == false {
872 v.lastCutTime = time.Now()
873 v.Cursor.DeleteSelection()
874 v.Cursor.ResetSelection()
875 messenger.Message("Cut line")
878 return PostActionCall("CutLine", v)
883 // Cut the selection to the system clipboard
884 func (v *View) Cut(usePlugin bool) bool {
885 if usePlugin && !PreActionCall("Cut", v) {
889 if v.Cursor.HasSelection() {
890 clipboard.WriteAll(v.Cursor.GetSelection())
891 v.Cursor.DeleteSelection()
892 v.Cursor.ResetSelection()
894 messenger.Message("Cut selection")
897 return PostActionCall("Cut", v)
905 // DuplicateLine duplicates the current line
906 func (v *View) DuplicateLine(usePlugin bool) bool {
907 if usePlugin && !PreActionCall("DuplicateLine", v) {
912 v.Buf.Insert(v.Cursor.Loc, "\n"+v.Buf.Line(v.Cursor.Y))
914 messenger.Message("Duplicated line")
917 return PostActionCall("DuplicateLine", v)
922 // DeleteLine deletes the current line
923 func (v *View) DeleteLine(usePlugin bool) bool {
924 if usePlugin && !PreActionCall("DeleteLine", v) {
928 v.Cursor.SelectLine()
929 if !v.Cursor.HasSelection() {
932 v.Cursor.DeleteSelection()
933 v.Cursor.ResetSelection()
934 messenger.Message("Deleted line")
937 return PostActionCall("DeleteLine", v)
942 // Paste whatever is in the system clipboard into the buffer
943 // Delete and paste if the user has a selection
944 func (v *View) Paste(usePlugin bool) bool {
945 if usePlugin && !PreActionCall("Paste", v) {
949 leadingWS := GetLeadingWhitespace(v.Buf.Line(v.Cursor.Y))
951 if v.Cursor.HasSelection() {
952 v.Cursor.DeleteSelection()
953 v.Cursor.ResetSelection()
955 clip, _ := clipboard.ReadAll()
956 clip = strings.Replace(clip, "\n", "\n"+leadingWS, -1)
957 v.Buf.Insert(v.Cursor.Loc, clip)
958 v.Cursor.Loc = v.Cursor.Loc.Move(Count(clip), v.Buf)
960 messenger.Message("Pasted clipboard")
963 return PostActionCall("Paste", v)
968 // SelectAll selects the entire buffer
969 func (v *View) SelectAll(usePlugin bool) bool {
970 if usePlugin && !PreActionCall("SelectAll", v) {
974 v.Cursor.CurSelection[0] = v.Buf.Start()
975 v.Cursor.CurSelection[1] = v.Buf.End()
976 // Put the cursor at the beginning
981 return PostActionCall("SelectAll", v)
986 // OpenFile opens a new file in the buffer
987 func (v *View) OpenFile(usePlugin bool) bool {
988 if usePlugin && !PreActionCall("OpenFile", v) {
992 if v.CanClose("Continue? (yes, no, save) ") {
993 filename, canceled := messenger.Prompt("File to open: ", "Open", FileCompletion)
997 home, _ := homedir.Dir()
998 filename = strings.Replace(filename, "~", home, 1)
999 file, err := ioutil.ReadFile(filename)
1003 // File does not exist -- create an empty buffer with that name
1004 buf = NewBuffer([]byte{}, filename)
1006 buf = NewBuffer(file, filename)
1011 return PostActionCall("OpenFile", v)
1018 // Start moves the viewport to the start of the buffer
1019 func (v *View) Start(usePlugin bool) bool {
1020 if usePlugin && !PreActionCall("Start", v) {
1027 return PostActionCall("Start", v)
1032 // End moves the viewport to the end of the buffer
1033 func (v *View) End(usePlugin bool) bool {
1034 if usePlugin && !PreActionCall("End", v) {
1038 if v.height > v.Buf.NumLines {
1041 v.Topline = v.Buf.NumLines - v.height
1045 return PostActionCall("End", v)
1050 // PageUp scrolls the view up a page
1051 func (v *View) PageUp(usePlugin bool) bool {
1052 if usePlugin && !PreActionCall("PageUp", v) {
1056 if v.Topline > v.height {
1057 v.ScrollUp(v.height)
1063 return PostActionCall("PageUp", v)
1068 // PageDown scrolls the view down a page
1069 func (v *View) PageDown(usePlugin bool) bool {
1070 if usePlugin && !PreActionCall("PageDown", v) {
1074 if v.Buf.NumLines-(v.Topline+v.height) > v.height {
1075 v.ScrollDown(v.height)
1076 } else if v.Buf.NumLines >= v.height {
1077 v.Topline = v.Buf.NumLines - v.height
1081 return PostActionCall("PageDown", v)
1086 // CursorPageUp places the cursor a page up
1087 func (v *View) CursorPageUp(usePlugin bool) bool {
1088 if usePlugin && !PreActionCall("CursorPageUp", v) {
1092 if v.Cursor.HasSelection() {
1093 v.Cursor.Loc = v.Cursor.CurSelection[0]
1094 v.Cursor.ResetSelection()
1096 v.Cursor.UpN(v.height)
1099 return PostActionCall("CursorPageUp", v)
1104 // CursorPageDown places the cursor a page up
1105 func (v *View) CursorPageDown(usePlugin bool) bool {
1106 if usePlugin && !PreActionCall("CursorPageDown", v) {
1110 if v.Cursor.HasSelection() {
1111 v.Cursor.Loc = v.Cursor.CurSelection[1]
1112 v.Cursor.ResetSelection()
1114 v.Cursor.DownN(v.height)
1117 return PostActionCall("CursorPageDown", v)
1122 // HalfPageUp scrolls the view up half a page
1123 func (v *View) HalfPageUp(usePlugin bool) bool {
1124 if usePlugin && !PreActionCall("HalfPageUp", v) {
1128 if v.Topline > v.height/2 {
1129 v.ScrollUp(v.height / 2)
1135 return PostActionCall("HalfPageUp", v)
1140 // HalfPageDown scrolls the view down half a page
1141 func (v *View) HalfPageDown(usePlugin bool) bool {
1142 if usePlugin && !PreActionCall("HalfPageDown", v) {
1146 if v.Buf.NumLines-(v.Topline+v.height) > v.height/2 {
1147 v.ScrollDown(v.height / 2)
1149 if v.Buf.NumLines >= v.height {
1150 v.Topline = v.Buf.NumLines - v.height
1155 return PostActionCall("HalfPageDown", v)
1160 // ToggleRuler turns line numbers off and on
1161 func (v *View) ToggleRuler(usePlugin bool) bool {
1162 if usePlugin && !PreActionCall("ToggleRuler", v) {
1166 if v.Buf.Settings["ruler"] == false {
1167 v.Buf.Settings["ruler"] = true
1168 messenger.Message("Enabled ruler")
1170 v.Buf.Settings["ruler"] = false
1171 messenger.Message("Disabled ruler")
1175 return PostActionCall("ToggleRuler", v)
1180 // JumpLine jumps to a line and moves the view accordingly.
1181 func (v *View) JumpLine(usePlugin bool) bool {
1182 if usePlugin && !PreActionCall("JumpLine", v) {
1186 // Prompt for line number
1187 linestring, canceled := messenger.Prompt("Jump to line # ", "LineNumber", NoCompletion)
1191 lineint, err := strconv.Atoi(linestring)
1192 lineint = lineint - 1 // fix offset
1194 messenger.Error(err) // return errors
1197 // Move cursor and view if possible.
1198 if lineint < v.Buf.NumLines && lineint >= 0 {
1200 v.Cursor.Y = lineint
1203 return PostActionCall("JumpLine", v)
1207 messenger.Error("Only ", v.Buf.NumLines, " lines to jump")
1211 // ClearStatus clears the messenger bar
1212 func (v *View) ClearStatus(usePlugin bool) bool {
1213 if usePlugin && !PreActionCall("ClearStatus", v) {
1217 messenger.Message("")
1220 return PostActionCall("ClearStatus", v)
1225 // ToggleHelp toggles the help screen
1226 func (v *View) ToggleHelp(usePlugin bool) bool {
1227 if usePlugin && !PreActionCall("ToggleHelp", v) {
1232 // Open the default help
1239 return PostActionCall("ToggleHelp", v)
1244 // ShellMode opens a terminal to run a shell command
1245 func (v *View) ShellMode(usePlugin bool) bool {
1246 if usePlugin && !PreActionCall("ShellMode", v) {
1250 input, canceled := messenger.Prompt("$ ", "Shell", NoCompletion)
1252 // The true here is for openTerm to make the command interactive
1253 HandleShellCommand(input, true)
1255 return PostActionCall("ShellMode", v)
1261 // CommandMode lets the user enter a command
1262 func (v *View) CommandMode(usePlugin bool) bool {
1263 if usePlugin && !PreActionCall("CommandMode", v) {
1267 input, canceled := messenger.Prompt("> ", "Command", CommandCompletion)
1269 HandleCommand(input)
1271 return PostActionCall("CommandMode", v)
1278 // Quit quits the editor
1279 // This behavior needs to be changed and should really only quit the editor if this
1281 // However, since micro only supports one view for now, it doesn't really matter
1282 func (v *View) Quit(usePlugin bool) bool {
1283 if usePlugin && !PreActionCall("Quit", v) {
1287 // Make sure not to quit if there are unsaved changes
1288 if v.CanClose("Quit anyway? (yes, no, save) ") {
1290 if len(tabs[curTab].views) > 1 {
1291 v.splitNode.Delete()
1292 tabs[v.TabNum].Cleanup()
1293 tabs[v.TabNum].Resize()
1294 } else if len(tabs) > 1 {
1295 if len(tabs[v.TabNum].views) == 1 {
1296 tabs = tabs[:v.TabNum+copy(tabs[v.TabNum:], tabs[v.TabNum+1:])]
1297 for i, t := range tabs {
1300 if curTab >= len(tabs) {
1304 // CurView().Resize(screen.Size())
1305 CurView().ToggleTabbar()
1306 CurView().matches = Match(CurView())
1311 PostActionCall("Quit", v)
1320 return PostActionCall("Quit", v)
1325 // AddTab adds a new tab with an empty buffer
1326 func (v *View) AddTab(usePlugin bool) bool {
1327 if usePlugin && !PreActionCall("AddTab", v) {
1331 tab := NewTabFromView(NewView(NewBuffer([]byte{}, "")))
1332 tab.SetNum(len(tabs))
1333 tabs = append(tabs, tab)
1336 for _, t := range tabs {
1337 for _, v := range t.views {
1344 return PostActionCall("AddTab", v)
1349 // PreviousTab switches to the previous tab in the tab list
1350 func (v *View) PreviousTab(usePlugin bool) bool {
1351 if usePlugin && !PreActionCall("PreviousTab", v) {
1357 } else if curTab == 0 {
1358 curTab = len(tabs) - 1
1362 return PostActionCall("PreviousTab", v)
1367 // NextTab switches to the next tab in the tab list
1368 func (v *View) NextTab(usePlugin bool) bool {
1369 if usePlugin && !PreActionCall("NextTab", v) {
1373 if curTab < len(tabs)-1 {
1375 } else if curTab == len(tabs)-1 {
1380 return PostActionCall("NextTab", v)
1385 // NextSplit changes the view to the next split
1386 func (v *View) NextSplit(usePlugin bool) bool {
1387 if usePlugin && !PreActionCall("NextSplit", v) {
1392 if tab.curView < len(tab.views)-1 {
1399 return PostActionCall("NextSplit", v)
1404 // PreviousSplit changes the view to the previous split
1405 func (v *View) PreviousSplit(usePlugin bool) bool {
1406 if usePlugin && !PreActionCall("PreviousSplit", v) {
1411 if tab.curView > 0 {
1414 tab.curView = len(tab.views) - 1
1418 return PostActionCall("PreviousSplit", v)
1423 // None is no action