b := NewBufferFromString(strings.Join(before, "\n"), "", BTDefault)
- assert.NotEqual(b.GetName(), "")
- assert.Equal(b.ExternallyModified(), false)
- assert.Equal(b.Modified(), false)
- assert.Equal(b.NumCursors(), 1)
+ assert.NotEqual("", b.GetName())
+ assert.Equal(false, b.ExternallyModified())
+ assert.Equal(false, b.Modified())
+ assert.Equal(1, b.NumCursors())
checkText := func(lines []string) {
- assert.Equal(b.Bytes(), []byte(strings.Join(lines, "\n")))
- assert.Equal(b.LinesNum(), len(lines))
+ assert.Equal([]byte(strings.Join(lines, "\n")), b.Bytes())
+ assert.Equal(len(lines), b.LinesNum())
for i, s := range lines {
- assert.Equal(b.Line(i), s)
- assert.Equal(b.LineBytes(i), []byte(s))
+ assert.Equal(s, b.Line(i))
+ assert.Equal([]byte(s), b.LineBytes(i))
}
}
cursors = append(cursors, cursor)
}
- assert.Equal(b.NumCursors(), 1+len(operations))
+ assert.Equal(1+len(operations), b.NumCursors())
for i, op := range operations {
cursor := cursors[i]
+ b.SetCurCursor(cursor.Num)
cursor.DeleteSelection()
+ cursor.ResetSelection()
b.Insert(cursor.Loc, strings.Join(op.text, "\n"))
}
checkText(after)
- for _ = range operations {
- b.UndoOneEvent()
+ for b.UndoStack.Peek() != nil {
b.UndoOneEvent()
}
for i, op := range operations {
cursor := cursors[i]
- assert.Equal(cursor.Loc, op.start)
- assert.Equal(cursor.CurSelection[0], op.start)
- assert.Equal(cursor.CurSelection[1], op.end)
+ if !cursor.HasSelection() {
+ assert.Equal(op.start, cursor.Loc)
+ } else {
+ assert.Equal(op.start, cursor.CurSelection[0])
+ assert.Equal(op.end, cursor.CurSelection[1])
+ }
}
- for _ = range operations {
- b.RedoOneEvent()
+ for b.RedoStack.Peek() != nil {
b.RedoOneEvent()
}
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 {
}
// 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
// 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],
Deltas: []Delta{{text, start, Loc{0, 0}}},
Time: time.Now(),
}
- oldl := eh.buf.LinesNum()
- eh.Execute(e)
- linecount := eh.buf.LinesNum() - oldl
- textcount := utf8.RuneCount(text)
- lastnl := bytes.LastIndex(text, []byte{'\n'})
- var endX int
- var textX int
- if lastnl >= 0 {
- endX = utf8.RuneCount(text[lastnl+1:])
- textX = endX
- } else {
- endX = start.X + textcount
- textX = textcount
- }
-
- e.Deltas[0].End = clamp(Loc{endX, start.Y + linecount}, eh.buf.LineArray)
- end := e.Deltas[0].End
-
- for _, c := range eh.cursors {
- move := func(loc Loc) Loc {
- 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
- }
- c.Loc = move(c.Loc)
- c.Relocate()
- 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{
Deltas: []Delta{{[]byte{}, start, end}},
Time: time.Now(),
}
- eh.Execute(e)
-
- for _, c := range eh.cursors {
- move := func(loc Loc) Loc {
- 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.LastVisualX = c.GetVisualX()
- }
+ eh.DoTextEvent(e, true)
}
// MultipleReplace creates an multiple insertions executes them
}
eh.UndoOneEvent()
+ break
}
}
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
}
eh.RedoOneEvent()
+ break
}
}
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]
teCursor.Num = -1
}
+ // Modifies the text event
+ eh.UndoTextEvent(t)
+
eh.UndoStack.Push(t)
}