-
Notifications
You must be signed in to change notification settings - Fork 87
/
undo.go
137 lines (128 loc) · 3.22 KB
/
undo.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
// Copyright 2013 The lime Authors.
// Use of this source code is governed by a 2-clause
// BSD-style license that can be found in the LICENSE file.
package backend
type (
UndoStack struct {
position int
actions []*Edit
}
)
// Adds the provided Edit object to the UndoStack, potentially
// destroying the old redo stack if one had been created.
// TODO(.): It would be nice with branched undo histories
func (us *UndoStack) Add(a *Edit) {
if us.position != len(us.actions) {
us.actions = us.actions[0:us.position]
}
us.actions = append(us.actions, a)
us.position++
}
// index returns the real index in the UndoStack of an undo item
// relative to the current position.
//
// When modifying_only is set to true, only actions actually modifying
// the buffer (as opposed to just moving the cursor) are counted as an
// index. Also see comment in Undo.
func (us *UndoStack) index(relative int, modifying_only bool) int {
dir := -1
i := us.position
if relative > 0 {
dir = 1
} else {
i--
}
relative *= dir
for ; i >= 0 && i < len(us.actions) && relative > 0; i += dir {
if modifying_only {
if us.actions[i].composite.Len() != 0 {
relative--
}
} else {
relative--
}
}
if i >= 0 && i < len(us.actions) {
return i
} else {
return -1
}
}
// Reverts the last action on the UndoStack.
//
// When the argument "hard" is set to true,
// the "last action" will be the last action that
// modified the contents of the buffer (rather than just
// changing the cursor position). In this case, all
// actions between the current action and the last "hard"
// action will be reverted.
func (us *UndoStack) Undo(hard bool) {
if us.position <= 0 {
// Nothing to undo
return
}
to := us.index(0, hard)
if to == -1 {
to = 0
}
for us.position > to {
us.position--
us.actions[us.position].Undo()
}
}
// Re-applies the next action in the undo stack
// if there are any actions on the stack that had
// been undone.
//
// See comment in Undo regarding the use of "hard".
func (us *UndoStack) Redo(hard bool) {
if us.position >= len(us.actions) {
// No more actions to redo
return
}
to := us.index(1, hard)
if to == -1 {
to = len(us.actions)
}
for us.position < to {
us.actions[us.position].Apply()
us.position++
}
}
// Returns the current position in the UndoStack.
func (us *UndoStack) Position() int {
return us.position
}
// Glues all edits from the position given by mark,
// to the current position in the UndoStack, replacing
// them by a single entry which will now be composite
// of all those other actions.
//
// In other words, after the glue operation
// a single "undo" operation will then undo all of those edits
// and a single redo after that will redo them all again.
func (us *UndoStack) GlueFrom(mark int) {
if mark >= us.position {
return
}
var e Edit
e.command = "sequence"
type entry struct {
name string
args Args
}
e.v = us.actions[mark].v
e.savedSel.AddAll(us.actions[mark].savedSel.Regions())
entries := make([]entry, us.position-mark)
for i := range entries {
a := us.actions[i+mark]
entries[i].name = a.command
entries[i].args = a.args
e.composite.Add(a)
}
us.position = mark
us.actions = us.actions[:mark+1]
e.args = make(Args)
e.args["commands"] = entries
us.Add(&e)
}