2 * Copyright (c) 2014 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.
18 #include "text-motions.h"
19 #include "text-objects.h"
20 #include "text-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
) {
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
);
36 it
= text_iterator_get(txt
, r
.end
);
37 while (text_iterator_byte_prev(&it
, &c
) && (c
== '\r' || c
== '\n'));
39 return text_range_linewise(txt
, &r
);
42 /* TODO: reduce code duplication? */
44 Filerange
text_object_longword(Text
*txt
, size_t pos
) {
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
);
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(prev
) && isspace(next
)) {
58 /* on a single character */
60 r
.end
= text_char_next(txt
, pos
);
61 } else if (isspace(prev
)) {
62 /* at start of a word */
64 r
.end
= text_char_next(txt
, text_longword_end_next(txt
, pos
));
65 } else if (isspace(next
)) {
66 /* at end of a word */
67 r
.start
= text_longword_start_prev(txt
, pos
);
68 r
.end
= text_char_next(txt
, pos
);
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
));
77 Filerange
text_object_longword_outer(Text
*txt
, size_t pos
) {
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
);
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(prev
) && isspace(next
)) {
91 /* on a single character */
93 r
.end
= text_longword_start_next(txt
, pos
);
94 } else if (isspace(prev
)) {
95 /* at start of a word */
97 r
.end
= text_longword_start_next(txt
, text_longword_end_next(txt
, pos
));
98 } else if (isspace(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
);
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
));
110 Filerange
text_object_word(Text
*txt
, size_t pos
) {
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
);
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
)) {
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
));
127 /* on a single character */
129 r
.end
= text_char_next(txt
, pos
);
131 } else if (isboundry(prev
)) {
132 /* at start of a word */
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
);
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
));
148 Filerange
text_object_word_outer(Text
*txt
, size_t pos
) {
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
);
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
)) {
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
));
166 /* on a single character */
168 r
.end
= text_char_next(txt
, pos
);
170 } else if (isboundry(prev
)) {
171 /* at start of a word */
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
);
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
));
187 Filerange
text_object_word_find_next(Text
*txt
, size_t pos
, const char *word
) {
188 size_t len
= strlen(word
);
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
)
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
);
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
)
212 return text_range_empty();
217 Filerange
text_object_line(Text
*txt
, size_t pos
) {
219 r
.start
= text_line_begin(txt
, pos
);
220 r
.end
= text_line_next(txt
, pos
);
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
) {
231 r
.start
= text_sentence_prev(txt
, pos
);
232 r
.end
= text_sentence_next(txt
, pos
);
236 Filerange
text_object_paragraph(Text
*txt
, size_t pos
) {
238 r
.start
= text_paragraph_prev(txt
, pos
);
239 r
.end
= text_paragraph_next(txt
, pos
);
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
))
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
) {
264 int opened
= 1, closed
= 1;
265 Filerange r
= text_range_empty();
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;
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
);
287 while (text_iterator_byte_get(&it
, &c
)) {
288 if (c
== open
&& --opened
== 0) {
289 r
.start
= it
.pos
+ 1;
291 } else if (c
== close
&& it
.pos
!= pos
) {
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) {
302 } else if (c
== open
&& it
.pos
!= pos
) {
305 text_iterator_byte_next(&it
, NULL
);
308 if (!text_range_valid(&r
))
309 return text_range_empty();
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
);
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
) {
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
);
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
));