Introduction
Introduction Statistics Contact Development Disclaimer Help
undo.c - ledit - Text editor (WIP)
git clone git://lumidify.org/ledit.git (fast, but not encrypted)
git clone https://lumidify.org/ledit.git (encrypted, but very slow)
git clone git://4kcetb7mo7hj6grozzybxtotsub5bempzo4lirzc3437amof2c2impyd.onion/…
Log
Files
Refs
README
LICENSE
---
undo.c (9769B)
---
1 #include <string.h>
2 #include <stdlib.h>
3 #include <stdint.h>
4
5 #include <X11/Xlib.h>
6 #include <X11/Xutil.h>
7 #include <pango/pangoxft.h>
8
9 #include "util.h"
10 #include "memory.h"
11 #include "common.h"
12 #include "txtbuf.h"
13 #include "cache.h"
14 #include "undo.h"
15 #include "assert.h"
16
17 /* FIXME: more sanity checks in case text is
18 inserted/deleted without adding to undo stack */
19 /* FIXME: cursor positions can be a bit weird when
20 undo is used across different insert sessions in
21 insert mode - e.g. if some text is inserted, then
22 'o' is used in normal mode to append a new line and
23 type some text, the cursor position at a certain
24 undo position will differ in insert mode depending
25 on whether it was reached with undo or redo since
26 'o' saves the position at which it was pressed,
27 not the position of the last insert */
28
29 enum operation {
30 UNDO_INSERT,
31 UNDO_DELETE
32 };
33
34 typedef struct {
35 txtbuf *text;
36 enum operation type;
37 ledit_mode mode;
38 ledit_range op_range;
39 ledit_range cursor_range;
40 int group;
41 int mode_group;
42 } undo_elem;
43
44 struct undo_stack {
45 size_t len, cur, cap;
46 undo_elem *stack;
47 int cur_valid;
48 int change_mode_group;
49 };
50
51 undo_stack *
52 undo_stack_create(void) {
53 undo_stack *undo = ledit_malloc(sizeof(undo_stack));
54 undo->len = undo->cap = 0;
55 undo->cur = 0;
56 undo->cur_valid = 0;
57 undo->stack = NULL;
58 undo->change_mode_group = 0;
59 return undo;
60 }
61
62 void
63 undo_stack_destroy(undo_stack *undo) {
64 for (size_t i = 0; i < undo->cap; i++) {
65 if (undo->stack[i].text != NULL)
66 txtbuf_destroy(undo->stack[i].text);
67 }
68 free(undo->stack);
69 free(undo);
70 }
71
72 /* FIXME: resize text buffers when they aren't needed anymore */
73 static undo_elem *
74 push_undo_elem(undo_stack *undo) {
75 if (undo->cur_valid)
76 undo->cur++;
77 else
78 undo->cur = 0;
79 undo->cur_valid = 1;
80 undo->len = add_sz(undo->cur, 1);
81 if (undo->len > undo->cap) {
82 size_t cap = ideal_array_size(undo->cap, undo->len);
83 undo->stack = ledit_reallocarray(undo->stack, cap, sizeo…
84 for (size_t i = undo->cap; i < cap; i++) {
85 undo->stack[i].text = NULL;
86 }
87 undo->cap = cap;
88 }
89 return &undo->stack[undo->cur];
90 }
91
92 static undo_elem *
93 peek_undo_elem(undo_stack *undo) {
94 if (!undo->cur_valid)
95 return NULL;
96 return &undo->stack[undo->cur];
97 }
98
99 void
100 undo_change_mode_group(undo_stack *undo) {
101 undo->change_mode_group = 1;
102 }
103
104 #if TEST
105 void
106 dump_undo_stack(FILE *file, undo_stack *undo) {
107 fprintf(
108 file,
109 "cur: %zu, cur_valid: %d, change_mode_group: %d, len: %zu, c…
110 undo->cur, undo->cur_valid, undo->change_mode_group, undo->l…
111 );
112 for (size_t i = 0; i < undo->len; i++) {
113 undo_elem *e = &undo->stack[i];
114 fprintf(
115 file,
116 "type %d, mode %d, group %d, mode_group %d, text '%.…
117 "op_range (%zu,%zu;%zu,%zu), cursor_range (%zu,%zu;%…
118 e->type, e->mode, e->group, e->mode_group, (int)e->t…
119 e->op_range.line1, e->op_range.byte1, e->op_range.li…
120 e->cursor_range.line1, e->cursor_range.byte1, e->cur…
121 );
122 }
123 }
124 #endif
125
126 static void
127 push_undo(
128 undo_stack *undo, txtbuf *text,
129 ledit_range insert_range, /* maybe not the best name */
130 ledit_range cursor_range,
131 int start_group,
132 enum operation type, ledit_mode mode) {
133 undo_elem *old = peek_undo_elem(undo);
134 int last_group = old == NULL ? 0 : old->group;
135 int last_mode_group = old == NULL ? 0 : old->mode_group;
136 undo_elem *e = push_undo_elem(undo);
137 e->group = start_group ? !last_group : last_group;
138 e->mode_group = undo->change_mode_group ? !last_mode_group : las…
139 undo->change_mode_group = 0;
140 e->op_range = insert_range;
141 e->cursor_range = cursor_range;
142 e->mode = mode;
143 e->type = type;
144 if (e->text != NULL)
145 txtbuf_copy(e->text, text);
146 else
147 e->text = txtbuf_dup(text);
148 }
149
150 void
151 undo_push_insert(
152 undo_stack *undo, txtbuf *text,
153 ledit_range insert_range, ledit_range cursor_range,
154 int start_group, ledit_mode mode) {
155 push_undo(
156 undo, text, insert_range, cursor_range,
157 start_group, UNDO_INSERT, mode
158 );
159 }
160
161 void
162 undo_push_delete(
163 undo_stack *undo, txtbuf *text,
164 ledit_range delete_range, ledit_range cursor_range,
165 int start_group, ledit_mode mode) {
166 push_undo(
167 undo, text, delete_range, cursor_range,
168 start_group, UNDO_DELETE, mode
169 );
170 }
171
172 undo_status
173 ledit_undo(undo_stack *undo, ledit_mode mode, void *callback_data,
174 undo_insert_callback insert_cb, undo_delete_callback delete_cb,
175 size_t *cur_line_ret, size_t *cur_index_ret, size_t *min_line_ret) {
176 undo_elem *e;
177 /* skip empty elements */
178 while (undo->cur_valid && undo->stack[undo->cur].text->len == 0)…
179 if (undo->cur == 0)
180 undo->cur_valid = 0;
181 else
182 undo->cur--;
183 }
184 if (!undo->cur_valid)
185 return UNDO_OLDEST_CHANGE;
186 int group = undo->stack[undo->cur].group;
187 int mode_group = undo->stack[undo->cur].mode_group;
188 size_t min_line = SIZE_MAX;
189 int mode_group_same = 0;
190 size_t cur_line = 0;
191 size_t cur_index = 0;
192 while (undo->cur_valid &&
193 (undo->stack[undo->cur].group == group || (mode_group_sam…
194 ((mode == NORMAL ||
195 mode == VISUAL) &&
196 undo->stack[undo->cur].mode == INSERT &&
197 undo->stack[undo->cur].mode_group == mode_group)))) {
198 e = &undo->stack[undo->cur];
199 /* if the mode group is the same, we need to update the …
200 otherwise it can happen that some iterations are perf…
201 because the mode group (but not the normal group) is …
202 same, and then the next normal group is also undone b…
203 it has the same group id as the original group here */
204 if (mode_group_same)
205 group = e->group;
206 switch (e->type) {
207 case UNDO_INSERT:
208 /* FIXME: should the paste buffer also be modifi…
209 /* printf("delete %zu,%zu; %zu,%zu\n", e->op_ran…
210 delete_cb(
211 callback_data,
212 e->op_range.line1, e->op_range.byte1,
213 e->op_range.line2, e->op_range.byte2
214 );
215 break;
216 case UNDO_DELETE:
217 /* printf("delete %zu,%zu; %zu,%zu\n", e->op_ran…
218 insert_cb(
219 callback_data,
220 e->op_range.line1, e->op_range.byte1,
221 e->text->text, e->text->len
222 );
223 break;
224 default:
225 /* FIXME: show this as a message */
226 fprintf(stderr, "Error with undo. This should no…
227 break;
228 }
229 /* FIXME: make sure this is always sorted already */
230 if (e->op_range.line1 < min_line)
231 min_line = e->op_range.line1;
232 if (undo->cur == 0)
233 undo->cur_valid = 0;
234 else
235 undo->cur--;
236 cur_line = e->cursor_range.line1;
237 cur_index = e->cursor_range.byte1;
238 }
239 /* these can't be written directly in the while loop because tha…
240 conflicts - when text is inserted/deleted on the current line…
241 view, the cursor is updated, but if the current line was alre…
242 here before the end of the undo run, it's possible for multip…
243 to have a cursor highlight set in the end (at least when work…
244 multiple views) */
245 *cur_line_ret = cur_line;
246 *cur_index_ret = cur_index;
247 *min_line_ret = min_line;
248 if (mode == NORMAL || mode == VISUAL)
249 undo_change_mode_group(undo);
250 return UNDO_NORMAL;
251 }
252
253 undo_status
254 ledit_redo(undo_stack *undo, ledit_mode mode, void *callback_data,
255 undo_insert_callback insert_cb, undo_delete_callback delete_cb,
256 size_t *cur_line_ret, size_t *cur_index_ret, size_t *min_line_ret) {
257 undo_elem *e;
258 if (undo->len == 0)
259 return UNDO_NEWEST_CHANGE;
260 /* skip elements where no text is changed */
261 while (undo->cur < undo->len - 1 && undo->stack[undo->cur + 1].t…
262 undo->cur++;
263 }
264 if (undo->cur_valid && undo->cur >= undo->len - 1)
265 return UNDO_NEWEST_CHANGE;
266 if (!undo->cur_valid) {
267 undo->cur_valid = 1;
268 undo->cur = 0;
269 } else {
270 undo->cur++;
271 }
272 int group = undo->stack[undo->cur].group;
273 int mode_group = undo->stack[undo->cur].mode_group;
274 size_t min_line = SIZE_MAX;
275 int mode_group_same = 0;
276 size_t cur_line = 0;
277 size_t cur_index = 0;
278 while (undo->cur < undo->len &&
279 (undo->stack[undo->cur].group == group || (mode_group_sam…
280 ((mode == NORMAL ||
281 mode == VISUAL) &&
282 undo->stack[undo->cur].mode == INSERT &&
283 undo->stack[undo->cur].mode_group == mode_group)))) {
284 e = &undo->stack[undo->cur];
285 if (mode_group_same)
286 group = e->group;
287 switch (e->type) {
288 case UNDO_INSERT:
289 insert_cb(
290 callback_data,
291 e->op_range.line1, e->op_range.byte1,
292 e->text->text, e->text->len
293 );
294 break;
295 case UNDO_DELETE:
296 delete_cb(
297 callback_data,
298 e->op_range.line1, e->op_range.byte1,
299 e->op_range.line2, e->op_range.byte2
300 );
301 break;
302 default:
303 fprintf(stderr, "Error with redo. This should no…
304 break;
305 }
306 if (e->op_range.line1 < min_line)
307 min_line = e->op_range.line1;
308 undo->cur++;
309 cur_line = e->cursor_range.line2;
310 cur_index = e->cursor_range.byte2;
311 }
312 *cur_line_ret = cur_line;
313 *cur_index_ret = cur_index;
314 /* it should theoretically never be 0 anyways, but whatever */
315 if (undo->cur > 0)
316 undo->cur--;
317 *min_line_ret = min_line;
318 if (mode == NORMAL || mode == VISUAL)
319 undo_change_mode_group(undo);
320 return UNDO_NORMAL;
321 }
322
323 void
324 undo_change_last_cur_range(undo_stack *undo, ledit_range cur_range) {
325 undo_elem *e = peek_undo_elem(undo);
326 if (e != NULL)
327 e->cursor_range = cur_range;
328 }
329
330 char *
331 undo_state_to_str(undo_status s) {
332 switch (s) {
333 case UNDO_NORMAL:
334 return "Performed undo/redo";
335 case UNDO_OLDEST_CHANGE:
336 return "Already at oldest change";
337 case UNDO_NEWEST_CHANGE:
338 return "Already at newest change";
339 default:
340 return "This is a bug. Tell lumidify about it.";
341 }
342 }
You are viewing proxied material from lumidify.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.