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 };