]> git.lizzy.rs Git - micro.git/blobdiff - cmd/micro/eventhandler.go
Code optimisation (#1117)
[micro.git] / cmd / micro / eventhandler.go
index 84f7465cf57f84b5aea859a1e59df1e5e4a93275..adf7cba4a3cbfd4c9973d0adaa22b77093f2c504 100644 (file)
@@ -1,18 +1,22 @@
 package main
 
 import (
+       "strings"
        "time"
 
        dmp "github.com/sergi/go-diff/diffmatchpatch"
+       "github.com/yuin/gopher-lua"
 )
 
 const (
        // Opposite and undoing events must have opposite values
 
-       // TextEventInsert repreasents an insertion event
+       // TextEventInsert represents an insertion event
        TextEventInsert = 1
        // TextEventRemove represents a deletion event
        TextEventRemove = -1
+       // TextEventReplace represents a replace event
+       TextEventReplace = 0
 )
 
 // TextEvent holds data for a manipulation on some text that can be undone
@@ -20,18 +24,37 @@ type TextEvent struct {
        C Cursor
 
        EventType int
-       Text      string
-       Start     int
-       End       int
+       Deltas    []Delta
        Time      time.Time
 }
 
+// A Delta is a change to the buffer
+type Delta struct {
+       Text  string
+       Start Loc
+       End   Loc
+}
+
 // ExecuteTextEvent runs a text event
 func ExecuteTextEvent(t *TextEvent, buf *Buffer) {
        if t.EventType == TextEventInsert {
-               buf.insert(t.Start, t.Text)
+               for _, d := range t.Deltas {
+                       buf.insert(d.Start, []byte(d.Text))
+               }
        } else if t.EventType == TextEventRemove {
-               t.Text = buf.remove(t.Start, t.End)
+               for i, d := range t.Deltas {
+                       t.Deltas[i].Text = buf.remove(d.Start, d.End)
+               }
+       } else if t.EventType == TextEventReplace {
+               for i, d := range t.Deltas {
+                       t.Deltas[i].Text = buf.remove(d.Start, d.End)
+                       buf.insert(d.Start, []byte(d.Text))
+                       t.Deltas[i].Start = d.Start
+                       t.Deltas[i].End = Loc{d.Start.X + Count(d.Text), d.Start.Y}
+               }
+               for i, j := 0, len(t.Deltas)-1; i < j; i, j = i+1, j-1 {
+                       t.Deltas[i], t.Deltas[j] = t.Deltas[j], t.Deltas[i]
+               }
        }
 }
 
@@ -62,47 +85,92 @@ func NewEventHandler(buf *Buffer) *EventHandler {
 // This means that we can transform the buffer into any string and still preserve undo/redo
 // through insert and delete events
 func (eh *EventHandler) ApplyDiff(new string) {
-       messenger.Message("Applying diff")
        differ := dmp.New()
        diff := differ.DiffMain(eh.buf.String(), new, false)
-       var charNum int
+       loc := eh.buf.Start()
        for _, d := range diff {
-               if d.Type == dmp.DiffInsert {
-                       eh.Insert(charNum, d.Text)
-               } else if d.Type == dmp.DiffDelete {
-                       eh.Remove(charNum, charNum+Count(d.Text))
+               if d.Type == dmp.DiffDelete {
+                       eh.Remove(loc, loc.Move(Count(d.Text), eh.buf))
+               } else {
+                       if d.Type == dmp.DiffInsert {
+                               eh.Insert(loc, d.Text)
+                       }
+                       loc = loc.Move(Count(d.Text), eh.buf)
                }
-               charNum += Count(d.Text)
        }
 }
 
 // Insert creates an insert text event and executes it
-func (eh *EventHandler) Insert(start int, text string) {
+func (eh *EventHandler) Insert(start Loc, text string) {
        e := &TextEvent{
-               C:         eh.buf.Cursor,
+               C:         *eh.buf.cursors[eh.buf.curCursor],
                EventType: TextEventInsert,
-               Text:      text,
-               Start:     start,
-               End:       start + Count(text),
+               Deltas:    []Delta{{text, start, Loc{0, 0}}},
                Time:      time.Now(),
        }
        eh.Execute(e)
+       e.Deltas[0].End = start.Move(Count(text), eh.buf)
+       end := e.Deltas[0].End
+
+       for _, c := range eh.buf.cursors {
+               move := func(loc Loc) Loc {
+                       if start.Y != end.Y && loc.GreaterThan(start) {
+                               loc.Y += end.Y - start.Y
+                       } else if loc.Y == start.Y && loc.GreaterEqual(start) {
+                               loc = loc.Move(Count(text), eh.buf)
+                       }
+                       return loc
+               }
+               c.Loc = move(c.Loc)
+               c.CurSelection[0] = move(c.CurSelection[0])
+               c.CurSelection[1] = move(c.CurSelection[1])
+               c.OrigSelection[0] = move(c.OrigSelection[0])
+               c.OrigSelection[1] = move(c.OrigSelection[1])
+               c.LastVisualX = c.GetVisualX()
+       }
 }
 
 // Remove creates a remove text event and executes it
-func (eh *EventHandler) Remove(start, end int) {
+func (eh *EventHandler) Remove(start, end Loc) {
        e := &TextEvent{
-               C:         eh.buf.Cursor,
+               C:         *eh.buf.cursors[eh.buf.curCursor],
                EventType: TextEventRemove,
-               Start:     start,
-               End:       end,
+               Deltas:    []Delta{{"", start, end}},
+               Time:      time.Now(),
+       }
+       eh.Execute(e)
+
+       for _, c := range eh.buf.cursors {
+               move := func(loc Loc) Loc {
+                       if start.Y != end.Y && loc.GreaterThan(end) {
+                               loc.Y -= end.Y - start.Y
+                       } else if loc.Y == end.Y && loc.GreaterEqual(end) {
+                               loc = loc.Move(-Diff(start, end, eh.buf), eh.buf)
+                       }
+                       return loc
+               }
+               c.Loc = move(c.Loc)
+               c.CurSelection[0] = move(c.CurSelection[0])
+               c.CurSelection[1] = move(c.CurSelection[1])
+               c.OrigSelection[0] = move(c.OrigSelection[0])
+               c.OrigSelection[1] = move(c.OrigSelection[1])
+               c.LastVisualX = c.GetVisualX()
+       }
+}
+
+// MultipleReplace creates an multiple insertions executes them
+func (eh *EventHandler) MultipleReplace(deltas []Delta) {
+       e := &TextEvent{
+               C:         *eh.buf.cursors[eh.buf.curCursor],
+               EventType: TextEventReplace,
+               Deltas:    deltas,
                Time:      time.Now(),
        }
        eh.Execute(e)
 }
 
 // Replace deletes from start to end and replaces it with the given string
-func (eh *EventHandler) Replace(start, end int, replace string) {
+func (eh *EventHandler) Replace(start, end Loc, replace string) {
        eh.Remove(start, end)
        eh.Insert(start, replace)
 }
@@ -113,6 +181,17 @@ func (eh *EventHandler) Execute(t *TextEvent) {
                eh.RedoStack = new(Stack)
        }
        eh.UndoStack.Push(t)
+
+       for pl := range loadedPlugins {
+               ret, err := Call(pl+".onBeforeTextEvent", t)
+               if err != nil && !strings.HasPrefix(err.Error(), "function does not exist") {
+                       TermMessage(err)
+               }
+               if val, ok := ret.(lua.LBool); ok && val == lua.LFalse {
+                       return
+               }
+       }
+
        ExecuteTextEvent(t, eh.buf)
 }
 
@@ -157,8 +236,12 @@ func (eh *EventHandler) UndoOneEvent() {
 
        // Set the cursor in the right place
        teCursor := t.C
-       t.C = eh.buf.Cursor
-       eh.buf.Cursor.Goto(teCursor)
+       if teCursor.Num >= 0 && teCursor.Num < len(eh.buf.cursors) {
+               t.C = *eh.buf.cursors[teCursor.Num]
+               eh.buf.cursors[teCursor.Num].Goto(teCursor)
+       } else {
+               teCursor.Num = -1
+       }
 
        // Push it to the redo stack
        eh.RedoStack.Push(t)
@@ -200,8 +283,12 @@ func (eh *EventHandler) RedoOneEvent() {
        UndoTextEvent(t, eh.buf)
 
        teCursor := t.C
-       t.C = eh.buf.Cursor
-       eh.buf.Cursor.Goto(teCursor)
+       if teCursor.Num >= 0 && teCursor.Num < len(eh.buf.cursors) {
+               t.C = *eh.buf.cursors[teCursor.Num]
+               eh.buf.cursors[teCursor.Num].Goto(teCursor)
+       } else {
+               teCursor.Num = -1
+       }
 
        eh.UndoStack.Push(t)
 }