Implement blackhole register "_
[vis.git] / vis-motions.c
blob91224b504c5be1df6b5f4525c9b7ba584dea59f2
1 #include <stdio.h>
2 #include <string.h>
3 #include <regex.h>
4 #include "vis-core.h"
5 #include "text-motions.h"
6 #include "text-objects.h"
7 #include "text-util.h"
9 /** utility functions */
11 static bool search_word(Vis *vis, Text *txt, size_t pos) {
12 char expr[512];
13 Filerange word = text_object_word(txt, pos);
14 if (!text_range_valid(&word))
15 return false;
16 char *buf = text_bytes_alloc0(txt, word.start, text_range_size(&word));
17 if (!buf)
18 return false;
19 snprintf(expr, sizeof(expr), "\\<%s\\>", buf);
20 free(buf);
21 return text_regex_compile(vis->search_pattern, expr, REG_EXTENDED) == 0;
24 /** motion implementations */
26 static size_t search_word_forward(Vis *vis, Text *txt, size_t pos) {
27 if (search_word(vis, txt, pos))
28 pos = text_search_forward(txt, pos, vis->search_pattern);
29 return pos;
32 static size_t search_word_backward(Vis *vis, Text *txt, size_t pos) {
33 if (search_word(vis, txt, pos))
34 pos = text_search_backward(txt, pos, vis->search_pattern);
35 return pos;
38 static size_t search_forward(Vis *vis, Text *txt, size_t pos) {
39 return text_search_forward(txt, pos, vis->search_pattern);
42 static size_t search_backward(Vis *vis, Text *txt, size_t pos) {
43 return text_search_backward(txt, pos, vis->search_pattern);
46 static size_t mark_goto(Vis *vis, File *file, size_t pos) {
47 return text_mark_get(file->text, file->marks[vis->action.mark]);
50 static size_t mark_line_goto(Vis *vis, File *file, size_t pos) {
51 return text_line_start(file->text, mark_goto(vis, file, pos));
54 static size_t to(Vis *vis, Text *txt, size_t pos) {
55 char c;
56 if (pos == text_line_end(txt, pos))
57 return pos;
58 size_t hit = text_line_find_next(txt, pos+1, vis->search_char);
59 if (!text_byte_get(txt, hit, &c) || c != vis->search_char[0])
60 return pos;
61 return hit;
64 static size_t till(Vis *vis, Text *txt, size_t pos) {
65 size_t hit = to(vis, txt, pos+1);
66 if (pos == text_line_end(txt, pos))
67 return pos;
68 if (hit != pos)
69 return text_char_prev(txt, hit);
70 return pos;
73 static size_t to_left(Vis *vis, Text *txt, size_t pos) {
74 char c;
75 if (pos == text_line_begin(txt, pos))
76 return pos;
77 size_t hit = text_line_find_prev(txt, pos-1, vis->search_char);
78 if (!text_byte_get(txt, hit, &c) || c != vis->search_char[0])
79 return pos;
80 return hit;
83 static size_t till_left(Vis *vis, Text *txt, size_t pos) {
84 if (pos == text_line_begin(txt, pos))
85 return pos;
86 size_t hit = to_left(vis, txt, pos-1);
87 if (hit != pos)
88 return text_char_next(txt, hit);
89 return pos;
92 static size_t line(Vis *vis, Text *txt, size_t pos) {
93 return text_pos_by_lineno(txt, vis_count_get_default(vis, 1));
96 static size_t column(Vis *vis, Text *txt, size_t pos) {
97 return text_line_offset(txt, pos, vis_count_get_default(vis, 0));
100 static size_t view_lines_top(Vis *vis, View *view) {
101 return view_screenline_goto(view, vis_count_get_default(vis, 1));
104 static size_t view_lines_middle(Vis *vis, View *view) {
105 int h = view_height_get(view);
106 return view_screenline_goto(view, h/2);
109 static size_t view_lines_bottom(Vis *vis, View *view) {
110 int h = view_height_get(vis->win->view);
111 return view_screenline_goto(vis->win->view, h - vis_count_get_default(vis, 0));
114 static size_t window_changelist_next(Vis *vis, Win *win, size_t pos) {
115 ChangeList *cl = &win->changelist;
116 Text *txt = win->file->text;
117 time_t state = text_state(txt);
118 if (cl->state != state)
119 cl->index = 0;
120 else if (cl->index > 0 && pos == cl->pos)
121 cl->index--;
122 size_t newpos = text_history_get(txt, cl->index);
123 if (newpos == EPOS)
124 cl->index++;
125 else
126 cl->pos = newpos;
127 cl->state = state;
128 return cl->pos;
131 static size_t window_changelist_prev(Vis *vis, Win *win, size_t pos) {
132 ChangeList *cl = &win->changelist;
133 Text *txt = win->file->text;
134 time_t state = text_state(txt);
135 if (cl->state != state)
136 cl->index = 0;
137 else if (pos == cl->pos)
138 win->changelist.index++;
139 size_t newpos = text_history_get(txt, cl->index);
140 if (newpos == EPOS)
141 cl->index--;
142 else
143 cl->pos = newpos;
144 cl->state = state;
145 return cl->pos;
148 static size_t window_jumplist_next(Vis *vis, Win *win, size_t cur) {
149 while (win->jumplist) {
150 Mark mark = ringbuf_next(win->jumplist);
151 if (!mark)
152 return cur;
153 size_t pos = text_mark_get(win->file->text, mark);
154 if (pos != EPOS && pos != cur)
155 return pos;
157 return cur;
160 static size_t window_jumplist_prev(Vis *vis, Win *win, size_t cur) {
161 while (win->jumplist) {
162 Mark mark = ringbuf_prev(win->jumplist);
163 if (!mark)
164 return cur;
165 size_t pos = text_mark_get(win->file->text, mark);
166 if (pos != EPOS && pos != cur)
167 return pos;
169 return cur;
172 static size_t window_nop(Vis *vis, Win *win, size_t pos) {
173 return pos;
176 static size_t bracket_match(Text *txt, size_t pos) {
177 size_t hit = text_bracket_match_symbol(txt, pos, "(){}[]");
178 if (hit != pos)
179 return hit;
180 char current;
181 Iterator it = text_iterator_get(txt, pos);
182 while (text_iterator_byte_get(&it, &current)) {
183 switch (current) {
184 case '(':
185 case ')':
186 case '{':
187 case '}':
188 case '[':
189 case ']':
190 return it.pos;
192 text_iterator_byte_next(&it, NULL);
194 return pos;
197 static size_t percent(Vis *vis, Text *txt, size_t pos) {
198 int ratio = vis_count_get_default(vis, 0);
199 if (ratio > 100)
200 ratio = 100;
201 return text_size(txt) * ratio / 100;
204 void vis_motion_type(Vis *vis, enum VisMotionType type) {
205 vis->action.type = type;
208 bool vis_motion(Vis *vis, enum VisMotion motion, ...) {
209 va_list ap;
210 va_start(ap, motion);
212 switch (motion) {
213 case VIS_MOVE_WORD_START_NEXT:
214 if (vis->action.op == &vis_operators[VIS_OP_CHANGE])
215 motion = VIS_MOVE_WORD_END_NEXT;
216 break;
217 case VIS_MOVE_LONGWORD_START_NEXT:
218 if (vis->action.op == &vis_operators[VIS_OP_CHANGE])
219 motion = VIS_MOVE_LONGWORD_END_NEXT;
220 break;
221 case VIS_MOVE_SEARCH_FORWARD:
222 case VIS_MOVE_SEARCH_BACKWARD:
224 const char *pattern = va_arg(ap, char*);
225 if (text_regex_compile(vis->search_pattern, pattern, REG_EXTENDED)) {
226 vis_cancel(vis);
227 goto err;
229 if (motion == VIS_MOVE_SEARCH_FORWARD)
230 motion = VIS_MOVE_SEARCH_NEXT;
231 else
232 motion = VIS_MOVE_SEARCH_PREV;
233 break;
235 case VIS_MOVE_RIGHT_TO:
236 case VIS_MOVE_LEFT_TO:
237 case VIS_MOVE_RIGHT_TILL:
238 case VIS_MOVE_LEFT_TILL:
240 const char *key = va_arg(ap, char*);
241 if (!key)
242 goto err;
243 strncpy(vis->search_char, key, sizeof(vis->search_char));
244 vis->search_char[sizeof(vis->search_char)-1] = '\0';
245 vis->last_totill = motion;
246 break;
248 case VIS_MOVE_TOTILL_REPEAT:
249 if (!vis->last_totill)
250 goto err;
251 motion = vis->last_totill;
252 break;
253 case VIS_MOVE_TOTILL_REVERSE:
254 switch (vis->last_totill) {
255 case VIS_MOVE_RIGHT_TO:
256 motion = VIS_MOVE_LEFT_TO;
257 break;
258 case VIS_MOVE_LEFT_TO:
259 motion = VIS_MOVE_RIGHT_TO;
260 break;
261 case VIS_MOVE_RIGHT_TILL:
262 motion = VIS_MOVE_LEFT_TILL;
263 break;
264 case VIS_MOVE_LEFT_TILL:
265 motion = VIS_MOVE_RIGHT_TILL;
266 break;
267 default:
268 goto err;
270 break;
271 case VIS_MOVE_MARK:
272 case VIS_MOVE_MARK_LINE:
274 int mark = va_arg(ap, int);
275 if (VIS_MARK_a <= mark && mark < VIS_MARK_INVALID)
276 vis->action.mark = mark;
277 else
278 goto err;
279 break;
281 default:
282 break;
285 vis->action.movement = &vis_motions[motion];
286 va_end(ap);
287 action_do(vis, &vis->action);
288 return true;
289 err:
290 va_end(ap);
291 return false;
294 Movement vis_motions[] = {
295 [VIS_MOVE_LINE_UP] = { .cur = view_line_up, .type = LINEWISE },
296 [VIS_MOVE_LINE_DOWN] = { .cur = view_line_down, .type = LINEWISE },
297 [VIS_MOVE_SCREEN_LINE_UP] = { .cur = view_screenline_up, },
298 [VIS_MOVE_SCREEN_LINE_DOWN] = { .cur = view_screenline_down, },
299 [VIS_MOVE_SCREEN_LINE_BEGIN] = { .cur = view_screenline_begin, .type = CHARWISE },
300 [VIS_MOVE_SCREEN_LINE_MIDDLE] = { .cur = view_screenline_middle, .type = CHARWISE },
301 [VIS_MOVE_SCREEN_LINE_END] = { .cur = view_screenline_end, .type = CHARWISE|INCLUSIVE },
302 [VIS_MOVE_LINE_PREV] = { .txt = text_line_prev, },
303 [VIS_MOVE_LINE_BEGIN] = { .txt = text_line_begin, },
304 [VIS_MOVE_LINE_START] = { .txt = text_line_start, },
305 [VIS_MOVE_LINE_FINISH] = { .txt = text_line_finish, .type = INCLUSIVE },
306 [VIS_MOVE_LINE_LASTCHAR] = { .txt = text_line_lastchar, .type = INCLUSIVE },
307 [VIS_MOVE_LINE_END] = { .txt = text_line_end, },
308 [VIS_MOVE_LINE_NEXT] = { .txt = text_line_next, },
309 [VIS_MOVE_LINE] = { .vis = line, .type = LINEWISE|IDEMPOTENT|JUMP },
310 [VIS_MOVE_COLUMN] = { .vis = column, .type = CHARWISE|IDEMPOTENT },
311 [VIS_MOVE_CHAR_PREV] = { .txt = text_char_prev, .type = CHARWISE },
312 [VIS_MOVE_CHAR_NEXT] = { .txt = text_char_next, .type = CHARWISE },
313 [VIS_MOVE_LINE_CHAR_PREV] = { .txt = text_line_char_prev, .type = CHARWISE },
314 [VIS_MOVE_LINE_CHAR_NEXT] = { .txt = text_line_char_next, .type = CHARWISE },
315 [VIS_MOVE_WORD_START_PREV] = { .txt = text_word_start_prev, .type = CHARWISE },
316 [VIS_MOVE_WORD_START_NEXT] = { .txt = text_word_start_next, .type = CHARWISE },
317 [VIS_MOVE_WORD_END_PREV] = { .txt = text_word_end_prev, .type = CHARWISE|INCLUSIVE },
318 [VIS_MOVE_WORD_END_NEXT] = { .txt = text_word_end_next, .type = CHARWISE|INCLUSIVE },
319 [VIS_MOVE_LONGWORD_START_PREV] = { .txt = text_longword_start_prev, .type = CHARWISE },
320 [VIS_MOVE_LONGWORD_START_NEXT] = { .txt = text_longword_start_next, .type = CHARWISE },
321 [VIS_MOVE_LONGWORD_END_PREV] = { .txt = text_longword_end_prev, .type = CHARWISE|INCLUSIVE },
322 [VIS_MOVE_LONGWORD_END_NEXT] = { .txt = text_longword_end_next, .type = CHARWISE|INCLUSIVE },
323 [VIS_MOVE_SENTENCE_PREV] = { .txt = text_sentence_prev, .type = CHARWISE },
324 [VIS_MOVE_SENTENCE_NEXT] = { .txt = text_sentence_next, .type = CHARWISE },
325 [VIS_MOVE_PARAGRAPH_PREV] = { .txt = text_paragraph_prev, .type = LINEWISE|JUMP },
326 [VIS_MOVE_PARAGRAPH_NEXT] = { .txt = text_paragraph_next, .type = LINEWISE|JUMP },
327 [VIS_MOVE_FUNCTION_START_PREV] = { .txt = text_function_start_prev, .type = LINEWISE|JUMP },
328 [VIS_MOVE_FUNCTION_START_NEXT] = { .txt = text_function_start_next, .type = LINEWISE|JUMP },
329 [VIS_MOVE_FUNCTION_END_PREV] = { .txt = text_function_end_prev, .type = LINEWISE|JUMP },
330 [VIS_MOVE_FUNCTION_END_NEXT] = { .txt = text_function_end_next, .type = LINEWISE|JUMP },
331 [VIS_MOVE_BRACKET_MATCH] = { .txt = bracket_match, .type = INCLUSIVE|JUMP },
332 [VIS_MOVE_FILE_BEGIN] = { .txt = text_begin, .type = LINEWISE|JUMP },
333 [VIS_MOVE_FILE_END] = { .txt = text_end, .type = LINEWISE|JUMP },
334 [VIS_MOVE_LEFT_TO] = { .vis = to_left, },
335 [VIS_MOVE_RIGHT_TO] = { .vis = to, .type = INCLUSIVE },
336 [VIS_MOVE_LEFT_TILL] = { .vis = till_left, },
337 [VIS_MOVE_RIGHT_TILL] = { .vis = till, .type = INCLUSIVE },
338 [VIS_MOVE_MARK] = { .file = mark_goto, .type = JUMP|IDEMPOTENT },
339 [VIS_MOVE_MARK_LINE] = { .file = mark_line_goto, .type = LINEWISE|JUMP|IDEMPOTENT },
340 [VIS_MOVE_SEARCH_WORD_FORWARD] = { .vis = search_word_forward, .type = JUMP },
341 [VIS_MOVE_SEARCH_WORD_BACKWARD]= { .vis = search_word_backward, .type = JUMP },
342 [VIS_MOVE_SEARCH_NEXT] = { .vis = search_forward, .type = JUMP },
343 [VIS_MOVE_SEARCH_PREV] = { .vis = search_backward, .type = JUMP },
344 [VIS_MOVE_WINDOW_LINE_TOP] = { .view = view_lines_top, .type = LINEWISE|JUMP|IDEMPOTENT },
345 [VIS_MOVE_WINDOW_LINE_MIDDLE] = { .view = view_lines_middle, .type = LINEWISE|JUMP|IDEMPOTENT },
346 [VIS_MOVE_WINDOW_LINE_BOTTOM] = { .view = view_lines_bottom, .type = LINEWISE|JUMP|IDEMPOTENT },
347 [VIS_MOVE_CHANGELIST_NEXT] = { .win = window_changelist_next, .type = INCLUSIVE },
348 [VIS_MOVE_CHANGELIST_PREV] = { .win = window_changelist_prev, .type = INCLUSIVE },
349 [VIS_MOVE_JUMPLIST_NEXT] = { .win = window_jumplist_next, .type = INCLUSIVE },
350 [VIS_MOVE_JUMPLIST_PREV] = { .win = window_jumplist_prev, .type = INCLUSIVE },
351 [VIS_MOVE_NOP] = { .win = window_nop, .type = IDEMPOTENT },
352 [VIS_MOVE_PERCENT] = { .vis = percent, .type = IDEMPOTENT },