]> git.lizzy.rs Git - micro.git/blobdiff - internal/buffer/eventhandler.go
Fix buffer tests and selection bug
[micro.git] / internal / buffer / eventhandler.go
index eeb7b8129023f60f55f5eeb7f842e95411fec523..3708a13f9f454826d5e7a74eaba4ce3ff5c25f98 100644 (file)
@@ -1,10 +1,15 @@
 package buffer
 
 import (
+       "bytes"
        "time"
        "unicode/utf8"
 
        dmp "github.com/sergi/go-diff/diffmatchpatch"
+       "github.com/zyedidia/micro/internal/config"
+       ulua "github.com/zyedidia/micro/internal/lua"
+       "github.com/zyedidia/micro/internal/screen"
+       luar "layeh.com/gopher-luar"
 )
 
 const (
@@ -17,7 +22,7 @@ const (
        // TextEventReplace represents a replace event
        TextEventReplace = 0
 
-       undoThreshold = 500 // If two events are less than n milliseconds apart, undo both of them
+       undoThreshold = 1000 // If two events are less than n milliseconds apart, undo both of them
 )
 
 // TextEvent holds data for a manipulation on some text that can be undone
@@ -36,6 +41,73 @@ type Delta struct {
        End   Loc
 }
 
+// DoTextEvent runs a text event
+func (eh *EventHandler) DoTextEvent(t *TextEvent, useUndo bool) {
+       oldl := eh.buf.LinesNum()
+
+       if useUndo {
+               eh.Execute(t)
+       } else {
+               ExecuteTextEvent(t, eh.buf)
+       }
+
+       if len(t.Deltas) != 1 {
+               return
+       }
+
+       text := t.Deltas[0].Text
+       start := t.Deltas[0].Start
+       lastnl := -1
+       var endX int
+       var textX int
+       if t.EventType == TextEventInsert {
+               linecount := eh.buf.LinesNum() - oldl
+               textcount := utf8.RuneCount(text)
+               lastnl = bytes.LastIndex(text, []byte{'\n'})
+               if lastnl >= 0 {
+                       endX = utf8.RuneCount(text[lastnl+1:])
+                       textX = endX
+               } else {
+                       endX = start.X + textcount
+                       textX = textcount
+               }
+               t.Deltas[0].End = clamp(Loc{endX, start.Y + linecount}, eh.buf.LineArray)
+       }
+       end := t.Deltas[0].End
+
+       for _, c := range eh.cursors {
+               move := func(loc Loc) Loc {
+                       if t.EventType == TextEventInsert {
+                               if start.Y != loc.Y && loc.GreaterThan(start) {
+                                       loc.Y += end.Y - start.Y
+                               } else if loc.Y == start.Y && loc.GreaterEqual(start) {
+                                       loc.Y += end.Y - start.Y
+                                       if lastnl >= 0 {
+                                               loc.X += textX - start.X
+                                       } else {
+                                               loc.X += textX
+                                       }
+                               }
+                               return loc
+                       } else {
+                               if loc.Y != end.Y && loc.GreaterThan(end) {
+                                       loc.Y -= end.Y - start.Y
+                               } else if loc.Y == end.Y && loc.GreaterEqual(end) {
+                                       loc = loc.MoveLA(-DiffLA(start, end, eh.buf.LineArray), eh.buf.LineArray)
+                               }
+                               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.Relocate()
+               c.LastVisualX = c.GetVisualX()
+       }
+}
+
 // ExecuteTextEvent runs a text event
 func ExecuteTextEvent(t *TextEvent, buf *SharedBuffer) {
        if t.EventType == TextEventInsert {
@@ -60,9 +132,9 @@ func ExecuteTextEvent(t *TextEvent, buf *SharedBuffer) {
 }
 
 // UndoTextEvent undoes a text event
-func UndoTextEvent(t *TextEvent, buf *SharedBuffer) {
+func (eh *EventHandler) UndoTextEvent(t *TextEvent) {
        t.EventType = -t.EventType
-       ExecuteTextEvent(t, buf)
+       eh.DoTextEvent(t, false)
 }
 
 // EventHandler executes text manipulations and allows undoing and redoing
@@ -107,60 +179,38 @@ func (eh *EventHandler) ApplyDiff(new string) {
 // Insert creates an insert text event and executes it
 func (eh *EventHandler) Insert(start Loc, textStr string) {
        text := []byte(textStr)
+       eh.InsertBytes(start, text)
+}
+
+// InsertBytes creates an insert text event and executes it
+func (eh *EventHandler) InsertBytes(start Loc, text []byte) {
+       if len(text) == 0 {
+               return
+       }
+       start = clamp(start, eh.buf.LineArray)
        e := &TextEvent{
                C:         *eh.cursors[eh.active],
                EventType: TextEventInsert,
                Deltas:    []Delta{{text, start, Loc{0, 0}}},
                Time:      time.Now(),
        }
-       eh.Execute(e)
-       e.Deltas[0].End = start.MoveLA(utf8.RuneCount(text), eh.buf.LineArray)
-       end := e.Deltas[0].End
-
-       for _, c := range eh.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.MoveLA(utf8.RuneCount(text), eh.buf.LineArray)
-                       }
-                       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()
-       }
+       eh.DoTextEvent(e, true)
 }
 
 // Remove creates a remove text event and executes it
 func (eh *EventHandler) Remove(start, end Loc) {
+       if start == end {
+               return
+       }
+       start = clamp(start, eh.buf.LineArray)
+       end = clamp(end, eh.buf.LineArray)
        e := &TextEvent{
                C:         *eh.cursors[eh.active],
                EventType: TextEventRemove,
                Deltas:    []Delta{{[]byte{}, start, end}},
                Time:      time.Now(),
        }
-       eh.Execute(e)
-
-       for _, c := range eh.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.MoveLA(-DiffLA(start, end, eh.buf.LineArray), eh.buf.LineArray)
-                       }
-                       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()
-       }
+       eh.DoTextEvent(e, true)
 }
 
 // MultipleReplace creates an multiple insertions executes them
@@ -187,16 +237,14 @@ func (eh *EventHandler) Execute(t *TextEvent) {
        }
        eh.UndoStack.Push(t)
 
-       // TODO: Call plugins on text events
-       // for pl := range loadedPlugins {
-       //      ret, err := Call(pl+".onBeforeTextEvent", t)
-       //      if err != nil && !strings.HasPrefix(err.Error(), "function does not exist") {
-       //              screen.TermMessage(err)
-       //      }
-       //      if val, ok := ret.(lua.LBool); ok && val == lua.LFalse {
-       //              return
-       //      }
-       // }
+       b, err := config.RunPluginFnBool("onBeforeTextEvent", luar.New(ulua.L, eh.buf), luar.New(ulua.L, t))
+       if err != nil {
+               screen.TermMessage(err)
+       }
+
+       if !b {
+               return
+       }
 
        ExecuteTextEvent(t, eh.buf)
 }
@@ -209,8 +257,7 @@ func (eh *EventHandler) Undo() {
        }
 
        startTime := t.Time.UnixNano() / int64(time.Millisecond)
-
-       eh.UndoOneEvent()
+       endTime := startTime - (startTime % undoThreshold)
 
        for {
                t = eh.UndoStack.Peek()
@@ -218,12 +265,12 @@ func (eh *EventHandler) Undo() {
                        return
                }
 
-               if startTime-(t.Time.UnixNano()/int64(time.Millisecond)) > undoThreshold {
+               if t.Time.UnixNano()/int64(time.Millisecond) < endTime {
                        return
                }
-               startTime = t.Time.UnixNano() / int64(time.Millisecond)
 
                eh.UndoOneEvent()
+               break
        }
 }
 
@@ -235,10 +282,9 @@ func (eh *EventHandler) UndoOneEvent() {
        if t == nil {
                return
        }
-
        // Undo it
        // Modifies the text event
-       UndoTextEvent(t, eh.buf)
+       eh.UndoTextEvent(t)
 
        // Set the cursor in the right place
        teCursor := t.C
@@ -261,8 +307,7 @@ func (eh *EventHandler) Redo() {
        }
 
        startTime := t.Time.UnixNano() / int64(time.Millisecond)
-
-       eh.RedoOneEvent()
+       endTime := startTime - (startTime % undoThreshold) + undoThreshold
 
        for {
                t = eh.RedoStack.Peek()
@@ -270,11 +315,12 @@ func (eh *EventHandler) Redo() {
                        return
                }
 
-               if (t.Time.UnixNano()/int64(time.Millisecond))-startTime > undoThreshold {
+               if t.Time.UnixNano()/int64(time.Millisecond) > endTime {
                        return
                }
 
                eh.RedoOneEvent()
+               break
        }
 }
 
@@ -285,9 +331,6 @@ func (eh *EventHandler) RedoOneEvent() {
                return
        }
 
-       // Modifies the text event
-       UndoTextEvent(t, eh.buf)
-
        teCursor := t.C
        if teCursor.Num >= 0 && teCursor.Num < len(eh.cursors) {
                t.C = *eh.cursors[teCursor.Num]
@@ -296,5 +339,8 @@ func (eh *EventHandler) RedoOneEvent() {
                teCursor.Num = -1
        }
 
+       // Modifies the text event
+       eh.UndoTextEvent(t)
+
        eh.UndoStack.Push(t)
 }