1 #include <string.h>
2 #include <ctype.h>
3 #include "mle.h"
4
5 // Clone cursor
cursor_clone(cursor_t * cursor,int use_srules,cursor_t ** ret_clone)6 int cursor_clone(cursor_t *cursor, int use_srules, cursor_t **ret_clone) {
7 cursor_t *clone;
8 bview_add_cursor(cursor->bview, cursor->mark->bline, cursor->mark->col, &clone);
9 if (cursor->is_anchored) {
10 cursor_toggle_anchor(clone, use_srules);
11 mark_join(clone->anchor, cursor->anchor);
12 }
13 *ret_clone = clone;
14 return MLE_OK;
15 }
16
17 // Remove cursor
cursor_destroy(cursor_t * cursor)18 int cursor_destroy(cursor_t *cursor) {
19 return bview_remove_cursor(cursor->bview, cursor);
20 }
21
22 // Select by mark
cursor_select_between(cursor_t * cursor,mark_t * a,mark_t * b,int use_srules)23 int cursor_select_between(cursor_t *cursor, mark_t *a, mark_t *b, int use_srules) {
24 cursor_drop_anchor(cursor, use_srules);
25 if (mark_is_lt(a, b)) {
26 mark_join(cursor->mark, a);
27 mark_join(cursor->anchor, b);
28 } else {
29 mark_join(cursor->mark, b);
30 mark_join(cursor->anchor, a);
31 }
32 return MLE_OK;
33 }
34
35 // Toggle cursor anchor
cursor_toggle_anchor(cursor_t * cursor,int use_srules)36 int cursor_toggle_anchor(cursor_t *cursor, int use_srules) {
37 if (!cursor->is_anchored) {
38 mark_clone(cursor->mark, &(cursor->anchor));
39 if (use_srules) {
40 cursor->sel_rule = srule_new_range(cursor->mark, cursor->anchor, 0, TB_REVERSE);
41 buffer_add_srule(cursor->bview->buffer, cursor->sel_rule);
42 }
43 cursor->is_anchored = 1;
44 } else {
45 if (use_srules && cursor->sel_rule) {
46 buffer_remove_srule(cursor->bview->buffer, cursor->sel_rule);
47 srule_destroy(cursor->sel_rule);
48 cursor->sel_rule = NULL;
49 }
50 mark_destroy(cursor->anchor);
51 cursor->is_anchored = 0;
52 }
53 return MLE_OK;
54 }
55
56 // Drop cursor anchor
cursor_drop_anchor(cursor_t * cursor,int use_srules)57 int cursor_drop_anchor(cursor_t *cursor, int use_srules) {
58 if (cursor->is_anchored) return MLE_OK;
59 return cursor_toggle_anchor(cursor, use_srules);
60 }
61
62 // Lift cursor anchor
cursor_lift_anchor(cursor_t * cursor)63 int cursor_lift_anchor(cursor_t *cursor) {
64 if (!cursor->is_anchored) return MLE_OK;
65 return cursor_toggle_anchor(cursor, cursor->sel_rule ? 1 : 0);
66 }
67
68 // Get lo and hi marks in a is_anchored=1 cursor
cursor_get_lo_hi(cursor_t * cursor,mark_t ** ret_lo,mark_t ** ret_hi)69 int cursor_get_lo_hi(cursor_t *cursor, mark_t **ret_lo, mark_t **ret_hi) {
70 if (!cursor->is_anchored) {
71 return MLE_ERR;
72 }
73 if (mark_is_gt(cursor->anchor, cursor->mark)) {
74 *ret_lo = cursor->mark;
75 *ret_hi = cursor->anchor;
76 } else {
77 *ret_lo = cursor->anchor;
78 *ret_hi = cursor->mark;
79 }
80 return MLE_OK;
81 }
82
83 // Get mark
cursor_get_mark(cursor_t * cursor,mark_t ** ret_mark)84 int cursor_get_mark(cursor_t *cursor, mark_t **ret_mark) {
85 *ret_mark = cursor->mark;
86 return MLE_OK;
87 }
88
89 // Get anchor if anchored
cursor_get_anchor(cursor_t * cursor,mark_t ** ret_anchor)90 int cursor_get_anchor(cursor_t *cursor, mark_t **ret_anchor) {
91 *ret_anchor = cursor->is_anchored ? cursor->anchor : NULL;
92 return MLE_OK;
93 }
94
95 // Make selection by strat
cursor_select_by(cursor_t * cursor,const char * strat,int use_srules)96 int cursor_select_by(cursor_t *cursor, const char *strat, int use_srules) {
97 if (cursor->is_anchored) {
98 return MLE_ERR;
99 }
100 if (strcmp(strat, "bracket") == 0) {
101 return cursor_select_by_bracket(cursor, use_srules);
102 } else if (strcmp(strat, "word") == 0) {
103 return cursor_select_by_word(cursor, use_srules);
104 } else if (strcmp(strat, "word_back") == 0) {
105 return cursor_select_by_word_back(cursor, use_srules);
106 } else if (strcmp(strat, "word_forward") == 0) {
107 return cursor_select_by_word_forward(cursor, use_srules);
108 } else if (strcmp(strat, "eol") == 0 && !mark_is_at_eol(cursor->mark)) {
109 cursor_toggle_anchor(cursor, use_srules);
110 mark_move_eol(cursor->anchor);
111 } else if (strcmp(strat, "bol") == 0 && !mark_is_at_bol(cursor->mark)) {
112 cursor_toggle_anchor(cursor, use_srules);
113 mark_move_bol(cursor->anchor);
114 } else if (strcmp(strat, "string") == 0) {
115 return cursor_select_by_string(cursor, use_srules);
116 } else {
117 MLE_RETURN_ERR(cursor->bview->editor, "Unrecognized cursor_select_by strat '%s'", strat);
118 }
119 return MLE_OK;
120 }
121
122 // Select by bracket
cursor_select_by_bracket(cursor_t * cursor,int use_srules)123 int cursor_select_by_bracket(cursor_t *cursor, int use_srules) {
124 mark_t *orig;
125 mark_clone(cursor->mark, &orig);
126 if (mark_move_bracket_top(cursor->mark, MLE_BRACKET_PAIR_MAX_SEARCH) != MLBUF_OK) {
127 mark_destroy(orig);
128 return MLE_ERR;
129 }
130 cursor_toggle_anchor(cursor, use_srules);
131 if (mark_move_bracket_pair(cursor->anchor, MLE_BRACKET_PAIR_MAX_SEARCH) != MLBUF_OK) {
132 cursor_toggle_anchor(cursor, 0);
133 mark_join(cursor->mark, orig);
134 mark_destroy(orig);
135 return MLE_ERR;
136 }
137 mark_move_by(cursor->mark, 1);
138 mark_destroy(orig);
139 return MLE_OK;
140 }
141
142 // Select by word-back
cursor_select_by_word_back(cursor_t * cursor,int use_srules)143 int cursor_select_by_word_back(cursor_t *cursor, int use_srules) {
144 if (mark_is_at_word_bound(cursor->mark, -1)) return MLE_ERR;
145 cursor_toggle_anchor(cursor, use_srules);
146 mark_move_prev_re(cursor->mark, MLE_RE_WORD_BACK, sizeof(MLE_RE_WORD_BACK)-1);
147 return MLE_OK;
148 }
149
150 // Select by word-forward
cursor_select_by_word_forward(cursor_t * cursor,int use_srules)151 int cursor_select_by_word_forward(cursor_t *cursor, int use_srules) {
152 if (mark_is_at_word_bound(cursor->mark, 1)) return MLE_ERR;
153 cursor_toggle_anchor(cursor, use_srules);
154 mark_move_next_re(cursor->mark, MLE_RE_WORD_FORWARD, sizeof(MLE_RE_WORD_FORWARD)-1);
155 return MLE_OK;
156 }
157
158 // Select by string
cursor_select_by_string(cursor_t * cursor,int use_srules)159 int cursor_select_by_string(cursor_t *cursor, int use_srules) {
160 mark_t *orig;
161 uint32_t qchar;
162 char *qre;
163 mark_clone(cursor->mark, &orig);
164 if (mark_move_prev_re(cursor->mark, "(?<!\\\\)[`'\"]", strlen("(?<!\\\\)[`'\"]")) != MLBUF_OK) {
165 mark_destroy(orig);
166 return MLE_ERR;
167 }
168 cursor_toggle_anchor(cursor, use_srules);
169 mark_get_char_after(cursor->mark, &qchar);
170 mark_move_by(cursor->mark, 1);
171 if (qchar == '"') {
172 qre = "(?<!\\\\)\"";
173 } else if (qchar == '\'') {
174 qre = "(?<!\\\\)'";
175 } else {
176 qre = "(?<!\\\\)`";
177 }
178 if (mark_move_next_re_nudge(cursor->anchor, qre, strlen(qre)) != MLBUF_OK) {
179 cursor_toggle_anchor(cursor, use_srules);
180 mark_join(cursor->mark, orig);
181 mark_destroy(orig);
182 return MLE_ERR;
183 }
184 mark_destroy(orig);
185 return MLE_OK;
186 }
187
188 // Select by word
cursor_select_by_word(cursor_t * cursor,int use_srules)189 int cursor_select_by_word(cursor_t *cursor, int use_srules) {
190 uint32_t after;
191 if (mark_is_at_eol(cursor->mark)) return MLE_ERR;
192 mark_get_char_after(cursor->mark, &after);
193 if (!isalnum((char)after) && (char)after != '_') return MLE_ERR;
194 if (!mark_is_at_word_bound(cursor->mark, -1)) {
195 mark_move_prev_re(cursor->mark, MLE_RE_WORD_BACK, sizeof(MLE_RE_WORD_BACK)-1);
196 }
197 cursor_toggle_anchor(cursor, use_srules);
198 mark_move_next_re(cursor->mark, MLE_RE_WORD_FORWARD, sizeof(MLE_RE_WORD_FORWARD)-1);
199 return MLE_OK;
200 }
201
202 // Cut or copy text
cursor_cut_copy(cursor_t * cursor,int is_cut,int use_srules,int append)203 int cursor_cut_copy(cursor_t *cursor, int is_cut, int use_srules, int append) {
204 char *cutbuf;
205 bint_t cutbuf_len;
206 bint_t cur_len;
207 if (!append && cursor->cut_buffer) {
208 free(cursor->cut_buffer);
209 cursor->cut_buffer = NULL;
210 }
211 if (!cursor->is_anchored) {
212 use_srules = 0;
213 cursor_toggle_anchor(cursor, use_srules);
214 mark_move_bol(cursor->mark);
215 mark_move_eol(cursor->anchor);
216 mark_move_by(cursor->anchor, 1);
217 }
218 mark_get_between_mark(cursor->mark, cursor->anchor, &cutbuf, &cutbuf_len);
219 if (append && cursor->cut_buffer) {
220 cur_len = strlen(cursor->cut_buffer);
221 cursor->cut_buffer = realloc(cursor->cut_buffer, cur_len + cutbuf_len + 1);
222 strncat(cursor->cut_buffer, cutbuf, cutbuf_len);
223 free(cutbuf);
224 } else {
225 cursor->cut_buffer = cutbuf;
226 }
227 if (cursor->bview->editor->cut_buffer) free(cursor->bview->editor->cut_buffer);
228 cursor->bview->editor->cut_buffer = strdup(cursor->cut_buffer);
229 if (is_cut) {
230 mark_delete_between_mark(cursor->mark, cursor->anchor);
231 }
232 cursor_toggle_anchor(cursor, use_srules);
233 return MLE_OK;
234 }
235
236 // Uncut (paste) text
cursor_uncut(cursor_t * cursor)237 int cursor_uncut(cursor_t *cursor) {
238 char *cut_buffer;
239 cut_buffer = cursor->cut_buffer ? cursor->cut_buffer : cursor->bview->editor->cut_buffer;
240 if (!cut_buffer) return MLE_ERR;
241 mark_insert_before(cursor->mark, cut_buffer, strlen(cut_buffer));
242 return MLE_OK;
243 }
244
245 // Regex search and replace
cursor_replace(cursor_t * cursor,int interactive,char * opt_regex,char * opt_replacement)246 int cursor_replace(cursor_t *cursor, int interactive, char *opt_regex, char *opt_replacement) {
247 char *regex;
248 char *replacement;
249 int wrapped;
250 int all;
251 char *yn;
252 mark_t *lo_mark;
253 mark_t *hi_mark;
254 mark_t *orig_mark;
255 mark_t *search_mark;
256 mark_t *search_mark_end;
257 int anchored_before;
258 srule_t *highlight;
259 bline_t *bline;
260 bint_t col;
261 bint_t char_count;
262 bint_t orig_viewport_y;
263 int pcre_rc;
264 int pcre_ovector[30];
265 str_t repl_backref = {0};
266 int num_replacements;
267
268 if (!interactive && (!opt_regex || !opt_replacement)) {
269 return MLE_ERR;
270 }
271
272 regex = NULL;
273 replacement = NULL;
274 wrapped = 0;
275 lo_mark = NULL;
276 hi_mark = NULL;
277 orig_mark = NULL;
278 search_mark = NULL;
279 search_mark_end = NULL;
280 anchored_before = 0;
281 all = interactive ? 0 : 1;
282 num_replacements = 0;
283 mark_set_pcre_capture(&pcre_rc, pcre_ovector, 30);
284 orig_viewport_y = -1;
285
286 do {
287 if (!interactive) {
288 regex = strdup(opt_regex);
289 replacement = strdup(opt_replacement);
290 } else {
291 editor_prompt(cursor->bview->editor, "replace: Search regex?", NULL, ®ex);
292 if (!regex) break;
293 editor_prompt(cursor->bview->editor, "replace: Replacement string?", NULL, &replacement);
294 if (!replacement) break;
295 }
296 orig_mark = buffer_add_mark(cursor->bview->buffer, NULL, 0);
297 lo_mark = buffer_add_mark(cursor->bview->buffer, NULL, 0);
298 hi_mark = buffer_add_mark(cursor->bview->buffer, NULL, 0);
299 search_mark = buffer_add_mark(cursor->bview->buffer, NULL, 0);
300 search_mark_end = buffer_add_mark(cursor->bview->buffer, NULL, 0);
301 mark_join(search_mark, cursor->mark);
302 mark_join(orig_mark, cursor->mark);
303 orig_viewport_y = cursor->bview->viewport_y;
304 orig_mark->lefty = 1; // lefty==1 avoids moving when text is inserted at mark
305 lo_mark->lefty = 1;
306 if (cursor->is_anchored) {
307 anchored_before = mark_is_gt(cursor->mark, cursor->anchor);
308 mark_join(lo_mark, !anchored_before ? cursor->mark : cursor->anchor);
309 mark_join(hi_mark, anchored_before ? cursor->mark : cursor->anchor);
310 } else {
311 mark_move_beginning(lo_mark);
312 mark_move_end(hi_mark);
313 }
314 while (1) {
315 pcre_rc = 0;
316 if (mark_find_next_re(search_mark, regex, strlen(regex), &bline, &col, &char_count) == MLBUF_OK
317 && (mark_move_to(search_mark, bline->line_index, col) == MLBUF_OK)
318 && (mark_is_gte(search_mark, lo_mark))
319 && (mark_is_lt(search_mark, hi_mark))
320 && (!wrapped || mark_is_lt(search_mark, orig_mark))
321 ) {
322 mark_move_to(search_mark_end, bline->line_index, col + char_count);
323 mark_join(cursor->mark, search_mark);
324 yn = NULL;
325 if (all) {
326 yn = MLE_PROMPT_YES;
327 } else if (interactive) {
328 highlight = srule_new_range(search_mark, search_mark_end, 0, TB_REVERSE);
329 buffer_add_srule(cursor->bview->buffer, highlight);
330 bview_rectify_viewport(cursor->bview);
331 bview_draw(cursor->bview);
332 editor_prompt(cursor->bview->editor, "replace: OK to replace? (y=yes, n=no, a=all, C-c=stop)",
333 &(editor_prompt_params_t) { .kmap = cursor->bview->editor->kmap_prompt_yna }, &yn
334 );
335 buffer_remove_srule(cursor->bview->buffer, highlight);
336 srule_destroy(highlight);
337 bview_draw(cursor->bview);
338 }
339 if (!yn) {
340 break;
341 } else if (0 == strcmp(yn, MLE_PROMPT_YES) || 0 == strcmp(yn, MLE_PROMPT_ALL)) {
342 str_append_replace_with_backrefs(&repl_backref, search_mark->bline->data, replacement, pcre_rc, pcre_ovector, 30);
343 mark_replace_between_mark(search_mark, search_mark_end, repl_backref.data, repl_backref.len);
344 str_free(&repl_backref);
345 num_replacements += 1;
346 if (0 == strcmp(yn, MLE_PROMPT_ALL)) all = 1;
347 } else {
348 mark_move_by(search_mark, 1);
349 }
350 } else if (!wrapped) {
351 mark_join(search_mark, lo_mark);
352 wrapped = 1;
353 } else {
354 break;
355 }
356 }
357 } while(0);
358
359 if (cursor->is_anchored && lo_mark && hi_mark) {
360 mark_join(cursor->mark, anchored_before ? hi_mark : lo_mark);
361 mark_join(cursor->anchor, anchored_before ? lo_mark : hi_mark);
362 } else if (orig_mark) {
363 mark_join(cursor->mark, orig_mark);
364 }
365
366 mark_set_pcre_capture(NULL, NULL, 0);
367 if (regex) free(regex);
368 if (replacement) free(replacement);
369 if (lo_mark) mark_destroy(lo_mark);
370 if (hi_mark) mark_destroy(hi_mark);
371 if (orig_mark) mark_destroy(orig_mark);
372 if (search_mark) mark_destroy(search_mark);
373 if (search_mark_end) mark_destroy(search_mark_end);
374
375 if (interactive) {
376 MLE_SET_INFO(cursor->bview->editor, "replace: Replaced %d instance(s)", num_replacements);
377 if (orig_viewport_y >= 0) {
378 bview_set_viewport_y(cursor->bview, orig_viewport_y, 1);
379 } else {
380 bview_rectify_viewport(cursor->bview);
381 }
382 bview_draw(cursor->bview);
383 }
384
385 return MLE_OK;
386 }
387