lexers/pkgbuild: match functions with parentheses
[vis.git] / vis-operators.c
blobb7b2335697be8c45a0aab98d8855b7796c0ac4eb
1 #include <string.h>
2 #include <ctype.h>
3 #include "vis-core.h"
4 #include "text-motions.h"
5 #include "text-objects.h"
6 #include "text-util.h"
7 #include "util.h"
9 static size_t op_delete(Vis *vis, Text *txt, OperatorContext *c) {
10 c->reg->linewise = c->linewise;
11 register_put_range(vis, c->reg, txt, &c->range);
12 text_delete_range(txt, &c->range);
13 size_t pos = c->range.start;
14 if (c->linewise && pos == text_size(txt))
15 pos = text_line_begin(txt, text_line_prev(txt, pos));
16 return pos;
19 static size_t op_change(Vis *vis, Text *txt, OperatorContext *c) {
20 op_delete(vis, txt, c);
21 return c->range.start;
24 static size_t op_yank(Vis *vis, Text *txt, OperatorContext *c) {
25 c->reg->linewise = c->linewise;
26 register_put_range(vis, c->reg, txt, &c->range);
27 if (c->reg == &vis->registers[VIS_REG_DEFAULT])
28 register_put_range(vis, &vis->registers[VIS_REG_ZERO], txt, &c->range);
29 return c->linewise ? c->pos : c->range.start;
32 static size_t op_put(Vis *vis, Text *txt, OperatorContext *c) {
33 char b;
34 size_t pos = c->pos;
35 bool sel = text_range_size(&c->range) > 0;
36 bool sel_linewise = sel && text_range_is_linewise(txt, &c->range);
37 if (sel) {
38 text_delete_range(txt, &c->range);
39 pos = c->pos = c->range.start;
41 switch (c->arg->i) {
42 case VIS_OP_PUT_AFTER:
43 case VIS_OP_PUT_AFTER_END:
44 if (c->reg->linewise && !sel_linewise)
45 pos = text_line_next(txt, pos);
46 else if (!sel && text_byte_get(txt, pos, &b) && b != '\r' && b != '\n')
47 pos = text_char_next(txt, pos);
48 break;
49 case VIS_OP_PUT_BEFORE:
50 case VIS_OP_PUT_BEFORE_END:
51 if (c->reg->linewise)
52 pos = text_line_begin(txt, pos);
53 break;
56 size_t len;
57 const char *data = register_get(vis, c->reg, &len);
59 for (int i = 0; i < c->count; i++) {
60 char nl;
61 if (c->reg->linewise && pos > 0 && text_byte_get(txt, pos-1, &nl) && nl != '\n')
62 pos += text_insert_newline(txt, pos);
63 text_insert(txt, pos, data, len);
64 pos += len;
65 if (c->reg->linewise && pos > 0 && text_byte_get(txt, pos-1, &nl) && nl != '\n')
66 pos += text_insert_newline(txt, pos);
69 if (c->reg->linewise) {
70 switch (c->arg->i) {
71 case VIS_OP_PUT_BEFORE_END:
72 case VIS_OP_PUT_AFTER_END:
73 pos = text_line_start(txt, pos);
74 break;
75 case VIS_OP_PUT_AFTER:
76 pos = text_line_start(txt, text_line_next(txt, c->pos));
77 break;
78 case VIS_OP_PUT_BEFORE:
79 pos = text_line_start(txt, c->pos);
80 break;
82 } else {
83 switch (c->arg->i) {
84 case VIS_OP_PUT_AFTER:
85 case VIS_OP_PUT_BEFORE:
86 pos = text_char_prev(txt, pos);
87 break;
91 return pos;
94 static size_t op_shift_right(Vis *vis, Text *txt, OperatorContext *c) {
95 char spaces[9] = " ";
96 spaces[MIN(vis->tabwidth, LENGTH(spaces) - 1)] = '\0';
97 const char *tab = vis->expandtab ? spaces : "\t";
98 size_t tablen = strlen(tab);
99 size_t pos = text_line_begin(txt, c->range.end), prev_pos;
100 size_t inserted = 0;
102 /* if range ends at the begin of a line, skip line break */
103 if (pos == c->range.end)
104 pos = text_line_prev(txt, pos);
106 do {
107 prev_pos = pos = text_line_begin(txt, pos);
108 text_insert(txt, pos, tab, tablen);
109 pos = text_line_prev(txt, pos);
110 inserted += tablen;
111 } while (pos >= c->range.start && pos != prev_pos);
113 return c->pos + inserted;
116 static size_t op_shift_left(Vis *vis, Text *txt, OperatorContext *c) {
117 size_t pos = text_line_begin(txt, c->range.end), prev_pos;
118 size_t tabwidth = vis->tabwidth, tablen;
119 size_t deleted = 0;
121 /* if range ends at the begin of a line, skip line break */
122 if (pos == c->range.end)
123 pos = text_line_prev(txt, pos);
125 do {
126 char c;
127 size_t len = 0;
128 prev_pos = pos = text_line_begin(txt, pos);
129 Iterator it = text_iterator_get(txt, pos);
130 if (text_iterator_byte_get(&it, &c) && c == '\t') {
131 len = 1;
132 } else {
133 for (len = 0; text_iterator_byte_get(&it, &c) && c == ' '; len++)
134 text_iterator_byte_next(&it, NULL);
136 tablen = MIN(len, tabwidth);
137 text_delete(txt, pos, tablen);
138 pos = text_line_prev(txt, pos);
139 deleted += tablen;
140 } while (pos >= c->range.start && pos != prev_pos);
142 return c->pos - deleted;
145 static size_t op_case_change(Vis *vis, Text *txt, OperatorContext *c) {
146 size_t len = text_range_size(&c->range);
147 char *buf = malloc(len);
148 if (!buf)
149 return c->pos;
150 len = text_bytes_get(txt, c->range.start, len, buf);
151 size_t rem = len;
152 for (char *cur = buf; rem > 0; cur++, rem--) {
153 int ch = (unsigned char)*cur;
154 if (isascii(ch)) {
155 if (c->arg->i == VIS_OP_CASE_SWAP)
156 *cur = islower(ch) ? toupper(ch) : tolower(ch);
157 else if (c->arg->i == VIS_OP_CASE_UPPER)
158 *cur = toupper(ch);
159 else
160 *cur = tolower(ch);
164 text_delete(txt, c->range.start, len);
165 text_insert(txt, c->range.start, buf, len);
166 free(buf);
167 return c->pos;
170 static size_t op_cursor(Vis *vis, Text *txt, OperatorContext *c) {
171 View *view = vis->win->view;
172 Filerange r = text_range_linewise(txt, &c->range);
173 for (size_t line = text_range_line_first(txt, &r); line != EPOS; line = text_range_line_next(txt, &r, line)) {
174 size_t pos;
175 if (c->arg->i == VIS_OP_CURSOR_EOL)
176 pos = text_line_finish(txt, line);
177 else
178 pos = text_line_start(txt, line);
179 view_cursors_new_force(view, pos);
181 return EPOS;
184 static size_t op_join(Vis *vis, Text *txt, OperatorContext *c) {
185 size_t pos = text_line_begin(txt, c->range.end), prev_pos;
186 Mark mark = NULL;
188 /* if operator and range are both linewise, skip last line break */
189 if (c->linewise && text_range_is_linewise(txt, &c->range)) {
190 size_t line_prev = text_line_prev(txt, pos);
191 size_t line_prev_prev = text_line_prev(txt, line_prev);
192 if (line_prev_prev >= c->range.start)
193 pos = line_prev;
196 size_t len = c->arg->s ? strlen(c->arg->s) : 0;
198 do {
199 prev_pos = pos;
200 size_t end = text_line_start(txt, pos);
201 pos = text_char_next(txt, text_line_finish(txt, text_line_prev(txt, end)));
202 if (pos >= c->range.start && end > pos) {
203 text_delete(txt, pos, end - pos);
204 text_insert(txt, pos, c->arg->s, len);
205 if (!mark)
206 mark = text_mark_set(txt, pos);
207 } else {
208 break;
210 } while (pos != prev_pos);
212 size_t newpos = text_mark_get(txt, mark);
213 return newpos != EPOS ? newpos : c->range.start;
216 static size_t op_insert(Vis *vis, Text *txt, OperatorContext *c) {
217 return c->newpos != EPOS ? c->newpos : c->pos;
220 static size_t op_replace(Vis *vis, Text *txt, OperatorContext *c) {
221 return c->newpos != EPOS ? c->newpos : c->pos;
224 static size_t op_filter(Vis *vis, Text *txt, OperatorContext *c) {
225 return text_size(txt) + 1; /* do not change cursor position, would destroy selection */
228 bool vis_operator(Vis *vis, enum VisOperator id, ...) {
229 va_list ap;
230 va_start(ap, id);
232 switch (id) {
233 case VIS_OP_CASE_LOWER:
234 case VIS_OP_CASE_UPPER:
235 case VIS_OP_CASE_SWAP:
236 vis->action.arg.i = id;
237 id = VIS_OP_CASE_SWAP;
238 break;
239 case VIS_OP_CURSOR_SOL:
240 case VIS_OP_CURSOR_EOL:
241 vis->action.arg.i = id;
242 id = VIS_OP_CURSOR_SOL;
243 break;
244 case VIS_OP_PUT_AFTER:
245 case VIS_OP_PUT_AFTER_END:
246 case VIS_OP_PUT_BEFORE:
247 case VIS_OP_PUT_BEFORE_END:
248 vis->action.arg.i = id;
249 id = VIS_OP_PUT_AFTER;
250 break;
251 case VIS_OP_JOIN:
252 vis->action.arg.s = va_arg(ap, char*);
253 break;
254 case VIS_OP_FILTER:
255 vis->action.arg.s = va_arg(ap, char*);
256 /* fall through */
257 case VIS_OP_SHIFT_LEFT:
258 case VIS_OP_SHIFT_RIGHT:
259 vis_motion_type(vis, VIS_MOTIONTYPE_LINEWISE);
260 break;
261 default:
262 break;
264 if (id >= LENGTH(vis_operators))
265 goto err;
266 const Operator *op = &vis_operators[id];
267 if (vis->mode->visual) {
268 vis->action.op = op;
269 vis_do(vis);
270 goto out;
273 /* switch to operator mode inorder to make operator options and
274 * text-object available */
275 vis_mode_switch(vis, VIS_MODE_OPERATOR_PENDING);
276 if (vis->action.op == op) {
277 /* hacky way to handle double operators i.e. things like
278 * dd, yy etc where the second char isn't a movement */
279 vis->action.type = LINEWISE;
280 vis_motion(vis, VIS_MOVE_LINE_NEXT);
281 } else {
282 vis->action.op = op;
285 /* put is not a real operator, does not need a range to operate on */
286 if (id == VIS_OP_PUT_AFTER)
287 vis_motion(vis, VIS_MOVE_NOP);
289 out:
290 va_end(ap);
291 return true;
292 err:
293 va_end(ap);
294 return false;
297 const Operator vis_operators[] = {
298 [VIS_OP_DELETE] = { op_delete },
299 [VIS_OP_CHANGE] = { op_change },
300 [VIS_OP_YANK] = { op_yank },
301 [VIS_OP_PUT_AFTER] = { op_put },
302 [VIS_OP_SHIFT_RIGHT] = { op_shift_right },
303 [VIS_OP_SHIFT_LEFT] = { op_shift_left },
304 [VIS_OP_CASE_SWAP] = { op_case_change },
305 [VIS_OP_JOIN] = { op_join },
306 [VIS_OP_INSERT] = { op_insert },
307 [VIS_OP_REPLACE] = { op_replace },
308 [VIS_OP_CURSOR_SOL] = { op_cursor },
309 [VIS_OP_FILTER] = { op_filter },