func (m MoveFOL) Apply(e *editor.Editor) { v := e.ActiveView() c := v.Cursor() pos := utils.IndexFirstNonSpace(c.Line.Data) c.Boffset = pos v.MoveCursorTo(c) }
func (_ DeleteEOL) Apply(e *editor.Editor) { v := e.ActiveView() c := v.Cursor() l := c.Line d := l.Data[:c.Boffset] v.Buffer().Delete(c, len(l.Data)-len(d)) }
// Store the search term on the editor instance. // This allows us to use it later in other commands. func storeSearchTerm(e *editor.Editor, term string) { // don't do anything if no term is given if term == "" { return } e.LastSearchTerm = term e.ActiveView().SetHighlightBytes([]byte(term)) }
func (m MoveView) Apply(e *editor.Editor) { v := e.ActiveView() switch m.Dir { case Forward: v.MoveViewLines(m.Lines) case Backward: v.MoveViewLines(-m.Lines) } }
func (m MoveWordEnd) Apply(e *editor.Editor) { v := e.ActiveView() c := v.Cursor() ok := c.EndWord() v.MoveCursorTo(c) // FIXME Message is never printed if !ok { v.SetStatus("End of buffer") } }
func (r DisplayFileStatus) Apply(e *editor.Editor) { v := e.ActiveView() path := v.Buffer().Path numLines := v.Buffer().NumLines c := v.Cursor() pc := (float64(c.LineNum) / float64(numLines)) * 100 v.SetStatus("\"%s\" %d lines --%d%%--", path, numLines, int(pc)) }
func (m MoveEOF) Apply(e *editor.Editor) { v := e.ActiveView() b := v.Buffer() c := buffer.Cursor{ Line: b.LastLine, LineNum: b.NumLines, Boffset: b.LastLine.Len(), } v.MoveCursorTo(c) }
func (m MoveWord) Apply(e *editor.Editor) { // moveCursorWordForward v := e.ActiveView() c := v.Cursor() ok := c.NextWord() if !ok { e.SetStatus("End of buffer") return } v.MoveCursorTo(c) }
func (m NearestVSplit) Apply(e *editor.Editor) { n := e.ActiveViewNode() var d int switch m.Dir { case Forward: d = 1 case Backward: d = -1 } if k := n.NearestVSplit(d); k != nil { e.SetActiveViewNode(k) } }
func (m MoveLine) Apply(e *editor.Editor) { v := e.ActiveView() c := v.Cursor() switch m.Dir { case Forward: if !c.NextLine() { v.SetStatus("End of file") return } case Backward: if !c.PrevLine() { v.SetStatus("Beginning of file") return } } v.MoveCursorTo(c) }
func (r Redo) Apply(e *editor.Editor) { for i := 0; i < r.Count; i++ { e.ActiveView().Buffer().Redo() } }
func (_ DeleteRuneBackward) Apply(e *editor.Editor) { view := e.ActiveView() view.Buffer().DeleteRuneBackward(view.Cursor()) }
func (m MoveEOL) Apply(e *editor.Editor) { v := e.ActiveView() c := v.Cursor() c.MoveEOL() v.MoveCursorTo(c) }
func (m *normalMode) Enter(e *editor.Editor) { e.ActiveView().Buffer().FinalizeActionGroup() }
func (r InsertRune) Apply(e *editor.Editor) { view := e.ActiveView() view.Buffer().InsertRune(view.Cursor(), r.Rune) }
func (u Undo) Apply(e *editor.Editor) { for i := 0; i < u.Count; i++ { e.ActiveView().Buffer().Undo() } }
func (u Undo) Apply(e *editor.Editor) { e.ActiveView().Buffer().Undo() }
func (r Redo) Apply(e *editor.Editor) { e.ActiveView().Buffer().Redo() }
func (s Search) Apply(e *editor.Editor) { v := e.ActiveView() c := v.Cursor() if e.LastSearchTerm == "" { e.SetStatus("Nothing to search for.") return } word := []byte(e.LastSearchTerm) switch s.Dir { case Forward: e.SetStatus("Search forward for: %s", e.LastSearchTerm) for { // move the cursor one run forward. // this allows us to move to the next match. // without this, if the word under the cursor is a match, // then we won't be able to advance to the next match c.NextRune(false) i := bytes.Index(c.Line.Data[c.Boffset:], word) if i != -1 { c.Boffset += i break } c.Line = c.Line.Next if c.Line == nil { e.SetStatus("No more results") return } c.LineNum++ c.Boffset = 0 } case Backward: e.SetStatus("Search backward for: %s", e.LastSearchTerm) for { i := bytes.LastIndex(c.Line.Data[:c.Boffset], word) if i != -1 { c.Boffset = i break } c.Line = c.Line.Prev if c.Line == nil { e.SetStatus("No previous results") return } c.LineNum-- c.Boffset = len(c.Line.Data) } } v.MoveCursorTo(c) }
// Interpret command and apply changes to editor. func execCommand(e *editor.Editor, command string) error { fields := strings.Fields(command) // prevent a crash if no commands are given if len(fields) == 0 { return nil } cmd, args := fields[0], fields[1:] switch cmd { case "q": // TODO if more than one split, close active one only. e.Quit() case "w": b := e.ActiveView().Buffer() switch len(args) { case 0: b.Save() case 1: b.SaveAs(args[0]) default: return fmt.Errorf("too many arguments to :w") } case "e": var filename string switch len(args) { case 0: return fmt.Errorf("TODO re-read current file, if any") case 1: filename = args[0] default: return fmt.Errorf("too many arguments for :e") } // TODO: Don't replace the current buffer if it has been modified buffer, err := e.NewBufferFromFile(filename) if err != nil { return err } e.ActiveView().Attach(buffer) case "sp", "split": e.SplitHorizontally() // TODO file argument | shell command argument case "vsp", "vsplit": e.SplitVertically() case "nohls": e.ActiveView().ShowHighlights(false) case "hls": e.ActiveView().ShowHighlights(true) } if lineNum, err := strconv.Atoi(cmd); err == nil { // cmd is a number, we should move to that line e.ActiveView().MoveCursorToLine(lineNum) } return nil }