vis
a vi-like editor based on Plan 9's structural regular expressions
git clone https://9o.is/git/vis.git
vis-marks.c
(5906B)
1 #include "vis-core.h"
2
3 static int ranges_comparator(const void *a, const void *b) {
4 const Filerange *r1 = a, *r2 = b;
5 if (!text_range_valid(r1))
6 return text_range_valid(r2) ? 1 : 0;
7 if (!text_range_valid(r2))
8 return -1;
9 return (r1->start < r2->start || (r1->start == r2->start && r1->end < r2->end)) ? -1 : 1;
10 }
11
12 void vis_mark_normalize(Array *a) {
13 array_sort(a, ranges_comparator);
14 Filerange *prev = NULL, *r = array_get(a, 0);
15 for (size_t i = 0; r; r = array_get(a, i)) {
16 if (text_range_size(r) == 0) {
17 array_remove(a, i);
18 } else if (prev && text_range_overlap(prev, r)) {
19 *prev = text_range_union(prev, r);
20 array_remove(a, i);
21 } else {
22 prev = r;
23 i++;
24 }
25 }
26 }
27
28 bool vis_mark_equal(Array *a, Array *b) {
29 if (a->len != b->len)
30 return false;
31 size_t len = a->len;
32 for (size_t i = 0; i < len; i++) {
33 if (!text_range_equal(array_get(a, i), array_get(b, i)))
34 return false;
35 }
36
37 return true;
38 }
39
40 void mark_init(Array *arr) {
41 array_init_sized(arr, sizeof(SelectionRegion));
42 }
43
44 void mark_release(Array *arr) {
45 if (!arr)
46 return;
47 array_release(arr);
48 }
49
50 static Array *mark_from(Vis *vis, enum VisMark id) {
51 if (!vis->win)
52 return NULL;
53 if (id == VIS_MARK_SELECTION)
54 return &vis->win->saved_selections;
55 File *file = vis->win->file;
56 if (id < LENGTH(file->marks))
57 return &file->marks[id];
58 return NULL;
59 }
60
61 enum VisMark vis_mark_used(Vis *vis) {
62 return vis->action.mark;
63 }
64
65 void vis_mark(Vis *vis, enum VisMark mark) {
66 if (mark < LENGTH(vis->win->file->marks))
67 vis->action.mark = mark;
68 }
69
70 static Array mark_get(Win *win, Array *mark) {
71 Array sel;
72 array_init_sized(&sel, sizeof(Filerange));
73 if (!mark)
74 return sel;
75 size_t len = mark->len;
76 array_reserve(&sel, len);
77 for (size_t i = 0; i < len; i++) {
78 SelectionRegion *sr = array_get(mark, i);
79 Filerange r = view_regions_restore(&win->view, sr);
80 if (text_range_valid(&r))
81 array_add(&sel, &r);
82 }
83 vis_mark_normalize(&sel);
84 return sel;
85 }
86
87 Array vis_mark_get(Win *win, enum VisMark id) {
88 return mark_get(win, mark_from(win->vis, id));
89 }
90
91 static void mark_set(Win *win, Array *mark, Array *sel) {
92 if (!mark)
93 return;
94 array_clear(mark);
95 for (size_t i = 0, len = sel->len; i < len; i++) {
96 SelectionRegion ss;
97 Filerange *r = array_get(sel, i);
98 if (view_regions_save(&win->view, r, &ss))
99 array_add(mark, &ss);
100 }
101 }
102
103 void vis_mark_set(Win *win, enum VisMark id, Array *sel) {
104 mark_set(win, mark_from(win->vis, id), sel);
105 }
106
107 void marklist_init(MarkList *list, size_t max) {
108 Array mark;
109 mark_init(&mark);
110 array_init_sized(&list->prev, sizeof(Array));
111 array_reserve(&list->prev, max);
112 array_add(&list->prev, &mark);
113 array_init_sized(&list->next, sizeof(Array));
114 array_reserve(&list->next, max);
115 }
116
117 void marklist_release(MarkList *list) {
118 for (size_t i = 0, len = list->prev.len; i < len; i++)
119 array_release(array_get(&list->prev, i));
120 array_release(&list->prev);
121 for (size_t i = 0, len = list->next.len; i < len; i++)
122 array_release(array_get(&list->next, i));
123 array_release(&list->next);
124 }
125
126 static bool marklist_push(Win *win, MarkList *list, Array *sel) {
127 Array *top = array_peek(&list->prev);
128 if (top) {
129 Array top_sel = mark_get(win, top);
130 bool eq = vis_mark_equal(&top_sel, sel);
131 array_release(&top_sel);
132 if (eq)
133 return true;
134 }
135
136 for (size_t i = 0, len = list->next.len; i < len; i++)
137 array_release(array_get(&list->next, i));
138 array_clear(&list->next);
139 Array arr;
140 mark_init(&arr);
141 if (list->prev.len >= list->prev.count) {
142 Array *tmp = array_get(&list->prev, 0);
143 arr = *tmp;
144 array_remove(&list->prev, 0);
145 }
146 mark_set(win, &arr, sel);
147 return array_push(&list->prev, &arr);
148 }
149
150 bool vis_jumplist_save(Vis *vis) {
151 Array sel = view_selections_get_all(&vis->win->view);
152 bool ret = marklist_push(vis->win, &vis->win->jumplist, &sel);
153 array_release(&sel);
154 return ret;
155 }
156
157 static bool marklist_prev(Win *win, MarkList *list) {
158 View *view = &win->view;
159 bool restore = false;
160 Array cur = view_selections_get_all(view);
161 bool anchored = view_selections_primary_get(view)->anchored;
162 Array *top = array_peek(&list->prev);
163 if (!top)
164 goto out;
165 Array top_sel = mark_get(win, top);
166 restore = !vis_mark_equal(&top_sel, &cur);
167 if (restore)
168 view_selections_set_all(view, &top_sel, anchored);
169 array_release(&top_sel);
170 if (restore)
171 goto out;
172
173 while (list->prev.len > 1) {
174 Array *prev = array_pop(&list->prev);
175 array_push(&list->next, prev);
176 prev = array_peek(&list->prev);
177 Array sel = mark_get(win, prev);
178 restore = sel.len > 0;
179 if (restore)
180 view_selections_set_all(view, &sel, anchored);
181 array_release(&sel);
182 if (restore)
183 goto out;
184 }
185 out:
186 array_release(&cur);
187 return restore;
188 }
189
190 static bool marklist_next(Win *win, MarkList *list) {
191 View *view = &win->view;
192 bool anchored = view_selections_primary_get(view)->anchored;
193 for (;;) {
194 Array *next = array_pop(&list->next);
195 if (!next)
196 return false;
197 Array sel = mark_get(win, next);
198 if (sel.len > 0) {
199 view_selections_set_all(view, &sel, anchored);
200 array_release(&sel);
201 array_push(&list->prev, next);
202 return true;
203 }
204 array_release(next);
205 }
206 }
207
208 bool vis_jumplist_prev(Vis *vis) {
209 return marklist_prev(vis->win, &vis->win->jumplist);
210 }
211
212 bool vis_jumplist_next(Vis *vis) {
213 return marklist_next(vis->win, &vis->win->jumplist);
214 }
215
216 enum VisMark vis_mark_from(Vis *vis, char mark) {
217 if (mark >= 'a' && mark <= 'z')
218 return VIS_MARK_a + mark - 'a';
219 for (size_t i = 0; i < LENGTH(vis_marks); i++) {
220 if (vis_marks[i].name == mark)
221 return i;
222 }
223 return VIS_MARK_INVALID;
224 }
225
226 char vis_mark_to(Vis *vis, enum VisMark mark) {
227 if (VIS_MARK_a <= mark && mark <= VIS_MARK_z)
228 return 'a' + mark - VIS_MARK_a;
229
230 if (mark < LENGTH(vis_marks))
231 return vis_marks[mark].name;
232
233 return '\0';
234 }
235
236 const MarkDef vis_marks[] = {
237 [VIS_MARK_DEFAULT] = { '\'', VIS_HELP("Default mark") },
238 [VIS_MARK_SELECTION] = { '^', VIS_HELP("Last selections") },
239 };