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