Line | Branch | Exec | Source |
---|---|---|---|
1 | #include <stdlib.h> | ||
2 | #include <string.h> | ||
3 | #include "change.h" | ||
4 | #include "buffer.h" | ||
5 | #include "command/error.h" | ||
6 | #include "edit.h" | ||
7 | #include "editor.h" | ||
8 | #include "util/debug.h" | ||
9 | #include "util/xmalloc.h" | ||
10 | #include "window.h" | ||
11 | |||
12 | // NOLINTBEGIN(*-avoid-non-const-global-variables) | ||
13 | static ChangeMergeEnum change_merge; | ||
14 | static ChangeMergeEnum prev_change_merge; | ||
15 | // This doesn't need to be local to buffer because commands are atomic | ||
16 | static Change *change_barrier; | ||
17 | // NOLINTEND(*-avoid-non-const-global-variables) | ||
18 | |||
19 | 335 | static Change *alloc_change(void) | |
20 | { | ||
21 | 335 | return xnew0(Change, 1); | |
22 | } | ||
23 | |||
24 | 332 | static void add_change(Buffer *buffer, Change *change) | |
25 | { | ||
26 | 332 | Change *head = buffer->cur_change; | |
27 | 332 | change->next = head; | |
28 | 332 | head->prev = xrenew(head->prev, head->nr_prev + 1); | |
29 | 332 | head->prev[head->nr_prev++] = change; | |
30 | 332 | buffer->cur_change = change; | |
31 | 332 | } | |
32 | |||
33 | 56 | static bool is_change_chain_barrier(const Change *change) | |
34 | { | ||
35 |
4/4✓ Branch 0 (2→3) taken 21 times.
✓ Branch 1 (2→4) taken 35 times.
✓ Branch 2 (3→4) taken 15 times.
✓ Branch 3 (3→5) taken 6 times.
|
56 | return !change->ins_count && !change->del_count; |
36 | } | ||
37 | |||
38 | 270 | static Change *new_change(Buffer *buffer) | |
39 | { | ||
40 |
2/2✓ Branch 0 (2→3) taken 31 times.
✓ Branch 1 (2→5) taken 239 times.
|
270 | if (change_barrier) { |
41 | /* | ||
42 | * We are recording series of changes (:replace for example) | ||
43 | * and now we have just made the first change so we have to | ||
44 | * mark beginning of the chain. | ||
45 | * | ||
46 | * We could have done this before when starting the change | ||
47 | * chain but then we may have ended up with an empty chain. | ||
48 | * We don't want to record empty changes ever. | ||
49 | */ | ||
50 | 31 | add_change(buffer, change_barrier); | |
51 | 31 | change_barrier = NULL; | |
52 | } | ||
53 | |||
54 | 270 | Change *change = alloc_change(); | |
55 | 270 | add_change(buffer, change); | |
56 | 270 | return change; | |
57 | } | ||
58 | |||
59 | 270 | static size_t buffer_offset(const View *view) | |
60 | { | ||
61 | 270 | return block_iter_get_offset(&view->cursor); | |
62 | } | ||
63 | |||
64 | 264 | static void record_insert(View *view, size_t len) | |
65 | { | ||
66 | 264 | Change *change = view->buffer->cur_change; | |
67 | 264 | BUG_ON(!len); | |
68 | 264 | if ( | |
69 |
2/2✓ Branch 0 (4→5) taken 211 times.
✓ Branch 1 (4→9) taken 53 times.
|
264 | change_merge == prev_change_merge |
70 |
2/2✓ Branch 0 (5→6) taken 88 times.
✓ Branch 1 (5→9) taken 123 times.
|
211 | && change_merge == CHANGE_MERGE_INSERT |
71 | ) { | ||
72 | 88 | BUG_ON(change->del_count); | |
73 | 88 | change->ins_count += len; | |
74 | 88 | return; | |
75 | } | ||
76 | |||
77 | 176 | change = new_change(view->buffer); | |
78 | 176 | change->offset = buffer_offset(view); | |
79 | 176 | change->ins_count = len; | |
80 | } | ||
81 | |||
82 | 54 | static void record_delete(View *view, char *buf, size_t len, bool move_after) | |
83 | { | ||
84 | 54 | BUG_ON(!len); | |
85 | 54 | BUG_ON(!buf); | |
86 | |||
87 | 54 | Change *change = view->buffer->cur_change; | |
88 |
2/2✓ Branch 0 (6→7) taken 43 times.
✓ Branch 1 (6→13) taken 11 times.
|
54 | if (change_merge == prev_change_merge) { |
89 |
2/2✓ Branch 0 (7→8) taken 1 times.
✓ Branch 1 (7→10) taken 42 times.
|
43 | if (change_merge == CHANGE_MERGE_DELETE) { |
90 | 1 | change->buf = xrealloc(change->buf, change->del_count + len); | |
91 | 1 | memcpy(change->buf + change->del_count, buf, len); | |
92 | 1 | change->del_count += len; | |
93 | 1 | free(buf); | |
94 | 1 | return; | |
95 | } | ||
96 |
2/2✓ Branch 0 (10→11) taken 1 times.
✓ Branch 1 (10→13) taken 41 times.
|
42 | if (change_merge == CHANGE_MERGE_ERASE) { |
97 | 1 | buf = xrealloc(buf, len + change->del_count); | |
98 | 1 | memcpy(buf + len, change->buf, change->del_count); | |
99 | 1 | change->del_count += len; | |
100 | 1 | free(change->buf); | |
101 | 1 | change->buf = buf; | |
102 | 1 | change->offset -= len; | |
103 | 1 | return; | |
104 | } | ||
105 | } | ||
106 | |||
107 | 52 | change = new_change(view->buffer); | |
108 | 52 | change->offset = buffer_offset(view); | |
109 | 52 | change->del_count = len; | |
110 | 52 | change->move_after = move_after; | |
111 | 52 | change->buf = buf; | |
112 | } | ||
113 | |||
114 | 42 | static void record_replace(View *view, char *deleted, size_t del_count, size_t ins_count) | |
115 | { | ||
116 | 42 | BUG_ON(del_count && !deleted); | |
117 | 42 | BUG_ON(!del_count && deleted); | |
118 | 42 | BUG_ON(!del_count && !ins_count); | |
119 | |||
120 | 42 | Change *change = new_change(view->buffer); | |
121 | 42 | change->offset = buffer_offset(view); | |
122 | 42 | change->ins_count = ins_count; | |
123 | 42 | change->del_count = del_count; | |
124 | 42 | change->buf = deleted; | |
125 | 42 | } | |
126 | |||
127 | 6719 | void begin_change(ChangeMergeEnum m) | |
128 | { | ||
129 | 6719 | change_merge = m; | |
130 | 6719 | } | |
131 | |||
132 | 6705 | void end_change(void) | |
133 | { | ||
134 | 6705 | prev_change_merge = change_merge; | |
135 | 6705 | } | |
136 | |||
137 | 34 | void begin_change_chain(void) | |
138 | { | ||
139 | 34 | BUG_ON(change_barrier); | |
140 | |||
141 | // Allocate change chain barrier but add it to the change tree only if | ||
142 | // there will be any real changes | ||
143 | 34 | change_barrier = alloc_change(); | |
144 | 34 | change_merge = CHANGE_MERGE_NONE; | |
145 | 34 | } | |
146 | |||
147 | 34 | void end_change_chain(View *view) | |
148 | { | ||
149 |
2/2✓ Branch 0 (2→3) taken 3 times.
✓ Branch 1 (2→4) taken 31 times.
|
34 | if (change_barrier) { |
150 | // There were no changes in this change chain | ||
151 | 3 | free(change_barrier); | |
152 | 3 | change_barrier = NULL; | |
153 | } else { | ||
154 | // There were some changes; add end of chain marker | ||
155 | 31 | add_change(view->buffer, alloc_change()); | |
156 | } | ||
157 | 34 | } | |
158 | |||
159 | 26 | static void fix_cursors(const View *view, size_t offset, size_t del, size_t ins) | |
160 | { | ||
161 | 26 | const Buffer *buffer = view->buffer; | |
162 |
2/2✓ Branch 0 (9→3) taken 52 times.
✓ Branch 1 (9→10) taken 26 times.
|
78 | for (size_t i = 0, n = buffer->views.count; i < n; i++) { |
163 | 52 | View *v = buffer->views.ptrs[i]; | |
164 |
4/4✓ Branch 0 (3→4) taken 26 times.
✓ Branch 1 (3→8) taken 26 times.
✓ Branch 2 (4→5) taken 7 times.
✓ Branch 3 (4→8) taken 19 times.
|
52 | if (v != view && offset < v->saved_cursor_offset) { |
165 |
1/2✓ Branch 0 (5→6) taken 7 times.
✗ Branch 1 (5→7) not taken.
|
7 | if (offset + del <= v->saved_cursor_offset) { |
166 | 7 | v->saved_cursor_offset -= del; | |
167 | 7 | v->saved_cursor_offset += ins; | |
168 | } else { | ||
169 | ✗ | v->saved_cursor_offset = offset; | |
170 | } | ||
171 | } | ||
172 | } | ||
173 | 26 | } | |
174 | |||
175 | 50 | static void reverse_change(View *view, Change *change) | |
176 | { | ||
177 | 50 | const size_t ins_count = change->ins_count; | |
178 | 50 | const size_t del_count = change->del_count; | |
179 | 50 | BUG_ON(!del_count && !ins_count); | |
180 | |||
181 |
2/2✓ Branch 0 (4→5) taken 26 times.
✓ Branch 1 (4→6) taken 24 times.
|
50 | if (view->buffer->views.count > 1) { |
182 | // NOLINTNEXTLINE(readability-suspicious-call-argument) | ||
183 | 26 | fix_cursors(view, change->offset, ins_count, del_count); | |
184 | } | ||
185 | |||
186 | 50 | block_iter_goto_offset(&view->cursor, change->offset); | |
187 | |||
188 |
2/2✓ Branch 0 (7→8) taken 15 times.
✓ Branch 1 (7→12) taken 35 times.
|
50 | if (ins_count == 0) { |
189 | // Convert delete to insert | ||
190 | 15 | do_insert(view, change->buf, del_count); | |
191 |
2/2✓ Branch 0 (9→10) taken 4 times.
✓ Branch 1 (9→11) taken 11 times.
|
15 | if (change->move_after) { |
192 | 4 | block_iter_skip_bytes(&view->cursor, del_count); | |
193 | } | ||
194 | 15 | change->ins_count = del_count; | |
195 | 15 | change->del_count = 0; | |
196 | 15 | free(change->buf); | |
197 | 15 | change->buf = NULL; | |
198 | 15 | return; | |
199 | } | ||
200 | |||
201 |
2/2✓ Branch 0 (12→13) taken 31 times.
✓ Branch 1 (12→15) taken 4 times.
|
35 | if (del_count == 0) { |
202 | // Convert insert to delete | ||
203 | 31 | change->buf = do_delete(view, ins_count, true); | |
204 | 31 | change->del_count = ins_count; | |
205 | 31 | change->ins_count = 0; | |
206 | 31 | return; | |
207 | } | ||
208 | |||
209 | // Reverse replace | ||
210 | // NOLINTNEXTLINE(readability-suspicious-call-argument) | ||
211 | 4 | char *buf = do_replace(view, ins_count, change->buf, del_count); | |
212 | 4 | free(change->buf); | |
213 | 4 | change->buf = buf; | |
214 | 4 | change->ins_count = del_count; | |
215 | 4 | change->del_count = ins_count; | |
216 | } | ||
217 | |||
218 | 1024 | bool undo(View *view) | |
219 | { | ||
220 | 1024 | Change *change = view->buffer->cur_change; | |
221 | 1024 | view_reset_preferred_x(view); | |
222 |
2/2✓ Branch 0 (3→4) taken 47 times.
✓ Branch 1 (3→12) taken 977 times.
|
1024 | if (!change->next) { |
223 | return false; | ||
224 | } | ||
225 | |||
226 |
2/2✓ Branch 0 (4→5) taken 3 times.
✓ Branch 1 (4→10) taken 44 times.
|
47 | if (is_change_chain_barrier(change)) { |
227 | unsigned long count = 0; | ||
228 | 9 | while (1) { | |
229 | 6 | change = change->next; | |
230 |
2/2✓ Branch 0 (5→6) taken 3 times.
✓ Branch 1 (5→8) taken 3 times.
|
6 | if (is_change_chain_barrier(change)) { |
231 | break; | ||
232 | } | ||
233 | 3 | reverse_change(view, change); | |
234 | 3 | count++; | |
235 | } | ||
236 |
1/2✗ Branch 0 (8→9) not taken.
✓ Branch 1 (8→11) taken 3 times.
|
3 | if (count > 1) { |
237 | ✗ | ErrorBuffer *ebuf = &view->window->editor->err; | |
238 | ✗ | info_msg(ebuf, "Undid %lu changes", count); | |
239 | } | ||
240 | } else { | ||
241 | 44 | reverse_change(view, change); | |
242 | } | ||
243 | |||
244 | 47 | view->buffer->cur_change = change->next; | |
245 | 47 | return true; | |
246 | } | ||
247 | |||
248 | 6 | bool redo(View *view, unsigned long change_id) | |
249 | { | ||
250 | 6 | ErrorBuffer *ebuf = &view->window->editor->err; | |
251 | 6 | Change *change = view->buffer->cur_change; | |
252 | 6 | view_reset_preferred_x(view); | |
253 |
2/2✓ Branch 0 (3→4) taken 1 times.
✓ Branch 1 (3→7) taken 5 times.
|
6 | if (!change->prev) { |
254 | // Don't complain if change_id is 0 | ||
255 |
1/2✓ Branch 0 (4→5) taken 1 times.
✗ Branch 1 (4→6) not taken.
|
1 | if (change_id) { |
256 | 1 | error_msg(ebuf, "Nothing to redo"); | |
257 | } | ||
258 | 1 | return false; | |
259 | } | ||
260 | |||
261 | 5 | const unsigned long nr_prev = change->nr_prev; | |
262 | 5 | BUG_ON(nr_prev == 0); | |
263 |
2/2✓ Branch 0 (9→10) taken 1 times.
✓ Branch 1 (9→12) taken 4 times.
|
5 | if (change_id == 0) { |
264 | // Default to newest change | ||
265 | 1 | change_id = nr_prev - 1; | |
266 |
1/2✗ Branch 0 (10→11) not taken.
✓ Branch 1 (10→16) taken 1 times.
|
1 | if (nr_prev > 1) { |
267 | ✗ | unsigned long i = change_id + 1; | |
268 | ✗ | info_msg(ebuf, "Redoing newest (%lu) of %lu possible changes", i, nr_prev); | |
269 | } | ||
270 | } else { | ||
271 |
2/2✓ Branch 0 (12→13) taken 2 times.
✓ Branch 1 (12→16) taken 2 times.
|
4 | if (--change_id >= nr_prev) { |
272 |
2/2✓ Branch 0 (13→14) taken 1 times.
✓ Branch 1 (13→15) taken 1 times.
|
2 | if (nr_prev == 1) { |
273 | 1 | return error_msg(ebuf, "There is only 1 possible change to redo"); | |
274 | } | ||
275 | 1 | return error_msg(ebuf, "There are only %lu possible changes to redo", nr_prev); | |
276 | } | ||
277 | } | ||
278 | |||
279 | 3 | change = change->prev[change_id]; | |
280 |
1/2✗ Branch 0 (16→17) not taken.
✓ Branch 1 (16→22) taken 3 times.
|
3 | if (is_change_chain_barrier(change)) { |
281 | unsigned long count = 0; | ||
282 | ✗ | while (1) { | |
283 | ✗ | change = change->prev[change->nr_prev - 1]; | |
284 | ✗ | if (is_change_chain_barrier(change)) { | |
285 | break; | ||
286 | } | ||
287 | ✗ | reverse_change(view, change); | |
288 | ✗ | count++; | |
289 | } | ||
290 | ✗ | if (count > 1) { | |
291 | ✗ | info_msg(ebuf, "Redid %lu changes", count); | |
292 | } | ||
293 | } else { | ||
294 | 3 | reverse_change(view, change); | |
295 | } | ||
296 | |||
297 | 3 | view->buffer->cur_change = change; | |
298 | 3 | return true; | |
299 | } | ||
300 | |||
301 | 81 | void free_changes(Change *c) | |
302 | { | ||
303 | 94 | top: | |
304 |
2/2✓ Branch 0 (5→4) taken 332 times.
✓ Branch 1 (5→6) taken 94 times.
|
426 | while (c->nr_prev) { |
305 | 332 | c = c->prev[c->nr_prev - 1]; | |
306 | } | ||
307 | |||
308 | // c is leaf now | ||
309 |
2/2✓ Branch 0 (10→7) taken 332 times.
✓ Branch 1 (10→11) taken 81 times.
|
413 | while (c->next) { |
310 | 332 | Change *next = c->next; | |
311 | 332 | free(c->buf); | |
312 | 332 | free(c); | |
313 | |||
314 | 332 | c = next; | |
315 |
2/2✓ Branch 0 (7→8) taken 13 times.
✓ Branch 1 (7→9) taken 319 times.
|
332 | if (--c->nr_prev) { |
316 | 13 | goto top; | |
317 | } | ||
318 | |||
319 | // We have become leaf | ||
320 | 319 | free(c->prev); | |
321 | } | ||
322 | 81 | } | |
323 | |||
324 | 264 | void buffer_insert_bytes(View *view, const char *buf, const size_t len) | |
325 | { | ||
326 | 264 | view_reset_preferred_x(view); | |
327 |
1/2✓ Branch 0 (3→4) taken 264 times.
✗ Branch 1 (3→13) not taken.
|
264 | if (len == 0) { |
328 | return; | ||
329 | } | ||
330 | |||
331 | 264 | size_t rec_len = len; | |
332 |
4/4✓ Branch 0 (4→5) taken 220 times.
✓ Branch 1 (4→8) taken 44 times.
✓ Branch 2 (5→6) taken 41 times.
✓ Branch 3 (5→8) taken 179 times.
|
264 | if (buf[len - 1] != '\n' && block_iter_is_eof(&view->cursor)) { |
333 | // Force newline at EOF | ||
334 | 41 | do_insert(view, "\n", 1); | |
335 | 41 | rec_len++; | |
336 | } | ||
337 | |||
338 | 264 | do_insert(view, buf, len); | |
339 | 264 | record_insert(view, rec_len); | |
340 | |||
341 |
1/2✗ Branch 0 (10→11) not taken.
✓ Branch 1 (10→13) taken 264 times.
|
264 | if (view->buffer->views.count > 1) { |
342 | ✗ | fix_cursors(view, block_iter_get_offset(&view->cursor), len, 0); | |
343 | } | ||
344 | } | ||
345 | |||
346 | 98 | static bool would_delete_last_bytes(const View *view, size_t count) | |
347 | { | ||
348 | 98 | const Block *blk = view->cursor.blk; | |
349 | 98 | size_t offset = view->cursor.offset; | |
350 | 98 | while (1) { | |
351 | 98 | size_t avail = blk->size - offset; | |
352 |
2/2✓ Branch 0 (3→4) taken 7 times.
✓ Branch 1 (3→6) taken 91 times.
|
98 | if (avail > count) { |
353 | return false; | ||
354 | } | ||
355 | |||
356 |
1/2✗ Branch 0 (4→5) not taken.
✓ Branch 1 (4→6) taken 7 times.
|
7 | if (blk->node.next == view->cursor.head) { |
357 | return true; | ||
358 | } | ||
359 | |||
360 | ✗ | count -= avail; | |
361 | ✗ | blk = BLOCK(blk->node.next); | |
362 | ✗ | offset = 0; | |
363 | } | ||
364 | } | ||
365 | |||
366 | 57 | static void buffer_delete_bytes_internal(View *view, size_t len, bool move_after) | |
367 | { | ||
368 | 57 | view_reset_preferred_x(view); | |
369 |
2/2✓ Branch 0 (3→4) taken 56 times.
✓ Branch 1 (3→18) taken 1 times.
|
57 | if (len == 0) { |
370 | return; | ||
371 | } | ||
372 | |||
373 | // Check if all newlines from EOF would be deleted | ||
374 |
2/2✓ Branch 0 (5→6) taken 3 times.
✓ Branch 1 (5→13) taken 53 times.
|
56 | if (would_delete_last_bytes(view, len)) { |
375 | 3 | BlockIter bi = view->cursor; | |
376 | 3 | CodePoint u; | |
377 |
3/4✓ Branch 0 (7→8) taken 3 times.
✗ Branch 1 (7→12) not taken.
✓ Branch 2 (8→9) taken 2 times.
✓ Branch 3 (8→12) taken 1 times.
|
3 | if (block_iter_prev_char(&bi, &u) && u != '\n') { |
378 | // No newline before cursor | ||
379 |
1/2✓ Branch 0 (9→10) taken 2 times.
✗ Branch 1 (9→12) not taken.
|
2 | if (--len == 0) { |
380 | 2 | begin_change(CHANGE_MERGE_NONE); | |
381 | 2 | return; | |
382 | } | ||
383 | } | ||
384 | } | ||
385 | |||
386 | 54 | record_delete(view, do_delete(view, len, true), len, move_after); | |
387 | |||
388 |
1/2✗ Branch 0 (15→16) not taken.
✓ Branch 1 (15→18) taken 54 times.
|
54 | if (view->buffer->views.count > 1) { |
389 | ✗ | fix_cursors(view, block_iter_get_offset(&view->cursor), len, 0); | |
390 | } | ||
391 | } | ||
392 | |||
393 | 48 | void buffer_delete_bytes(View *view, size_t len) | |
394 | { | ||
395 | 48 | buffer_delete_bytes_internal(view, len, false); | |
396 | 48 | } | |
397 | |||
398 | 9 | void buffer_erase_bytes(View *view, size_t len) | |
399 | { | ||
400 | 9 | buffer_delete_bytes_internal(view, len, true); | |
401 | 9 | } | |
402 | |||
403 | 273 | void buffer_replace_bytes(View *view, size_t del_count, const char *ins, size_t ins_count) | |
404 | { | ||
405 |
2/2✓ Branch 0 (2→3) taken 223 times.
✓ Branch 1 (2→5) taken 50 times.
|
273 | if (del_count == 0) { |
406 | 223 | buffer_insert_bytes(view, ins, ins_count); | |
407 | 223 | return; | |
408 | } | ||
409 |
2/2✓ Branch 0 (5→6) taken 8 times.
✓ Branch 1 (5→8) taken 42 times.
|
50 | if (ins_count == 0) { |
410 | 8 | buffer_delete_bytes(view, del_count); | |
411 | 8 | return; | |
412 | } | ||
413 | |||
414 | 42 | view_reset_preferred_x(view); | |
415 | |||
416 | // Check if all newlines from EOF would be deleted | ||
417 |
2/2✓ Branch 0 (10→11) taken 4 times.
✓ Branch 1 (10→15) taken 38 times.
|
42 | if (would_delete_last_bytes(view, del_count)) { |
418 |
1/2✗ Branch 0 (11→12) not taken.
✓ Branch 1 (11→15) taken 4 times.
|
4 | if (ins[ins_count - 1] != '\n') { |
419 | // Don't replace last newline | ||
420 | ✗ | if (--del_count == 0) { | |
421 | ✗ | buffer_insert_bytes(view, ins, ins_count); | |
422 | ✗ | return; | |
423 | } | ||
424 | } | ||
425 | } | ||
426 | |||
427 | 42 | char *deleted = do_replace(view, del_count, ins, ins_count); | |
428 | 42 | record_replace(view, deleted, del_count, ins_count); | |
429 | |||
430 |
1/2✗ Branch 0 (17→18) not taken.
✓ Branch 1 (17→20) taken 42 times.
|
42 | if (view->buffer->views.count > 1) { |
431 | ✗ | fix_cursors(view, block_iter_get_offset(&view->cursor), del_count, ins_count); | |
432 | } | ||
433 | } | ||
434 |