5 #include "text-motions.h"
6 #include "text-objects.h"
10 static Regex
*search_word(Vis
*vis
, Text
*txt
, size_t pos
) {
12 Filerange word
= text_object_word(txt
, pos
);
13 if (!text_range_valid(&word
))
15 char *buf
= text_bytes_alloc0(txt
, word
.start
, text_range_size(&word
));
18 snprintf(expr
, sizeof(expr
), "[[:<:]]%s[[:>:]]", buf
);
19 Regex
*regex
= vis_regex(vis
, expr
);
21 snprintf(expr
, sizeof(expr
), "\\<%s\\>", buf
);
22 regex
= vis_regex(vis
, expr
);
28 static size_t search_word_forward(Vis
*vis
, Text
*txt
, size_t pos
) {
29 Regex
*regex
= search_word(vis
, txt
, pos
);
31 vis
->search_direction
= VIS_MOVE_SEARCH_REPEAT_FORWARD
;
32 pos
= text_search_forward(txt
, pos
, regex
);
34 text_regex_free(regex
);
38 static size_t search_word_backward(Vis
*vis
, Text
*txt
, size_t pos
) {
39 Regex
*regex
= search_word(vis
, txt
, pos
);
41 vis
->search_direction
= VIS_MOVE_SEARCH_REPEAT_BACKWARD
;
42 pos
= text_search_backward(txt
, pos
, regex
);
44 text_regex_free(regex
);
48 static size_t search_forward(Vis
*vis
, Text
*txt
, size_t pos
) {
49 Regex
*regex
= vis_regex(vis
, NULL
);
51 pos
= text_search_forward(txt
, pos
, regex
);
52 text_regex_free(regex
);
56 static size_t search_backward(Vis
*vis
, Text
*txt
, size_t pos
) {
57 Regex
*regex
= vis_regex(vis
, NULL
);
59 pos
= text_search_backward(txt
, pos
, regex
);
60 text_regex_free(regex
);
64 static size_t common_word_next(Vis
*vis
, Text
*txt
, size_t pos
,
65 enum VisMotion start_next
, enum VisMotion end_next
,
66 int (*isboundary
)(int)) {
68 Iterator it
= text_iterator_get(txt
, pos
);
69 if (!text_iterator_byte_get(&it
, &c
))
71 const Movement
*motion
= NULL
;
72 int count
= vis_count_get_default(vis
, 1);
73 if (isspace((unsigned char)c
)) {
74 motion
= &vis_motions
[start_next
];
75 } else if (!isboundary((unsigned char)c
) && text_iterator_char_next(&it
, &c
) && isboundary((unsigned char)c
)) {
76 /* we are on the last character of a word */
78 /* map `cw` to `cl` */
79 motion
= &vis_motions
[VIS_MOVE_CHAR_NEXT
];
81 /* map `c{n}w` to `c{n-1}e` */
83 motion
= &vis_motions
[end_next
];
86 /* map `cw` to `ce` */
87 motion
= &vis_motions
[end_next
];
93 size_t newpos
= motion
->txt(txt
, pos
);
99 if (motion
->type
& INCLUSIVE
)
100 pos
= text_char_next(txt
, pos
);
105 static size_t word_next(Vis
*vis
, Text
*txt
, size_t pos
) {
106 return common_word_next(vis
, txt
, pos
, VIS_MOVE_WORD_START_NEXT
,
107 VIS_MOVE_WORD_END_NEXT
, is_word_boundary
);
110 static size_t longword_next(Vis
*vis
, Text
*txt
, size_t pos
) {
111 return common_word_next(vis
, txt
, pos
, VIS_MOVE_LONGWORD_START_NEXT
,
112 VIS_MOVE_LONGWORD_END_NEXT
, isspace
);
115 static size_t to(Vis
*vis
, Text
*txt
, size_t pos
) {
117 if (pos
== text_line_end(txt
, pos
))
119 size_t hit
= text_line_find_next(txt
, pos
+1, vis
->search_char
);
120 if (!text_byte_get(txt
, hit
, &c
) || c
!= vis
->search_char
[0])
125 static size_t till(Vis
*vis
, Text
*txt
, size_t pos
) {
126 size_t hit
= to(vis
, txt
, pos
+1);
127 if (pos
== text_line_end(txt
, pos
))
130 return text_char_prev(txt
, hit
);
134 static size_t to_left(Vis
*vis
, Text
*txt
, size_t pos
) {
135 return text_line_find_prev(txt
, pos
, vis
->search_char
);
138 static size_t till_left(Vis
*vis
, Text
*txt
, size_t pos
) {
139 if (pos
== text_line_begin(txt
, pos
))
141 size_t hit
= to_left(vis
, txt
, pos
-1);
143 return text_char_next(txt
, hit
);
147 static size_t firstline(Text
*txt
, size_t pos
) {
148 return text_line_start(txt
, 0);
151 static size_t line(Vis
*vis
, Text
*txt
, size_t pos
) {
152 int count
= vis_count_get_default(vis
, 1);
153 return text_line_start(txt
, text_pos_by_lineno(txt
, count
));
156 static size_t lastline(Text
*txt
, size_t pos
) {
157 pos
= text_size(txt
);
158 return text_line_start(txt
, pos
> 0 ? pos
-1 : pos
);
161 static size_t column(Vis
*vis
, Text
*txt
, size_t pos
) {
162 return text_line_offset(txt
, pos
, vis_count_get_default(vis
, 0));
165 static size_t view_lines_top(Vis
*vis
, View
*view
) {
166 return view_screenline_goto(view
, vis_count_get_default(vis
, 1));
169 static size_t view_lines_middle(Vis
*vis
, View
*view
) {
170 int h
= view_height_get(view
);
171 return view_screenline_goto(view
, h
/2);
174 static size_t view_lines_bottom(Vis
*vis
, View
*view
) {
175 int h
= view_height_get(vis
->win
->view
);
176 return view_screenline_goto(vis
->win
->view
, h
- vis_count_get_default(vis
, 0));
179 static size_t window_nop(Vis
*vis
, Win
*win
, size_t pos
) {
183 static size_t bracket_match(Text
*txt
, size_t pos
) {
184 size_t hit
= text_bracket_match_symbol(txt
, pos
, "(){}[]<>");
188 Iterator it
= text_iterator_get(txt
, pos
);
189 while (text_iterator_byte_get(&it
, ¤t
)) {
201 text_iterator_byte_next(&it
, NULL
);
206 static size_t percent(Vis
*vis
, Text
*txt
, size_t pos
) {
207 int ratio
= vis_count_get_default(vis
, 0);
210 return text_size(txt
) * ratio
/ 100;
213 static size_t byte(Vis
*vis
, Text
*txt
, size_t pos
) {
214 pos
= vis_count_get_default(vis
, 0);
215 size_t max
= text_size(txt
);
216 return pos
<= max
? pos
: max
;
219 static size_t byte_left(Vis
*vis
, Text
*txt
, size_t pos
) {
220 size_t off
= vis_count_get_default(vis
, 1);
221 return off
<= pos
? pos
-off
: 0;
224 static size_t byte_right(Vis
*vis
, Text
*txt
, size_t pos
) {
225 size_t off
= vis_count_get_default(vis
, 1);
226 size_t new = pos
+ off
;
227 size_t max
= text_size(txt
);
228 return new <= max
&& new > pos
? new : max
;
231 void vis_motion_type(Vis
*vis
, enum VisMotionType type
) {
232 vis
->action
.type
= type
;
235 int vis_motion_register(Vis
*vis
, enum VisMotionType type
, void *data
, VisMotionFunction
*motion
) {
237 Movement
*move
= calloc(1, sizeof *move
);
245 if (array_add_ptr(&vis
->motions
, move
))
246 return VIS_MOVE_LAST
+ array_length(&vis
->motions
) - 1;
251 bool vis_motion(Vis
*vis
, enum VisMotion motion
, ...) {
253 va_start(ap
, motion
);
256 case VIS_MOVE_WORD_START_NEXT
:
257 if (vis
->action
.op
== &vis_operators
[VIS_OP_CHANGE
])
258 motion
= VIS_MOVE_WORD_NEXT
;
260 case VIS_MOVE_LONGWORD_START_NEXT
:
261 if (vis
->action
.op
== &vis_operators
[VIS_OP_CHANGE
])
262 motion
= VIS_MOVE_LONGWORD_NEXT
;
264 case VIS_MOVE_SEARCH_FORWARD
:
265 case VIS_MOVE_SEARCH_BACKWARD
:
267 const char *pattern
= va_arg(ap
, char*);
268 Regex
*regex
= vis_regex(vis
, pattern
);
273 text_regex_free(regex
);
274 if (motion
== VIS_MOVE_SEARCH_FORWARD
)
275 motion
= VIS_MOVE_SEARCH_REPEAT_FORWARD
;
277 motion
= VIS_MOVE_SEARCH_REPEAT_BACKWARD
;
278 vis
->search_direction
= motion
;
281 case VIS_MOVE_SEARCH_REPEAT
:
282 case VIS_MOVE_SEARCH_REPEAT_REVERSE
:
284 if (!vis
->search_direction
)
285 vis
->search_direction
= VIS_MOVE_SEARCH_REPEAT_FORWARD
;
286 if (motion
== VIS_MOVE_SEARCH_REPEAT
) {
287 motion
= vis
->search_direction
;
289 motion
= vis
->search_direction
== VIS_MOVE_SEARCH_REPEAT_FORWARD
?
290 VIS_MOVE_SEARCH_REPEAT_BACKWARD
:
291 VIS_MOVE_SEARCH_REPEAT_FORWARD
;
295 case VIS_MOVE_RIGHT_TO
:
296 case VIS_MOVE_LEFT_TO
:
297 case VIS_MOVE_RIGHT_TILL
:
298 case VIS_MOVE_LEFT_TILL
:
300 const char *key
= va_arg(ap
, char*);
303 strncpy(vis
->search_char
, key
, sizeof(vis
->search_char
));
304 vis
->search_char
[sizeof(vis
->search_char
)-1] = '\0';
305 vis
->last_totill
= motion
;
308 case VIS_MOVE_TOTILL_REPEAT
:
309 if (!vis
->last_totill
)
311 motion
= vis
->last_totill
;
313 case VIS_MOVE_TOTILL_REVERSE
:
314 switch (vis
->last_totill
) {
315 case VIS_MOVE_RIGHT_TO
:
316 motion
= VIS_MOVE_LEFT_TO
;
318 case VIS_MOVE_LEFT_TO
:
319 motion
= VIS_MOVE_RIGHT_TO
;
321 case VIS_MOVE_RIGHT_TILL
:
322 motion
= VIS_MOVE_LEFT_TILL
;
324 case VIS_MOVE_LEFT_TILL
:
325 motion
= VIS_MOVE_RIGHT_TILL
;
335 if (motion
< LENGTH(vis_motions
))
336 vis
->action
.movement
= &vis_motions
[motion
];
338 vis
->action
.movement
= array_get_ptr(&vis
->motions
, motion
- VIS_MOVE_LAST
);
340 if (!vis
->action
.movement
)
351 const Movement vis_motions
[] = {
352 [VIS_MOVE_LINE_UP
] = {
354 .type
= LINEWISE
|LINEWISE_INCLUSIVE
,
356 [VIS_MOVE_LINE_DOWN
] = {
357 .cur
= view_line_down
,
358 .type
= LINEWISE
|LINEWISE_INCLUSIVE
,
360 [VIS_MOVE_SCREEN_LINE_UP
] = {
361 .cur
= view_screenline_up
,
363 [VIS_MOVE_SCREEN_LINE_DOWN
] = {
364 .cur
= view_screenline_down
,
366 [VIS_MOVE_SCREEN_LINE_BEGIN
] = {
367 .cur
= view_screenline_begin
,
370 [VIS_MOVE_SCREEN_LINE_MIDDLE
] = {
371 .cur
= view_screenline_middle
,
374 [VIS_MOVE_SCREEN_LINE_END
] = {
375 .cur
= view_screenline_end
,
376 .type
= CHARWISE
|INCLUSIVE
,
378 [VIS_MOVE_LINE_PREV
] = {
379 .txt
= text_line_prev
,
381 [VIS_MOVE_LINE_BEGIN
] = {
382 .txt
= text_line_begin
,
385 [VIS_MOVE_LINE_START
] = {
386 .txt
= text_line_start
,
389 [VIS_MOVE_LINE_FINISH
] = {
390 .txt
= text_line_finish
,
391 .type
= INCLUSIVE
|IDEMPOTENT
,
393 [VIS_MOVE_LINE_END
] = {
394 .txt
= text_line_end
,
397 [VIS_MOVE_LINE_NEXT
] = {
398 .txt
= text_line_next
,
402 .type
= LINEWISE
|IDEMPOTENT
|JUMP
,
404 [VIS_MOVE_COLUMN
] = {
406 .type
= CHARWISE
|IDEMPOTENT
,
408 [VIS_MOVE_CHAR_PREV
] = {
409 .txt
= text_char_prev
,
412 [VIS_MOVE_CHAR_NEXT
] = {
413 .txt
= text_char_next
,
416 [VIS_MOVE_LINE_CHAR_PREV
] = {
417 .txt
= text_line_char_prev
,
420 [VIS_MOVE_LINE_CHAR_NEXT
] = {
421 .txt
= text_line_char_next
,
424 [VIS_MOVE_CODEPOINT_PREV
] = {
425 .txt
= text_codepoint_prev
,
428 [VIS_MOVE_CODEPOINT_NEXT
] = {
429 .txt
= text_codepoint_next
,
432 [VIS_MOVE_WORD_NEXT
] = {
434 .type
= CHARWISE
|IDEMPOTENT
,
436 [VIS_MOVE_WORD_START_PREV
] = {
437 .txt
= text_word_start_prev
,
440 [VIS_MOVE_WORD_START_NEXT
] = {
441 .txt
= text_word_start_next
,
444 [VIS_MOVE_WORD_END_PREV
] = {
445 .txt
= text_word_end_prev
,
446 .type
= CHARWISE
|INCLUSIVE
,
448 [VIS_MOVE_WORD_END_NEXT
] = {
449 .txt
= text_word_end_next
,
450 .type
= CHARWISE
|INCLUSIVE
,
452 [VIS_MOVE_LONGWORD_NEXT
] = {
453 .vis
= longword_next
,
454 .type
= CHARWISE
|IDEMPOTENT
,
456 [VIS_MOVE_LONGWORD_START_PREV
] = {
457 .txt
= text_longword_start_prev
,
460 [VIS_MOVE_LONGWORD_START_NEXT
] = {
461 .txt
= text_longword_start_next
,
464 [VIS_MOVE_LONGWORD_END_PREV
] = {
465 .txt
= text_longword_end_prev
,
466 .type
= CHARWISE
|INCLUSIVE
,
468 [VIS_MOVE_LONGWORD_END_NEXT
] = {
469 .txt
= text_longword_end_next
,
470 .type
= CHARWISE
|INCLUSIVE
,
472 [VIS_MOVE_SENTENCE_PREV
] = {
473 .txt
= text_sentence_prev
,
476 [VIS_MOVE_SENTENCE_NEXT
] = {
477 .txt
= text_sentence_next
,
480 [VIS_MOVE_PARAGRAPH_PREV
] = {
481 .txt
= text_paragraph_prev
,
482 .type
= LINEWISE
|JUMP
,
484 [VIS_MOVE_PARAGRAPH_NEXT
] = {
485 .txt
= text_paragraph_next
,
486 .type
= LINEWISE
|JUMP
,
488 [VIS_MOVE_BLOCK_START
] = {
489 .txt
= text_block_start
,
492 [VIS_MOVE_BLOCK_END
] = {
493 .txt
= text_block_end
,
496 [VIS_MOVE_PARENTHESE_START
] = {
497 .txt
= text_parenthese_start
,
500 [VIS_MOVE_PARENTHESE_END
] = {
501 .txt
= text_parenthese_end
,
504 [VIS_MOVE_BRACKET_MATCH
] = {
505 .txt
= bracket_match
,
506 .type
= INCLUSIVE
|JUMP
,
508 [VIS_MOVE_FILE_BEGIN
] = {
510 .type
= LINEWISE
|LINEWISE_INCLUSIVE
|JUMP
|IDEMPOTENT
,
512 [VIS_MOVE_FILE_END
] = {
514 .type
= LINEWISE
|LINEWISE_INCLUSIVE
|JUMP
|IDEMPOTENT
,
516 [VIS_MOVE_LEFT_TO
] = {
520 [VIS_MOVE_RIGHT_TO
] = {
522 .type
= INCLUSIVE
|COUNT_EXACT
,
524 [VIS_MOVE_LEFT_TILL
] = {
528 [VIS_MOVE_RIGHT_TILL
] = {
530 .type
= INCLUSIVE
|COUNT_EXACT
,
532 [VIS_MOVE_SEARCH_WORD_FORWARD
] = {
533 .vis
= search_word_forward
,
536 [VIS_MOVE_SEARCH_WORD_BACKWARD
] = {
537 .vis
= search_word_backward
,
540 [VIS_MOVE_SEARCH_REPEAT_FORWARD
] = {
541 .vis
= search_forward
,
544 [VIS_MOVE_SEARCH_REPEAT_BACKWARD
] = {
545 .vis
= search_backward
,
548 [VIS_MOVE_WINDOW_LINE_TOP
] = {
549 .view
= view_lines_top
,
550 .type
= LINEWISE
|JUMP
|IDEMPOTENT
,
552 [VIS_MOVE_WINDOW_LINE_MIDDLE
] = {
553 .view
= view_lines_middle
,
554 .type
= LINEWISE
|JUMP
|IDEMPOTENT
,
556 [VIS_MOVE_WINDOW_LINE_BOTTOM
] = {
557 .view
= view_lines_bottom
,
558 .type
= LINEWISE
|JUMP
|IDEMPOTENT
,
564 [VIS_MOVE_PERCENT
] = {
572 [VIS_MOVE_BYTE_LEFT
] = {
576 [VIS_MOVE_BYTE_RIGHT
] = {