vis: improve <C-n> in visual mode
[vis.git] / vis-marks.c
blobe616f1dc717493e0bc3f326556b1de09f3069006
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 (!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;
61 enum VisMark vis_mark_used(Vis *vis) {
62 return vis->action.mark;
65 void vis_mark(Vis *vis, enum VisMark mark) {
66 if (mark < LENGTH(vis->win->file->marks))
67 vis->action.mark = mark;
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 View *view = win->view;
76 size_t len = array_length(mark);
77 array_reserve(&sel, len);
78 for (size_t i = 0; i < len; i++) {
79 SelectionRegion *sr = array_get(mark, i);
80 Filerange r = view_regions_restore(view, sr);
81 if (text_range_valid(&r))
82 array_add(&sel, &r);
84 vis_mark_normalize(&sel);
85 return sel;
88 Array vis_mark_get(Win *win, enum VisMark id) {
89 return mark_get(win, mark_from(win->vis, id));
92 static void mark_set(Win *win, Array *mark, Array *sel) {
93 if (!mark)
94 return;
95 array_clear(mark);
96 View *view = win->view;
97 for (size_t i = 0, len = array_length(sel); i < len; i++) {
98 SelectionRegion ss;
99 Filerange *r = array_get(sel, i);
100 if (view_regions_save(view, r, &ss))
101 array_add(mark, &ss);
105 void vis_mark_set(Win *win, enum VisMark id, Array *sel) {
106 mark_set(win, mark_from(win->vis, id), sel);
109 void marklist_init(MarkList *list, size_t max) {
110 Array mark;
111 mark_init(&mark);
112 array_init_sized(&list->prev, sizeof(Array));
113 array_reserve(&list->prev, max);
114 array_add(&list->prev, &mark);
115 array_init_sized(&list->next, sizeof(Array));
116 array_reserve(&list->next, max);
119 void marklist_release(MarkList *list) {
120 for (size_t i = 0, len = array_length(&list->prev); i < len; i++)
121 array_release(array_get(&list->prev, i));
122 array_release(&list->prev);
123 for (size_t i = 0, len = array_length(&list->next); i < len; i++)
124 array_release(array_get(&list->next, i));
125 array_release(&list->next);
128 static bool marklist_push(Win *win, MarkList *list, Array *sel) {
129 Array *top = array_peek(&list->prev);
130 if (top) {
131 Array top_sel = mark_get(win, top);
132 bool eq = vis_mark_equal(&top_sel, sel);
133 array_release(&top_sel);
134 if (eq)
135 return true;
138 for (size_t i = 0, len = array_length(&list->next); i < len; i++)
139 array_release(array_get(&list->next, i));
140 array_clear(&list->next);
141 Array arr;
142 mark_init(&arr);
143 if (array_length(&list->prev) >= array_capacity(&list->prev)) {
144 Array *tmp = array_get(&list->prev, 0);
145 arr = *tmp;
146 array_remove(&list->prev, 0);
148 mark_set(win, &arr, sel);
149 return array_push(&list->prev, &arr);
152 bool vis_jumplist_save(Vis *vis) {
153 View *view = vis->win->view;
154 Array sel = view_selections_get_all(view);
155 bool ret = marklist_push(vis->win, &vis->win->jumplist, &sel);
156 array_release(&sel);
157 return ret;
160 static bool marklist_prev(Win *win, MarkList *list) {
161 View *view = win->view;
162 bool restore = false;
163 Array cur = view_selections_get_all(view);
164 bool anchored = view_selections_anchored(view_selections_primary_get(view));
165 Array *top = array_peek(&list->prev);
166 if (!top)
167 goto out;
168 Array top_sel = mark_get(win, top);
169 restore = !vis_mark_equal(&top_sel, &cur);
170 if (restore)
171 view_selections_set_all(view, &top_sel, anchored);
172 array_release(&top_sel);
173 if (restore)
174 goto out;
176 while (array_length(&list->prev) > 1) {
177 Array *prev = array_pop(&list->prev);
178 array_push(&list->next, prev);
179 prev = array_peek(&list->prev);
180 Array sel = mark_get(win, prev);
181 restore = array_length(&sel) > 0;
182 if (restore)
183 view_selections_set_all(view, &sel, anchored);
184 array_release(&sel);
185 if (restore)
186 goto out;
188 out:
189 array_release(&cur);
190 return restore;
193 static bool marklist_next(Win *win, MarkList *list) {
194 View *view = win->view;
195 bool anchored = view_selections_anchored(view_selections_primary_get(view));
196 for (;;) {
197 Array *next = array_pop(&list->next);
198 if (!next)
199 return false;
200 Array sel = mark_get(win, next);
201 if (array_length(&sel) > 0) {
202 view_selections_set_all(view, &sel, anchored);
203 array_release(&sel);
204 array_push(&list->prev, next);
205 return true;
207 array_release(next);
211 bool vis_jumplist_prev(Vis *vis) {
212 return marklist_prev(vis->win, &vis->win->jumplist);
215 bool vis_jumplist_next(Vis *vis) {
216 return marklist_next(vis->win, &vis->win->jumplist);
219 enum VisMark vis_mark_from(Vis *vis, char mark) {
220 if (mark >= 'a' && mark <= 'z')
221 return VIS_MARK_a + mark - 'a';
222 for (size_t i = 0; i < LENGTH(vis_marks); i++) {
223 if (vis_marks[i].name == mark)
224 return i;
226 return VIS_MARK_INVALID;
229 const MarkDef vis_marks[] = {
230 [VIS_MARK_DEFAULT] = { '\'', VIS_HELP("Default mark") },
231 [VIS_MARK_SELECTION] = { '^', VIS_HELP("Last selections") },