Add explicit build commands to README
[vis.git] / vis-operators.c
blob7148b8cf42ee88034578911e17780157457bb87d
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 bool sel = text_range_size(&c->range) > 0;
34 bool sel_linewise = sel && text_range_is_linewise(txt, &c->range);
35 if (sel) {
36 text_delete_range(txt, &c->range);
37 pos = c->pos = c->range.start;
39 switch (c->arg->i) {
40 case VIS_OP_PUT_AFTER:
41 case VIS_OP_PUT_AFTER_END:
42 if (c->reg->linewise && !sel_linewise)
43 pos = text_line_next(txt, pos);
44 else if (!sel)
45 pos = text_char_next(txt, pos);
46 break;
47 case VIS_OP_PUT_BEFORE:
48 case VIS_OP_PUT_BEFORE_END:
49 if (c->reg->linewise)
50 pos = text_line_begin(txt, pos);
51 break;
54 for (int i = 0; i < c->count; i++) {
55 text_insert(txt, pos, c->reg->data, c->reg->len);
56 pos += c->reg->len;
59 if (c->reg->linewise) {
60 switch (c->arg->i) {
61 case VIS_OP_PUT_BEFORE_END:
62 case VIS_OP_PUT_AFTER_END:
63 pos = text_line_start(txt, pos);
64 break;
65 case VIS_OP_PUT_AFTER:
66 pos = text_line_start(txt, text_line_next(txt, c->pos));
67 break;
68 case VIS_OP_PUT_BEFORE:
69 pos = text_line_start(txt, c->pos);
70 break;
72 } else {
73 switch (c->arg->i) {
74 case VIS_OP_PUT_AFTER:
75 case VIS_OP_PUT_BEFORE:
76 pos = text_char_prev(txt, pos);
77 break;
81 return pos;
84 static size_t op_shift_right(Vis *vis, Text *txt, OperatorContext *c) {
85 size_t pos = text_line_begin(txt, c->range.end), prev_pos;
86 const char *tab = expandtab(vis);
87 size_t tablen = strlen(tab);
89 /* if range ends at the begin of a line, skip line break */
90 if (pos == c->range.end)
91 pos = text_line_prev(txt, pos);
93 do {
94 prev_pos = pos = text_line_begin(txt, pos);
95 text_insert(txt, pos, tab, tablen);
96 pos = text_line_prev(txt, pos);
97 } while (pos >= c->range.start && pos != prev_pos);
99 return c->pos + tablen;
102 static size_t op_shift_left(Vis *vis, Text *txt, OperatorContext *c) {
103 size_t pos = text_line_begin(txt, c->range.end), prev_pos;
104 size_t tabwidth = vis->tabwidth, tablen;
106 /* if range ends at the begin of a line, skip line break */
107 if (pos == c->range.end)
108 pos = text_line_prev(txt, pos);
110 do {
111 char c;
112 size_t len = 0;
113 prev_pos = pos = text_line_begin(txt, pos);
114 Iterator it = text_iterator_get(txt, pos);
115 if (text_iterator_byte_get(&it, &c) && c == '\t') {
116 len = 1;
117 } else {
118 for (len = 0; text_iterator_byte_get(&it, &c) && c == ' '; len++)
119 text_iterator_byte_next(&it, NULL);
121 tablen = MIN(len, tabwidth);
122 text_delete(txt, pos, tablen);
123 pos = text_line_prev(txt, pos);
124 } while (pos >= c->range.start && pos != prev_pos);
126 return c->pos - tablen;
129 static size_t op_case_change(Vis *vis, Text *txt, OperatorContext *c) {
130 size_t len = text_range_size(&c->range);
131 char *buf = malloc(len);
132 if (!buf)
133 return c->pos;
134 len = text_bytes_get(txt, c->range.start, len, buf);
135 size_t rem = len;
136 for (char *cur = buf; rem > 0; cur++, rem--) {
137 int ch = (unsigned char)*cur;
138 if (isascii(ch)) {
139 if (c->arg->i == VIS_OP_CASE_SWAP)
140 *cur = islower(ch) ? toupper(ch) : tolower(ch);
141 else if (c->arg->i == VIS_OP_CASE_UPPER)
142 *cur = toupper(ch);
143 else
144 *cur = tolower(ch);
148 text_delete(txt, c->range.start, len);
149 text_insert(txt, c->range.start, buf, len);
150 free(buf);
151 return c->pos;
154 static size_t op_cursor(Vis *vis, Text *txt, OperatorContext *c) {
155 View *view = vis->win->view;
156 Filerange r = text_range_linewise(txt, &c->range);
157 for (size_t line = text_range_line_first(txt, &r); line != EPOS; line = text_range_line_next(txt, &r, line)) {
158 Cursor *cursor = view_cursors_new(view);
159 if (cursor) {
160 size_t pos;
161 if (c->arg->i == VIS_OP_CURSOR_EOL)
162 pos = text_line_finish(txt, line);
163 else
164 pos = text_line_start(txt, line);
165 view_cursors_to(cursor, pos);
168 return EPOS;
171 static size_t op_join(Vis *vis, Text *txt, OperatorContext *c) {
172 size_t pos = text_line_begin(txt, c->range.end), prev_pos;
174 /* if operator and range are both linewise, skip last line break */
175 if (c->linewise && text_range_is_linewise(txt, &c->range)) {
176 size_t line_prev = text_line_prev(txt, pos);
177 size_t line_prev_prev = text_line_prev(txt, line_prev);
178 if (line_prev_prev >= c->range.start)
179 pos = line_prev;
182 do {
183 prev_pos = pos;
184 size_t end = text_line_start(txt, pos);
185 pos = text_char_next(txt, text_line_finish(txt, text_line_prev(txt, end)));
186 if (pos >= c->range.start && end > pos) {
187 text_delete(txt, pos, end - pos);
188 text_insert(txt, pos, " ", 1);
189 } else {
190 break;
192 } while (pos != prev_pos);
194 return c->range.start;
197 static size_t op_insert(Vis *vis, Text *txt, OperatorContext *c) {
198 macro_operator_record(vis);
199 return c->newpos != EPOS ? c->newpos : c->pos;
202 static size_t op_replace(Vis *vis, Text *txt, OperatorContext *c) {
203 macro_operator_record(vis);
204 return c->newpos != EPOS ? c->newpos : c->pos;
207 static size_t op_filter(Vis *vis, Text *txt, OperatorContext *c) {
208 if (!c->arg->s)
209 macro_operator_record(vis);
210 return text_size(txt) + 1; /* do not change cursor position, would destroy selection */
213 Operator ops[] = {
214 [VIS_OP_DELETE] = { op_delete },
215 [VIS_OP_CHANGE] = { op_change },
216 [VIS_OP_YANK] = { op_yank },
217 [VIS_OP_PUT_AFTER] = { op_put },
218 [VIS_OP_SHIFT_RIGHT] = { op_shift_right },
219 [VIS_OP_SHIFT_LEFT] = { op_shift_left },
220 [VIS_OP_CASE_SWAP] = { op_case_change },
221 [VIS_OP_JOIN] = { op_join },
222 [VIS_OP_INSERT] = { op_insert },
223 [VIS_OP_REPLACE] = { op_replace },
224 [VIS_OP_CURSOR_SOL] = { op_cursor },
225 [VIS_OP_FILTER] = { op_filter },