test: update
[vis.git] / vis-marks.c
blob754379269149c00c79729ac20ad262941622bdc0
1 #include "vis-core.h"
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;
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++;
28 bool vis_mark_equal(Array *a, Array *b) {
29 size_t len = array_length(a);
30 if (len != array_length(b))
31 return false;
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;
37 return true;
40 void mark_init(Array *arr) {
41 array_init_sized(arr, sizeof(SelectionRegion));
44 void mark_release(Array *arr) {
45 if (!arr)
46 return;
47 array_release(arr);
50 static Array *mark_from(Vis *vis, enum VisMark id) {
51 if (id == VIS_MARK_SELECTION && vis->win)
52 return array_peek(&vis->win->jumplist.prev);
53 File *file = vis->win->file;
54 if (id < LENGTH(file->marks))
55 return &file->marks[id];
56 return NULL;
59 enum VisMark vis_mark_used(Vis *vis) {
60 return vis->action.mark;
63 void vis_mark(Vis *vis, enum VisMark mark) {
64 if (mark < LENGTH(vis->win->file->marks))
65 vis->action.mark = mark;
68 static Array mark_get(Win *win, Array *mark) {
69 Array sel;
70 array_init_sized(&sel, sizeof(Filerange));
71 if (!mark)
72 return sel;
73 View *view = win->view;
74 size_t len = array_length(mark);
75 array_reserve(&sel, len);
76 for (size_t i = 0; i < len; i++) {
77 SelectionRegion *sr = array_get(mark, i);
78 Filerange r = view_regions_restore(view, sr);
79 if (text_range_valid(&r))
80 array_add(&sel, &r);
82 vis_mark_normalize(&sel);
83 return sel;
86 Array vis_mark_get(Vis *vis, enum VisMark id) {
87 return mark_get(vis->win, mark_from(vis, id));
90 static void mark_set(Win *win, Array *mark, Array *sel) {
91 if (!mark)
92 return;
93 array_clear(mark);
94 View *view = win->view;
95 for (size_t i = 0, len = array_length(sel); i < len; i++) {
96 SelectionRegion ss;
97 Filerange *r = array_get(sel, i);
98 if (view_regions_save(view, r, &ss))
99 array_add(mark, &ss);
103 void vis_mark_set(Vis *vis, enum VisMark id, Array *sel) {
104 mark_set(vis->win, mark_from(vis, id), sel);
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);
117 void marklist_release(MarkList *list) {
118 for (size_t i = 0, len = array_length(&list->prev); i < len; i++)
119 array_release(array_get(&list->prev, i));
120 array_release(&list->prev);
121 for (size_t i = 0, len = array_length(&list->next); i < len; i++)
122 array_release(array_get(&list->next, i));
123 array_release(&list->next);
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;
136 for (size_t i = 0, len = array_length(&list->next); 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 (array_length(&list->prev) >= array_capacity(&list->prev)) {
142 Array *tmp = array_get(&list->prev, 0);
143 arr = *tmp;
144 array_remove(&list->prev, 0);
146 mark_set(win, &arr, sel);
147 return array_push(&list->prev, &arr);
150 bool vis_jumplist_save(Vis *vis) {
151 View *view = vis->win->view;
152 Array sel = view_selections_get_all(view);
153 bool ret = marklist_push(vis->win, &vis->win->jumplist, &sel);
154 array_release(&sel);
155 return ret;
158 static bool marklist_prev(Win *win, MarkList *list) {
159 View *view = win->view;
160 bool restore = false;
161 Array cur = view_selections_get_all(view);
162 bool anchored = view_selections_anchored(view_selections_primary_get(view));
163 Array *top = array_peek(&list->prev);
164 if (!top)
165 goto out;
166 Array top_sel = mark_get(win, top);
167 restore = !vis_mark_equal(&top_sel, &cur);
168 if (restore)
169 view_selections_set_all(view, &top_sel, anchored);
170 array_release(&top_sel);
171 if (restore)
172 goto out;
174 while (array_length(&list->prev) > 1) {
175 Array *prev = array_pop(&list->prev);
176 array_push(&list->next, prev);
177 prev = array_peek(&list->prev);
178 Array sel = mark_get(win, prev);
179 restore = array_length(&sel) > 0;
180 if (restore)
181 view_selections_set_all(view, &sel, anchored);
182 array_release(&sel);
183 if (restore)
184 goto out;
186 out:
187 array_release(&cur);
188 return restore;
191 static bool marklist_next(Win *win, MarkList *list) {
192 View *view = win->view;
193 bool anchored = view_selections_anchored(view_selections_primary_get(view));
194 for (;;) {
195 Array *next = array_pop(&list->next);
196 if (!next)
197 return false;
198 Array sel = mark_get(win, next);
199 if (array_length(&sel) > 0) {
200 view_selections_set_all(view, &sel, anchored);
201 array_release(&sel);
202 array_push(&list->prev, next);
203 return true;
205 array_release(next);
209 bool vis_jumplist_prev(Vis *vis) {
210 return marklist_prev(vis->win, &vis->win->jumplist);
213 bool vis_jumplist_next(Vis *vis) {
214 return marklist_next(vis->win, &vis->win->jumplist);
217 enum VisMark vis_mark_from(Vis *vis, char mark) {
218 if (mark >= 'a' && mark <= 'z')
219 return VIS_MARK_a + mark - 'a';
220 for (size_t i = 0; i < LENGTH(vis_marks); i++) {
221 if (vis_marks[i].name == mark)
222 return i;
224 return VIS_MARK_INVALID;
227 const MarkDef vis_marks[] = {
228 [VIS_MARK_DEFAULT] = { '\'', VIS_HELP("Default mark") },
229 [VIS_MARK_SELECTION] = { '^', VIS_HELP("Last selections") },