5 #include "text-motions.h"
6 #include "text-objects.h"
10 #define blank(c) ((c) == ' ' || (c) == '\t')
11 #define space(c) (isspace((unsigned char)c))
12 #define boundary(c) (isboundary((unsigned char)c))
14 Filerange
text_object_entire(Text
*txt
, size_t pos
) {
15 return text_range_new(0, text_size(txt
));
18 Filerange
text_object_entire_inner(Text
*txt
, size_t pos
) {
20 Filerange r
= text_object_entire(txt
, pos
);
21 Iterator it
= text_iterator_get(txt
, r
.start
);
22 if (text_iterator_byte_get(&it
, &c
) && c
== '\n')
23 while (text_iterator_byte_next(&it
, &c
) && c
== '\n');
25 it
= text_iterator_get(txt
, r
.end
);
26 while (text_iterator_char_prev(&it
, &c
) && c
== '\n');
28 return text_range_linewise(txt
, &r
);
31 static Filerange
text_object_customword(Text
*txt
, size_t pos
, int (*isboundary
)(int)) {
33 char c
, prev
= '0', next
= '0';
34 Iterator it
= text_iterator_get(txt
, pos
);
35 if (!text_iterator_byte_get(&it
, &c
))
36 return text_range_empty();
37 if (pos
> 0 && text_iterator_byte_prev(&it
, &prev
))
38 text_iterator_byte_next(&it
, NULL
);
39 text_iterator_byte_next(&it
, &next
);
41 r
.start
= text_char_next(txt
, text_customword_end_prev(txt
, pos
, isboundary
));
42 r
.end
= text_customword_start_next(txt
, pos
, isboundary
);
43 } else if (boundary(prev
) && boundary(next
)) {
45 r
.start
= text_char_next(txt
, text_customword_end_prev(txt
, pos
, isboundary
));
46 r
.end
= text_char_next(txt
, text_customword_end_next(txt
, pos
, isboundary
));
48 /* on a single character */
50 r
.end
= text_char_next(txt
, pos
);
52 } else if (boundary(prev
)) {
53 /* at start of a word */
55 r
.end
= text_char_next(txt
, text_customword_end_next(txt
, pos
, isboundary
));
56 } else if (boundary(next
)) {
57 /* at end of a word */
58 r
.start
= text_customword_start_prev(txt
, pos
, isboundary
);
59 r
.end
= text_char_next(txt
, pos
);
61 /* in the middle of a word */
62 r
.start
= text_customword_start_prev(txt
, pos
, isboundary
);
63 r
.end
= text_char_next(txt
, text_customword_end_next(txt
, pos
, isboundary
));
69 Filerange
text_object_word(Text
*txt
, size_t pos
) {
70 return text_object_customword(txt
, pos
, is_word_boundary
);
73 Filerange
text_object_longword(Text
*txt
, size_t pos
) {
74 return text_object_customword(txt
, pos
, isspace
);
77 static Filerange
text_object_customword_outer(Text
*txt
, size_t pos
, int (*isboundary
)(int)) {
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 (pos
> 0 && 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_customword_end_prev(txt
, pos
, isboundary
));
89 r
.end
= text_char_next(txt
, text_customword_end_next(txt
, pos
, isboundary
));
90 } else if (boundary(prev
) && boundary(next
)) {
92 r
.start
= text_char_next(txt
, text_customword_end_prev(txt
, pos
, isboundary
));
93 r
.end
= text_word_start_next(txt
, text_customword_end_next(txt
, pos
, isboundary
));
95 /* on a single character */
97 r
.end
= text_customword_start_next(txt
, pos
, isboundary
);
99 } else if (boundary(prev
)) {
100 /* at start of a word */
102 r
.end
= text_customword_start_next(txt
, text_customword_end_next(txt
, pos
, isboundary
), isboundary
);
103 } else if (boundary(next
)) {
104 /* at end of a word */
105 r
.start
= text_customword_start_prev(txt
, pos
, isboundary
);
106 r
.end
= text_customword_start_next(txt
, pos
, isboundary
);
108 /* in the middle of a word */
109 r
.start
= text_customword_start_prev(txt
, pos
, isboundary
);
110 r
.end
= text_customword_start_next(txt
, text_customword_end_next(txt
, pos
, isboundary
), isboundary
);
116 Filerange
text_object_longword_outer(Text
*txt
, size_t pos
) {
117 return text_object_customword_outer(txt
, pos
, isspace
);
120 Filerange
text_object_word_outer(Text
*txt
, size_t pos
) {
121 return text_object_customword_outer(txt
, pos
, is_word_boundary
);
124 Filerange
text_object_word_find_next(Text
*txt
, size_t pos
, const char *word
) {
125 size_t len
= strlen(word
);
127 size_t match_pos
= text_find_next(txt
, pos
, word
);
128 if (match_pos
!= pos
) {
129 Filerange match_word
= text_object_word(txt
, match_pos
);
130 if (text_range_size(&match_word
) == len
)
132 pos
= match_word
.end
;
134 return text_range_empty();
139 Filerange
text_object_word_find_prev(Text
*txt
, size_t pos
, const char *word
) {
140 size_t len
= strlen(word
);
142 size_t match_pos
= text_find_prev(txt
, pos
, word
);
143 if (match_pos
!= pos
) {
144 Filerange match_word
= text_object_word(txt
, match_pos
);
145 if (text_range_size(&match_word
) == len
)
149 return text_range_empty();
154 Filerange
text_object_line(Text
*txt
, size_t pos
) {
156 r
.start
= text_line_begin(txt
, pos
);
157 r
.end
= text_line_next(txt
, pos
);
161 Filerange
text_object_line_inner(Text
*txt
, size_t pos
) {
162 Filerange r
= text_object_line(txt
, pos
);
163 return text_range_inner(txt
, &r
);
166 Filerange
text_object_sentence(Text
*txt
, size_t pos
) {
168 r
.start
= text_sentence_prev(txt
, pos
);
169 r
.end
= text_sentence_next(txt
, pos
);
173 static bool text_line_blank(Text
*txt
, size_t pos
) {
176 Iterator it
= text_iterator_get(txt
, text_line_begin(txt
, pos
));
177 while (text_iterator_byte_get(&it
, &c
) && c
!= '\n' && (b
= blank(c
)))
178 text_iterator_char_next(&it
, NULL
);
182 Filerange
text_object_paragraph(Text
*txt
, size_t pos
) {
185 if (text_line_blank(txt
, pos
)) {
186 Iterator it
= text_iterator_get(txt
, pos
), rit
= it
;
187 while (text_iterator_byte_get(&rit
, &c
) && (c
== '\n' || blank(c
)))
188 text_iterator_byte_prev(&rit
, NULL
);
189 if (c
== '\n' || blank(c
))
192 r
.start
= text_line_next(txt
, rit
.pos
);
193 while (text_iterator_byte_get(&it
, &c
) && (c
== '\n' || blank(c
)))
194 text_iterator_byte_next(&it
, NULL
);
195 if (it
.pos
== text_size(txt
))
198 r
.end
= text_line_begin(txt
, it
.pos
);
200 r
.start
= text_line_blank_prev(txt
, pos
);
201 if (r
.start
> 0 || (text_byte_get(txt
, r
.start
, &c
) && c
== '\n'))
202 r
.start
= text_line_next(txt
, r
.start
);
203 r
.end
= text_line_blank_next(txt
, pos
);
208 Filerange
text_object_paragraph_outer(Text
*txt
, size_t pos
) {
209 Filerange p1
= text_object_paragraph(txt
, pos
);
210 Filerange p2
= text_object_paragraph(txt
, p1
.end
);
211 return text_range_union(&p1
, &p2
);
214 static Filerange
text_object_bracket(Text
*txt
, size_t pos
, char type
) {
216 int opened
= 1, closed
= 1;
217 Filerange r
= text_range_empty();
220 case '(': case ')': open
= '('; close
= ')'; break;
221 case '{': case '}': open
= '{'; close
= '}'; break;
222 case '[': case ']': open
= '['; close
= ']'; break;
223 case '<': case '>': open
= '<'; close
= '>'; break;
224 case '"': open
= '"'; close
= '"'; break;
225 case '`': open
= '`'; close
= '`'; break;
226 case '\'': open
= '\''; close
= '\''; break;
230 Iterator it
= text_iterator_get(txt
, pos
);
232 if (open
== close
&& text_iterator_byte_get(&it
, &c
) && (c
== '"' || c
== '`' || c
== '\'')) {
233 size_t match
= text_bracket_match(txt
, pos
, NULL
);
234 r
.start
= MIN(pos
, match
) + 1;
235 r
.end
= MAX(pos
, match
);
239 while (text_iterator_byte_get(&it
, &c
)) {
240 if (c
== open
&& --opened
== 0) {
241 r
.start
= it
.pos
+ 1;
243 } else if (c
== close
&& it
.pos
!= pos
) {
246 text_iterator_byte_prev(&it
, NULL
);
249 it
= text_iterator_get(txt
, pos
);
250 while (text_iterator_byte_get(&it
, &c
)) {
251 if (c
== close
&& --closed
== 0) {
254 } else if (c
== open
&& it
.pos
!= pos
) {
257 text_iterator_byte_next(&it
, NULL
);
260 if (!text_range_valid(&r
))
261 return text_range_empty();
265 Filerange
text_object_square_bracket(Text
*txt
, size_t pos
) {
266 return text_object_bracket(txt
, pos
, ']');
269 Filerange
text_object_curly_bracket(Text
*txt
, size_t pos
) {
270 return text_object_bracket(txt
, pos
, '}');
273 Filerange
text_object_angle_bracket(Text
*txt
, size_t pos
) {
274 return text_object_bracket(txt
, pos
, '>');
277 Filerange
text_object_parenthesis(Text
*txt
, size_t pos
) {
278 return text_object_bracket(txt
, pos
, ')');
281 Filerange
text_object_quote(Text
*txt
, size_t pos
) {
282 return text_object_bracket(txt
, pos
, '"');
285 Filerange
text_object_single_quote(Text
*txt
, size_t pos
) {
286 return text_object_bracket(txt
, pos
, '\'');
289 Filerange
text_object_backtick(Text
*txt
, size_t pos
) {
290 return text_object_bracket(txt
, pos
, '`');
293 Filerange
text_object_search_forward(Text
*txt
, size_t pos
, Regex
*regex
) {
295 size_t end
= text_size(txt
);
297 bool found
= start
< end
&& !text_search_range_forward(txt
, start
, end
- start
, regex
, 1, match
, 0);
299 return text_range_new(match
[0].start
, match
[0].end
);
300 return text_range_empty();
303 Filerange
text_object_search_backward(Text
*txt
, size_t pos
, Regex
*regex
) {
307 bool found
= !text_search_range_backward(txt
, start
, end
, regex
, 1, match
, 0);
309 return text_range_new(match
[0].start
, match
[0].end
);
310 return text_range_empty();
313 Filerange
text_object_indentation(Text
*txt
, size_t pos
) {
315 size_t bol
= text_line_begin(txt
, pos
);
316 size_t sol
= text_line_start(txt
, bol
);
318 size_t end
= text_line_next(txt
, bol
);
319 size_t line_indent
= sol
- bol
;
320 bool line_empty
= text_byte_get(txt
, bol
, &c
) && c
== '\n';
322 char *buf
= text_bytes_alloc0(txt
, bol
, line_indent
);
323 char *tmp
= malloc(line_indent
);
328 return text_range_empty();
331 while ((bol
= text_line_begin(txt
, text_line_prev(txt
, start
))) != start
) {
332 sol
= text_line_start(txt
, bol
);
333 size_t indent
= sol
- bol
;
334 if (indent
< line_indent
)
336 bool empty
= text_byte_get(txt
, bol
, &c
) && c
== '\n';
337 if (line_empty
&& !empty
)
339 if (line_indent
== 0 && empty
)
341 text_bytes_get(txt
, bol
, line_indent
, tmp
);
342 if (memcmp(buf
, tmp
, line_indent
))
349 sol
= text_line_start(txt
, bol
);
350 size_t indent
= sol
- bol
;
351 if (indent
< line_indent
)
353 bool empty
= text_byte_get(txt
, bol
, &c
) && c
== '\n';
354 if (line_empty
&& !empty
)
356 if (line_indent
== 0 && empty
)
358 text_bytes_get(txt
, bol
, line_indent
, tmp
);
359 if (memcmp(buf
, tmp
, line_indent
))
361 end
= text_line_next(txt
, bol
);
362 } while (bol
!= end
);
366 return text_range_new(start
, end
);
369 Filerange
text_range_linewise(Text
*txt
, Filerange
*rin
) {
370 Filerange rout
= *rin
;
371 rout
.start
= text_line_begin(txt
, rin
->start
);
372 if (rin
->end
!= text_line_begin(txt
, rin
->end
))
373 rout
.end
= text_line_next(txt
, rin
->end
);
377 bool text_range_is_linewise(Text
*txt
, Filerange
*r
) {
378 return text_range_size(r
) > 0 &&
379 r
->start
== text_line_begin(txt
, r
->start
) &&
380 r
->end
== text_line_begin(txt
, r
->end
);
383 Filerange
text_range_inner(Text
*txt
, Filerange
*rin
) {
386 Iterator it
= text_iterator_get(txt
, rin
->start
);
387 while (text_iterator_byte_get(&it
, &c
) && space(c
))
388 text_iterator_byte_next(&it
, NULL
);
390 it
= text_iterator_get(txt
, rin
->end
);
391 do r
.end
= it
.pos
; while (text_iterator_byte_prev(&it
, &c
) && space(c
));