micro/internal/buffer/eventhandler.go

306 lines
7.3 KiB
Go
Raw Normal View History

2018-08-27 22:53:10 +03:00
package buffer
2016-03-20 01:16:10 +03:00
import (
"time"
2018-08-26 06:06:44 +03:00
"unicode/utf8"
dmp "github.com/sergi/go-diff/diffmatchpatch"
2020-01-31 08:56:15 +03:00
"github.com/zyedidia/micro/internal/config"
ulua "github.com/zyedidia/micro/internal/lua"
"github.com/zyedidia/micro/internal/screen"
luar "layeh.com/gopher-luar"
2016-03-20 01:16:10 +03:00
)
const (
2016-03-25 19:14:22 +03:00
// Opposite and undoing events must have opposite values
// TextEventInsert represents an insertion event
2016-03-20 01:16:10 +03:00
TextEventInsert = 1
// TextEventRemove represents a deletion event
TextEventRemove = -1
// TextEventReplace represents a replace event
2017-04-16 18:11:04 +03:00
TextEventReplace = 0
2018-08-27 22:53:10 +03:00
undoThreshold = 1000 // If two events are less than n milliseconds apart, undo both of them
2016-03-20 01:16:10 +03:00
)
// TextEvent holds data for a manipulation on some text that can be undone
type TextEvent struct {
2016-05-29 18:02:56 +03:00
C Cursor
2016-03-20 01:16:10 +03:00
2016-05-29 18:02:56 +03:00
EventType int
2017-04-16 18:11:04 +03:00
Deltas []Delta
2016-05-29 18:02:56 +03:00
Time time.Time
2016-03-20 01:16:10 +03:00
}
2018-01-05 01:14:51 +03:00
// A Delta is a change to the buffer
2017-04-16 18:11:04 +03:00
type Delta struct {
2018-08-26 06:06:44 +03:00
Text []byte
2017-04-16 18:11:04 +03:00
Start Loc
End Loc
}
2016-03-20 01:16:10 +03:00
// ExecuteTextEvent runs a text event
2019-01-15 00:52:25 +03:00
func ExecuteTextEvent(t *TextEvent, buf *SharedBuffer) {
2016-05-29 18:02:56 +03:00
if t.EventType == TextEventInsert {
2017-04-16 18:11:04 +03:00
for _, d := range t.Deltas {
2018-08-26 06:06:44 +03:00
buf.insert(d.Start, d.Text)
2017-04-16 18:11:04 +03:00
}
2016-05-29 18:02:56 +03:00
} else if t.EventType == TextEventRemove {
2017-04-16 18:11:04 +03:00
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)
2018-08-26 06:06:44 +03:00
buf.insert(d.Start, d.Text)
2017-09-04 23:21:08 +03:00
t.Deltas[i].Start = d.Start
2018-08-26 06:06:44 +03:00
t.Deltas[i].End = Loc{d.Start.X + utf8.RuneCount(d.Text), d.Start.Y}
2017-09-04 23:21:08 +03:00
}
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]
2017-04-16 18:11:04 +03:00
}
2016-03-20 01:16:10 +03:00
}
}
// UndoTextEvent undoes a text event
2019-01-15 00:52:25 +03:00
func UndoTextEvent(t *TextEvent, buf *SharedBuffer) {
2016-05-29 18:02:56 +03:00
t.EventType = -t.EventType
2016-05-01 23:45:23 +03:00
ExecuteTextEvent(t, buf)
2016-03-20 01:16:10 +03:00
}
// EventHandler executes text manipulations and allows undoing and redoing
type EventHandler struct {
2019-01-15 00:52:25 +03:00
buf *SharedBuffer
cursors []*Cursor
2019-01-17 01:52:30 +03:00
active int
2018-08-26 06:06:44 +03:00
UndoStack *TEStack
RedoStack *TEStack
2016-03-20 01:16:10 +03:00
}
// NewEventHandler returns a new EventHandler
2019-01-15 00:52:25 +03:00
func NewEventHandler(buf *SharedBuffer, cursors []*Cursor) *EventHandler {
2016-03-20 01:16:10 +03:00
eh := new(EventHandler)
2018-08-26 06:06:44 +03:00
eh.UndoStack = new(TEStack)
eh.RedoStack = new(TEStack)
2019-01-15 00:52:25 +03:00
eh.buf = buf
eh.cursors = cursors
2016-03-20 01:16:10 +03:00
return eh
}
// ApplyDiff takes a string and runs the necessary insertion and deletion events to make
// the buffer equal to that string
// 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) {
differ := dmp.New()
diff := differ.DiffMain(string(eh.buf.Bytes()), new, false)
2016-06-07 18:43:28 +03:00
loc := eh.buf.Start()
for _, d := range diff {
if d.Type == dmp.DiffDelete {
2019-01-15 00:52:25 +03:00
eh.Remove(loc, loc.MoveLA(utf8.RuneCountInString(d.Text), eh.buf.LineArray))
} else {
if d.Type == dmp.DiffInsert {
eh.Insert(loc, d.Text)
}
2019-01-15 00:52:25 +03:00
loc = loc.MoveLA(utf8.RuneCountInString(d.Text), eh.buf.LineArray)
}
}
}
2016-03-20 01:16:10 +03:00
// 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) {
2016-03-20 01:16:10 +03:00
e := &TextEvent{
2019-01-17 01:52:30 +03:00
C: *eh.cursors[eh.active],
2016-05-29 18:02:56 +03:00
EventType: TextEventInsert,
2017-08-08 18:30:09 +03:00
Deltas: []Delta{{text, start, Loc{0, 0}}},
2016-05-29 18:02:56 +03:00
Time: time.Now(),
2016-03-20 01:16:10 +03:00
}
eh.Execute(e)
2020-02-09 08:03:03 +03:00
textcount := utf8.RuneCount(text)
e.Deltas[0].End = start.MoveLA(textcount, 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) {
2020-02-09 08:03:03 +03:00
loc = loc.MoveLA(textcount, 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()
}
2016-03-20 01:16:10 +03:00
}
// Remove creates a remove text event and executes it
2016-06-07 18:43:28 +03:00
func (eh *EventHandler) Remove(start, end Loc) {
2016-03-20 01:16:10 +03:00
e := &TextEvent{
2019-01-17 01:52:30 +03:00
C: *eh.cursors[eh.active],
2016-05-29 18:02:56 +03:00
EventType: TextEventRemove,
2018-08-26 06:06:44 +03:00
Deltas: []Delta{{[]byte{}, start, end}},
2017-04-16 18:11:04 +03:00
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) {
2019-01-15 00:52:25 +03:00
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()
}
2017-04-16 18:11:04 +03:00
}
// MultipleReplace creates an multiple insertions executes them
2017-04-16 18:11:04 +03:00
func (eh *EventHandler) MultipleReplace(deltas []Delta) {
e := &TextEvent{
2019-01-17 01:52:30 +03:00
C: *eh.cursors[eh.active],
2017-04-16 18:11:04 +03:00
EventType: TextEventReplace,
Deltas: deltas,
2016-05-29 18:02:56 +03:00
Time: time.Now(),
2016-03-20 01:16:10 +03:00
}
eh.Execute(e)
}
2016-04-03 22:22:31 +03:00
// Replace deletes from start to end and replaces it with the given string
func (eh *EventHandler) Replace(start, end Loc, replace string) {
2016-04-03 22:22:31 +03:00
eh.Remove(start, end)
eh.Insert(start, replace)
}
2016-03-20 01:16:10 +03:00
// Execute a textevent and add it to the undo stack
func (eh *EventHandler) Execute(t *TextEvent) {
2016-05-29 18:02:56 +03:00
if eh.RedoStack.Len() > 0 {
2018-08-26 06:06:44 +03:00
eh.RedoStack = new(TEStack)
2016-03-20 03:32:14 +03:00
}
2016-05-29 18:02:56 +03:00
eh.UndoStack.Push(t)
2020-01-31 22:21:27 +03:00
b, err := config.RunPluginFnBool("onBeforeTextEvent", luar.New(ulua.L, eh.buf), luar.New(ulua.L, t))
2020-01-31 08:56:15 +03:00
if err != nil {
screen.TermMessage(err)
}
if !b {
return
}
ExecuteTextEvent(t, eh.buf)
2016-03-20 01:16:10 +03:00
}
// Undo the first event in the undo stack
func (eh *EventHandler) Undo() {
2016-05-29 18:02:56 +03:00
t := eh.UndoStack.Peek()
2016-04-01 16:54:10 +03:00
if t == nil {
return
}
2016-05-29 18:02:56 +03:00
startTime := t.Time.UnixNano() / int64(time.Millisecond)
endTime := startTime - (startTime % undoThreshold)
2016-04-01 16:54:10 +03:00
for {
2016-05-29 18:02:56 +03:00
t = eh.UndoStack.Peek()
2016-04-01 16:54:10 +03:00
if t == nil {
return
}
if t.Time.UnixNano()/int64(time.Millisecond) < endTime {
2016-04-01 16:54:10 +03:00
return
}
eh.UndoOneEvent()
}
}
// UndoOneEvent undoes one event
func (eh *EventHandler) UndoOneEvent() {
// This event should be undone
// Pop it off the stack
2016-05-29 18:02:56 +03:00
t := eh.UndoStack.Pop()
2016-03-20 01:16:10 +03:00
if t == nil {
return
}
2016-04-01 16:54:10 +03:00
// Undo it
2016-03-20 01:16:10 +03:00
// Modifies the text event
2016-05-29 18:02:56 +03:00
UndoTextEvent(t, eh.buf)
2016-03-20 01:16:10 +03:00
2016-04-01 16:54:10 +03:00
// Set the cursor in the right place
2016-05-29 18:02:56 +03:00
teCursor := t.C
if teCursor.Num >= 0 && teCursor.Num < len(eh.cursors) {
t.C = *eh.cursors[teCursor.Num]
eh.cursors[teCursor.Num].Goto(teCursor)
} else {
teCursor.Num = -1
}
2016-03-20 03:32:14 +03:00
2016-04-01 16:54:10 +03:00
// Push it to the redo stack
2016-05-29 18:02:56 +03:00
eh.RedoStack.Push(t)
2016-03-20 01:16:10 +03:00
}
// Redo the first event in the redo stack
func (eh *EventHandler) Redo() {
2016-05-29 18:02:56 +03:00
t := eh.RedoStack.Peek()
2016-04-01 16:54:10 +03:00
if t == nil {
return
}
2016-05-29 18:02:56 +03:00
startTime := t.Time.UnixNano() / int64(time.Millisecond)
endTime := startTime - (startTime % undoThreshold) + undoThreshold
2016-04-01 16:54:10 +03:00
for {
2016-05-29 18:02:56 +03:00
t = eh.RedoStack.Peek()
2016-04-01 16:54:10 +03:00
if t == nil {
return
}
if t.Time.UnixNano()/int64(time.Millisecond) > endTime {
2016-04-01 16:54:10 +03:00
return
}
eh.RedoOneEvent()
}
}
// RedoOneEvent redoes one event
func (eh *EventHandler) RedoOneEvent() {
2016-05-29 18:02:56 +03:00
t := eh.RedoStack.Pop()
2016-03-20 01:16:10 +03:00
if t == nil {
return
}
// Modifies the text event
2016-05-29 18:02:56 +03:00
UndoTextEvent(t, eh.buf)
2016-03-20 03:32:14 +03:00
2016-05-29 18:02:56 +03:00
teCursor := t.C
if teCursor.Num >= 0 && teCursor.Num < len(eh.cursors) {
t.C = *eh.cursors[teCursor.Num]
eh.cursors[teCursor.Num].Goto(teCursor)
} else {
teCursor.Num = -1
}
2016-03-20 03:32:14 +03:00
2016-05-29 18:02:56 +03:00
eh.UndoStack.Push(t)
2016-03-20 01:16:10 +03:00
}