turbo-editors/turbo-corepublic Fork 0
v1.0.1
Commits
Clone
git clone https://git.rickub.com/turbo-editors/turbo-core.git
git clone ssh://git@rickub.com/turbo-editors/turbo-core.git

Host key fingerprint (ed25519): SHA256:iycHnxEyq0Q7uyVpB7JlznP0G7JrTPXLYRcAU5CSLhc — verify it before your first connect.

undo_test.go · 245 lines · 5.7 KBGo Blame HistoryRaw
🛟 Updated. 28d5985 k33g 19h ago1package buffer
2
3import "testing"
4
5func TestUndoOnAFreshBufferReportsNothingToDo(t *testing.T) {
6 b := NewFromString("abc")
7
8 if b.CanUndo() {
9 t.Error("CanUndo() = true on a freshly loaded buffer")
10 }
11 if b.Undo() {
12 t.Error("Undo() = true, want false with an empty history")
13 }
14 if b.CanRedo() || b.Redo() {
15 t.Error("a fresh buffer must have nothing to redo either")
16 }
17}
18
19func TestUndoRevertsAnInsertionAndRestoresTheCursor(t *testing.T) {
20 b := NewFromString("hello")
21 b.MoveBufferEnd()
22
23 b.Insert(" world")
24 if got := b.Text(); got != "hello world" {
25 t.Fatalf("Text() = %q before undo, want %q", got, "hello world")
26 }
27
28 if !b.Undo() {
29 t.Fatal("Undo() = false, want true")
30 }
31 if got := b.Text(); got != "hello" {
32 t.Errorf("Text() = %q, want %q", got, "hello")
33 }
34 if got := b.Cursor(); got != (Position{Line: 0, Col: 5}) {
35 t.Errorf("Cursor() = %+v, want where it was before the insertion", got)
36 }
37}
38
39func TestUndoRevertsAMultiLineDeletion(t *testing.T) {
40 b := NewFromString("one\ntwo\nthree")
41
42 b.DeleteRange(Range{Start: Position{Line: 0, Col: 1}, End: Position{Line: 2, Col: 2}})
43 b.Undo()
44
45 if got := b.Text(); got != "one\ntwo\nthree" {
46 t.Errorf("Text() = %q, want the original text back", got)
47 }
48}
49
50func TestRedoReappliesTheChange(t *testing.T) {
51 b := NewFromString("one\ntwo")
52 b.MoveBufferEnd()
53
54 b.Insert("\nthree")
55 b.Undo()
56
57 if !b.Redo() {
58 t.Fatal("Redo() = false, want true")
59 }
60 if got := b.Text(); got != "one\ntwo\nthree" {
61 t.Errorf("Text() = %q, want the change back", got)
62 }
63 if got := b.Cursor(); got != (Position{Line: 2, Col: 5}) {
64 t.Errorf("Cursor() = %+v, want where it was after the insertion", got)
65 }
66}
67
68func TestANewEditClearsTheRedoStack(t *testing.T) {
69 b := NewFromString("a")
70 b.MoveBufferEnd()
71 b.Insert("b")
72 b.Undo()
73
74 b.Insert("c")
75
76 if b.CanRedo() {
77 t.Error("CanRedo() = true after a new edit, want the redo stack cleared")
78 }
79 if got := b.Text(); got != "ac" {
80 t.Errorf("Text() = %q, want %q", got, "ac")
81 }
82}
83
84func TestUndoWalksBackThroughSeveralSteps(t *testing.T) {
85 b := NewFromString("")
86 b.Insert("one")
87 b.Insert("-two")
88 b.Insert("-three")
89
90 steps := []string{"one-two", "one", ""}
91 for i, want := range steps {
92 if !b.Undo() {
93 t.Fatalf("Undo() = false at step %d", i)
94 }
95 if got := b.Text(); got != want {
96 t.Errorf("after undo %d, Text() = %q, want %q", i+1, got, want)
97 }
98 }
99 if b.Undo() {
100 t.Error("Undo() = true once the history is exhausted")
101 }
102}
103
104func TestTypedCharactersUndoAsOneStep(t *testing.T) {
105 b := NewFromString("")
106 for _, r := range "func" {
107 b.InsertRune(r)
108 }
109
110 if got := b.Text(); got != "func" {
111 t.Fatalf("Text() = %q, want %q", got, "func")
112 }
113
114 if !b.Undo() {
115 t.Fatal("Undo() = false, want true")
116 }
117 if got := b.Text(); got != "" {
118 t.Errorf("Text() = %q, want the whole typed run removed in one step", got)
119 }
120 if b.CanUndo() {
121 t.Error("the run of typing must have produced a single undo entry")
122 }
123}
124
125func TestMovingTheCursorEndsTheTypedRun(t *testing.T) {
126 b := NewFromString("")
127 b.InsertRune('a')
128 b.InsertRune('b')
129 b.MoveLeft()
130 b.InsertRune('c')
131
132 if got := b.Text(); got != "acb" {
133 t.Fatalf("Text() = %q, want %q", got, "acb")
134 }
135
136 b.Undo()
137 if got := b.Text(); got != "ab" {
138 t.Errorf("Text() = %q, want only the character typed after the move removed", got)
139 }
140 b.Undo()
141 if got := b.Text(); got != "" {
142 t.Errorf("Text() = %q, want the earlier run removed by the second undo", got)
143 }
144}
145
146func TestBackspacesUndoAsOneStep(t *testing.T) {
147 b := NewFromString("abcdef")
148 b.MoveBufferEnd()
149
150 for range 3 {
151 b.Backspace()
152 }
153 if got := b.Text(); got != "abc" {
154 t.Fatalf("Text() = %q, want %q", got, "abc")
155 }
156
157 if !b.Undo() {
158 t.Fatal("Undo() = false, want true")
159 }
160 if got := b.Text(); got != "abcdef" {
161 t.Errorf("Text() = %q, want the whole run of backspaces undone at once", got)
162 }
163 if got := b.Cursor(); got != (Position{Line: 0, Col: 6}) {
164 t.Errorf("Cursor() = %+v, want where the run of backspaces started", got)
165 }
166 if b.CanUndo() {
167 t.Error("the run of backspaces must have produced a single undo entry")
168 }
169}
170
171func TestTypingAndBackspacingDoNotMergeTogether(t *testing.T) {
172 b := NewFromString("")
173 b.InsertRune('a')
174 b.InsertRune('b')
175 b.Backspace()
176
177 if got := b.Text(); got != "a" {
178 t.Fatalf("Text() = %q, want %q", got, "a")
179 }
180
181 b.Undo()
182 if got := b.Text(); got != "ab" {
183 t.Errorf("Text() = %q, want only the backspace undone", got)
184 }
185 b.Undo()
186 if got := b.Text(); got != "" {
187 t.Errorf("Text() = %q, want the typing undone by the second step", got)
188 }
189}
190
191func TestUndoRedoRoundTripAcrossManyEdits(t *testing.T) {
192 b := NewFromString("package main\n")
193 b.MoveBufferEnd()
194 b.Insert("\nfunc main() {\n}")
195 b.SetCursor(Position{Line: 2, Col: 13})
196 b.InsertNewlineAndIndent()
197 b.Insert("\tprintln(1)")
198
199 final := b.Text()
200 depth := 0
201 for b.Undo() {
202 depth++
203 }
204 if got := b.Text(); got != "package main\n" {
205 t.Fatalf("after undoing everything, Text() = %q, want the original", got)
206 }
207
208 for range depth {
209 if !b.Redo() {
210 t.Fatal("Redo() ran out before the history did")
211 }
212 }
213 if got := b.Text(); got != final {
214 t.Errorf("after redoing everything, Text() = %q, want %q", got, final)
215 }
216}
217
218func TestUndoHistoryIsCappedAtItsMaximumDepth(t *testing.T) {
219 b := NewFromString("")
220 for i := range maxUndoDepth + 50 {
221 b.Insert(string(rune('a' + i%26)))
222 }
223
224 steps := 0
225 for b.Undo() {
226 steps++
227 }
228 if steps != maxUndoDepth {
229 t.Errorf("undid %d steps, want the history capped at %d", steps, maxUndoDepth)
230 }
231}
232
233func TestUndoClearsTheSelection(t *testing.T) {
234 b := NewFromString("abc")
235 b.MoveBufferEnd()
236 b.Insert("d")
237 b.StartSelection()
238 b.SetCursorKeepingSelection(Position{Line: 0, Col: 0})
239
240 b.Undo()
241
242 if _, ok := b.Selection(); ok {
243 t.Error("a selection must not survive an undo, whose text it may no longer match")
244 }
245}