Update .gitignore
[vis.git] / vis-operators.c
blob0923ef6556377899da88d145f9023c63925e1f28
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(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 macro_operator_record(vis);
22 return c->range.start;
25 static size_t op_yank(Vis *vis, Text *txt, OperatorContext *c) {
26 c->reg->linewise = c->linewise;
27 register_put(c->reg, txt, &c->range);
28 return c->pos;
31 static size_t op_put(Vis *vis, Text *txt, OperatorContext *c) {
32 size_t pos = c->pos;
33 switch (c->arg->i) {
34 case VIS_OP_PUT_AFTER:
35 case VIS_OP_PUT_AFTER_END:
36 if (c->reg->linewise)
37 pos = text_line_next(txt, pos);
38 else
39 pos = text_char_next(txt, pos);
40 break;
41 case VIS_OP_PUT_BEFORE:
42 case VIS_OP_PUT_BEFORE_END:
43 if (c->reg->linewise)
44 pos = text_line_begin(txt, pos);
45 break;
48 for (int i = 0; i < c->count; i++) {
49 text_insert(txt, pos, c->reg->data, c->reg->len);
50 pos += c->reg->len;
53 if (c->reg->linewise) {
54 switch (c->arg->i) {
55 case VIS_OP_PUT_BEFORE_END:
56 case VIS_OP_PUT_AFTER_END:
57 pos = text_line_start(txt, pos);
58 break;
59 case VIS_OP_PUT_AFTER:
60 pos = text_line_start(txt, text_line_next(txt, c->pos));
61 break;
62 case VIS_OP_PUT_BEFORE:
63 pos = text_line_start(txt, c->pos);
64 break;
66 } else {
67 switch (c->arg->i) {
68 case VIS_OP_PUT_AFTER:
69 case VIS_OP_PUT_BEFORE:
70 pos = text_char_prev(txt, pos);
71 break;
75 return pos;
78 static size_t op_shift_right(Vis *vis, Text *txt, OperatorContext *c) {
79 size_t pos = text_line_begin(txt, c->range.end), prev_pos;
80 const char *tab = expandtab(vis);
81 size_t tablen = strlen(tab);
83 /* if range ends at the begin of a line, skip line break */
84 if (pos == c->range.end)
85 pos = text_line_prev(txt, pos);
87 do {
88 prev_pos = pos = text_line_begin(txt, pos);
89 text_insert(txt, pos, tab, tablen);
90 pos = text_line_prev(txt, pos);
91 } while (pos >= c->range.start && pos != prev_pos);
93 return c->pos + tablen;
96 static size_t op_shift_left(Vis *vis, Text *txt, OperatorContext *c) {
97 size_t pos = text_line_begin(txt, c->range.end), prev_pos;
98 size_t tabwidth = vis->tabwidth, tablen;
100 /* if range ends at the begin of a line, skip line break */
101 if (pos == c->range.end)
102 pos = text_line_prev(txt, pos);
104 do {
105 char c;
106 size_t len = 0;
107 prev_pos = pos = text_line_begin(txt, pos);
108 Iterator it = text_iterator_get(txt, pos);
109 if (text_iterator_byte_get(&it, &c) && c == '\t') {
110 len = 1;
111 } else {
112 for (len = 0; text_iterator_byte_get(&it, &c) && c == ' '; len++)
113 text_iterator_byte_next(&it, NULL);
115 tablen = MIN(len, tabwidth);
116 text_delete(txt, pos, tablen);
117 pos = text_line_prev(txt, pos);
118 } while (pos >= c->range.start && pos != prev_pos);
120 return c->pos - tablen;
123 static size_t op_case_change(Vis *vis, Text *txt, OperatorContext *c) {
124 size_t len = text_range_size(&c->range);
125 char *buf = malloc(len);
126 if (!buf)
127 return c->pos;
128 len = text_bytes_get(txt, c->range.start, len, buf);
129 size_t rem = len;
130 for (char *cur = buf; rem > 0; cur++, rem--) {
131 int ch = (unsigned char)*cur;
132 if (isascii(ch)) {
133 if (c->arg->i == VIS_OP_CASE_SWAP)
134 *cur = islower(ch) ? toupper(ch) : tolower(ch);
135 else if (c->arg->i == VIS_OP_CASE_UPPER)
136 *cur = toupper(ch);
137 else
138 *cur = tolower(ch);
142 text_delete(txt, c->range.start, len);
143 text_insert(txt, c->range.start, buf, len);
144 free(buf);
145 return c->pos;
148 static size_t op_cursor(Vis *vis, Text *txt, OperatorContext *c) {
149 View *view = vis->win->view;
150 Filerange r = text_range_linewise(txt, &c->range);
151 for (size_t line = text_range_line_first(txt, &r); line != EPOS; line = text_range_line_next(txt, &r, line)) {
152 Cursor *cursor = view_cursors_new(view);
153 if (cursor) {
154 size_t pos;
155 if (c->arg->i == VIS_OP_CURSOR_EOL)
156 pos = text_line_finish(txt, line);
157 else
158 pos = text_line_start(txt, line);
159 view_cursors_to(cursor, pos);
162 return EPOS;
165 static size_t op_join(Vis *vis, Text *txt, OperatorContext *c) {
166 size_t pos = text_line_begin(txt, c->range.end), prev_pos;
168 /* if operator and range are both linewise, skip last line break */
169 if (c->linewise && text_range_is_linewise(txt, &c->range)) {
170 size_t line_prev = text_line_prev(txt, pos);
171 size_t line_prev_prev = text_line_prev(txt, line_prev);
172 if (line_prev_prev >= c->range.start)
173 pos = line_prev;
176 do {
177 prev_pos = pos;
178 size_t end = text_line_start(txt, pos);
179 pos = text_char_next(txt, text_line_finish(txt, text_line_prev(txt, end)));
180 if (pos >= c->range.start && end > pos) {
181 text_delete(txt, pos, end - pos);
182 text_insert(txt, pos, " ", 1);
183 } else {
184 break;
186 } while (pos != prev_pos);
188 return c->range.start;
191 static size_t op_insert(Vis *vis, Text *txt, OperatorContext *c) {
192 macro_operator_record(vis);
193 return c->newpos != EPOS ? c->newpos : c->pos;
196 static size_t op_replace(Vis *vis, Text *txt, OperatorContext *c) {
197 macro_operator_record(vis);
198 return c->newpos != EPOS ? c->newpos : c->pos;
201 Operator ops[] = {
202 [VIS_OP_DELETE] = { op_delete },
203 [VIS_OP_CHANGE] = { op_change },
204 [VIS_OP_YANK] = { op_yank },
205 [VIS_OP_PUT_AFTER] = { op_put },
206 [VIS_OP_SHIFT_RIGHT] = { op_shift_right },
207 [VIS_OP_SHIFT_LEFT] = { op_shift_left },
208 [VIS_OP_CASE_SWAP] = { op_case_change },
209 [VIS_OP_JOIN] = { op_join },
210 [VIS_OP_INSERT] = { op_insert },
211 [VIS_OP_REPLACE] = { op_replace },
212 [VIS_OP_CURSOR_SOL] = { op_cursor },