vis: s/ops/vis_operators/g
[vis.git] / text-objects.c
bloba85d70f214357a06e1c75d444ce3a1be732c42d3
1 /*
2 * Copyright (c) 2014-2015 Marc André Tanner <mat at brain-dump.org>
4 * Permission to use, copy, modify, and/or distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 #include <string.h>
17 #include <ctype.h>
18 #include "text-motions.h"
19 #include "text-objects.h"
20 #include "text-util.h"
21 #include "util.h"
23 #define isboundry is_word_boundry
25 Filerange text_object_entire(Text *txt, size_t pos) {
26 return text_range_new(0, text_size(txt));
29 Filerange text_object_entire_inner(Text *txt, size_t pos) {
30 char c;
31 Filerange r = text_object_entire(txt, pos);
32 Iterator it = text_iterator_get(txt, r.start);
33 while (text_iterator_byte_get(&it, &c) && (c == '\r' || c == '\n'))
34 text_iterator_byte_next(&it, NULL);
35 r.start = it.pos;
36 it = text_iterator_get(txt, r.end);
37 while (text_iterator_byte_prev(&it, &c) && (c == '\r' || c == '\n'));
38 r.end = it.pos;
39 return text_range_linewise(txt, &r);
42 /* TODO: reduce code duplication? */
44 Filerange text_object_longword(Text *txt, size_t pos) {
45 Filerange r;
46 char c, prev = '0', next = '0';
47 Iterator it = text_iterator_get(txt, pos);
48 if (!text_iterator_byte_get(&it, &c))
49 return text_range_empty();
50 if (text_iterator_byte_prev(&it, &prev))
51 text_iterator_byte_next(&it, NULL);
52 text_iterator_byte_next(&it, &next);
53 if (isspace((unsigned char)c)) {
54 /* middle of two words */
55 r.start = text_char_next(txt, text_longword_end_prev(txt, pos));
56 r.end = text_longword_start_next(txt, pos);
57 } else if (isspace((unsigned char)prev) && isspace((unsigned char)next)) {
58 /* on a single character */
59 r.start = pos;
60 r.end = text_char_next(txt, pos);
61 } else if (isspace((unsigned char)prev)) {
62 /* at start of a word */
63 r.start = pos;
64 r.end = text_char_next(txt, text_longword_end_next(txt, pos));
65 } else if (isspace((unsigned char)next)) {
66 /* at end of a word */
67 r.start = text_longword_start_prev(txt, pos);
68 r.end = text_char_next(txt, pos);
69 } else {
70 /* in the middle of a word */
71 r.start = text_longword_start_prev(txt, pos);
72 r.end = text_char_next(txt, text_longword_end_next(txt, pos));
74 return r;
77 Filerange text_object_longword_outer(Text *txt, size_t pos) {
78 Filerange r;
79 char c, prev = '0', next = '0';
80 Iterator it = text_iterator_get(txt, pos);
81 if (!text_iterator_byte_get(&it, &c))
82 return text_range_empty();
83 if (text_iterator_byte_prev(&it, &prev))
84 text_iterator_byte_next(&it, NULL);
85 text_iterator_byte_next(&it, &next);
86 if (isspace((unsigned char)c)) {
87 /* middle of two words, include leading white space */
88 r.start = text_char_next(txt, text_longword_end_prev(txt, pos));
89 r.end = text_char_next(txt, text_longword_end_next(txt, pos));
90 } else if (isspace((unsigned char)prev) && isspace((unsigned char)next)) {
91 /* on a single character */
92 r.start = pos;
93 r.end = text_longword_start_next(txt, pos);
94 } else if (isspace((unsigned char)prev)) {
95 /* at start of a word */
96 r.start = pos;
97 r.end = text_longword_start_next(txt, text_longword_end_next(txt, pos));
98 } else if (isspace((unsigned char)next)) {
99 /* at end of a word */
100 r.start = text_longword_start_prev(txt, pos);
101 r.end = text_longword_start_next(txt, pos);
102 } else {
103 /* in the middle of a word */
104 r.start = text_longword_start_prev(txt, pos);
105 r.end = text_longword_start_next(txt, text_longword_end_next(txt, pos));
107 return r;
110 Filerange text_object_word(Text *txt, size_t pos) {
111 Filerange r;
112 char c, prev = '0', next = '0';
113 Iterator it = text_iterator_get(txt, pos);
114 if (!text_iterator_byte_get(&it, &c))
115 return text_range_empty();
116 if (text_iterator_byte_prev(&it, &prev))
117 text_iterator_byte_next(&it, NULL);
118 text_iterator_byte_next(&it, &next);
119 if (isspace((unsigned char)c)) {
120 r.start = text_char_next(txt, text_word_end_prev(txt, pos));
121 r.end = text_word_start_next(txt, pos);
122 } else if (isboundry(prev) && isboundry(next)) {
123 if (isboundry(c)) {
124 r.start = text_char_next(txt, text_word_end_prev(txt, pos));
125 r.end = text_char_next(txt, text_word_end_next(txt, pos));
126 } else {
127 /* on a single character */
128 r.start = pos;
129 r.end = text_char_next(txt, pos);
131 } else if (isboundry(prev)) {
132 /* at start of a word */
133 r.start = pos;
134 r.end = text_char_next(txt, text_word_end_next(txt, pos));
135 } else if (isboundry(next)) {
136 /* at end of a word */
137 r.start = text_word_start_prev(txt, pos);
138 r.end = text_char_next(txt, pos);
139 } else {
140 /* in the middle of a word */
141 r.start = text_word_start_prev(txt, pos);
142 r.end = text_char_next(txt, text_word_end_next(txt, pos));
145 return r;
148 Filerange text_object_word_outer(Text *txt, size_t pos) {
149 Filerange r;
150 char c, prev = '0', next = '0';
151 Iterator it = text_iterator_get(txt, pos);
152 if (!text_iterator_byte_get(&it, &c))
153 return text_range_empty();
154 if (text_iterator_byte_prev(&it, &prev))
155 text_iterator_byte_next(&it, NULL);
156 text_iterator_byte_next(&it, &next);
157 if (isspace((unsigned char)c)) {
158 /* middle of two words, include leading white space */
159 r.start = text_char_next(txt, text_word_end_prev(txt, pos));
160 r.end = text_word_end_next(txt, pos);
161 } else if (isboundry(prev) && isboundry(next)) {
162 if (isboundry(c)) {
163 r.start = text_char_next(txt, text_word_end_prev(txt, pos));
164 r.end = text_word_start_next(txt, text_word_end_next(txt, pos));
165 } else {
166 /* on a single character */
167 r.start = pos;
168 r.end = text_char_next(txt, pos);
170 } else if (isboundry(prev)) {
171 /* at start of a word */
172 r.start = pos;
173 r.end = text_word_start_next(txt, text_word_end_next(txt, pos));
174 } else if (isboundry(next)) {
175 /* at end of a word */
176 r.start = text_word_start_prev(txt, pos);
177 r.end = text_word_start_next(txt, pos);
178 } else {
179 /* in the middle of a word */
180 r.start = text_word_start_prev(txt, pos);
181 r.end = text_word_start_next(txt, text_word_end_next(txt, pos));
184 return r;
187 Filerange text_object_word_find_next(Text *txt, size_t pos, const char *word) {
188 size_t len = strlen(word);
189 for (;;) {
190 size_t match_pos = text_find_next(txt, pos, word);
191 if (match_pos != pos) {
192 Filerange match_word = text_object_word(txt, match_pos);
193 if (text_range_size(&match_word) == len)
194 return match_word;
195 pos = match_word.end;
196 } else {
197 return text_range_empty();
202 Filerange text_object_word_find_prev(Text *txt, size_t pos, const char *word) {
203 size_t len = strlen(word);
204 for (;;) {
205 size_t match_pos = text_find_prev(txt, pos, word);
206 if (match_pos != pos) {
207 Filerange match_word = text_object_word(txt, match_pos);
208 if (text_range_size(&match_word) == len)
209 return match_word;
210 pos = match_pos;
211 } else {
212 return text_range_empty();
217 Filerange text_object_line(Text *txt, size_t pos) {
218 Filerange r;
219 r.start = text_line_begin(txt, pos);
220 r.end = text_line_next(txt, pos);
221 return r;
224 Filerange text_object_line_inner(Text *txt, size_t pos) {
225 Filerange r = text_object_line(txt, pos);
226 return text_range_inner(txt, &r);
229 Filerange text_object_sentence(Text *txt, size_t pos) {
230 Filerange r;
231 r.start = text_sentence_prev(txt, pos);
232 r.end = text_sentence_next(txt, pos);
233 return r;
236 Filerange text_object_paragraph(Text *txt, size_t pos) {
237 Filerange r;
238 r.start = text_paragraph_prev(txt, pos);
239 r.end = text_paragraph_next(txt, pos);
240 return r;
243 Filerange text_object_function(Text *txt, size_t pos) {
244 size_t a = text_function_start_prev(txt, pos);
245 size_t b = text_function_end_next(txt, pos);
246 if (text_function_end_next(txt, a) == b) {
247 Filerange r = text_range_new(a, b+1);
248 return text_range_linewise(txt, &r);
250 return text_range_empty();
253 Filerange text_object_function_inner(Text *txt, size_t pos) {
254 Filerange r = text_object_function(txt, pos);
255 if (!text_range_valid(&r))
256 return r;
257 size_t b = text_function_end_next(txt, pos);
258 size_t a = text_bracket_match(txt, b);
259 return text_range_new(a+1, b-1);
262 static Filerange text_object_bracket(Text *txt, size_t pos, char type) {
263 char c, open, close;
264 int opened = 1, closed = 1;
265 Filerange r = text_range_empty();
267 switch (type) {
268 case '(': case ')': open = '('; close = ')'; break;
269 case '{': case '}': open = '{'; close = '}'; break;
270 case '[': case ']': open = '['; close = ']'; break;
271 case '<': case '>': open = '<'; close = '>'; break;
272 case '"': open = '"'; close = '"'; break;
273 case '`': open = '`'; close = '`'; break;
274 case '\'': open = '\''; close = '\''; break;
275 default: return r;
278 Iterator it = text_iterator_get(txt, pos);
280 if (open == close && text_iterator_byte_get(&it, &c) && (c == '"' || c == '`' || c == '\'')) {
281 size_t match = text_bracket_match(txt, pos);
282 r.start = MIN(pos, match) + 1;
283 r.end = MAX(pos, match);
284 return r;
287 while (text_iterator_byte_get(&it, &c)) {
288 if (c == open && --opened == 0) {
289 r.start = it.pos + 1;
290 break;
291 } else if (c == close && it.pos != pos) {
292 opened++;
294 text_iterator_byte_prev(&it, NULL);
297 it = text_iterator_get(txt, pos);
298 while (text_iterator_byte_get(&it, &c)) {
299 if (c == close && --closed == 0) {
300 r.end = it.pos;
301 break;
302 } else if (c == open && it.pos != pos) {
303 closed++;
305 text_iterator_byte_next(&it, NULL);
308 if (!text_range_valid(&r))
309 return text_range_empty();
310 return r;
313 Filerange text_object_square_bracket(Text *txt, size_t pos) {
314 return text_object_bracket(txt, pos, ']');
317 Filerange text_object_curly_bracket(Text *txt, size_t pos) {
318 return text_object_bracket(txt, pos, '}');
321 Filerange text_object_angle_bracket(Text *txt, size_t pos) {
322 return text_object_bracket(txt, pos, '>');
325 Filerange text_object_paranthese(Text *txt, size_t pos) {
326 return text_object_bracket(txt, pos, ')');
329 Filerange text_object_quote(Text *txt, size_t pos) {
330 return text_object_bracket(txt, pos, '"');
333 Filerange text_object_single_quote(Text *txt, size_t pos) {
334 return text_object_bracket(txt, pos, '\'');
337 Filerange text_object_backtick(Text *txt, size_t pos) {
338 return text_object_bracket(txt, pos, '`');
341 Filerange text_range_linewise(Text *txt, Filerange *rin) {
342 Filerange rout = *rin;
343 rout.start = text_line_begin(txt, rin->start);
344 if (rin->end != text_line_begin(txt, rin->end))
345 rout.end = text_line_next(txt, rin->end);
346 return rout;
349 bool text_range_is_linewise(Text *txt, Filerange *r) {
350 return text_range_valid(r) &&
351 r->start == text_line_begin(txt, r->start) &&
352 r->end == text_line_begin(txt, r->end);
355 Filerange text_range_inner(Text *txt, Filerange *rin) {
356 char c;
357 Filerange r = *rin;
358 Iterator it = text_iterator_get(txt, rin->start);
359 while (text_iterator_byte_get(&it, &c) && isspace((unsigned char)c))
360 text_iterator_byte_next(&it, NULL);
361 r.start = it.pos;
362 it = text_iterator_get(txt, rin->end);
363 do r.end = it.pos; while (text_iterator_byte_prev(&it, &c) && isspace((unsigned char)c));
364 return r;