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_right(Vis
*vis
, Text
*txt
, size_t pos
) {
117 size_t hit
= text_find_next(txt
, pos
+1, vis
->search_char
);
118 if (!text_byte_get(txt
, hit
, &c
) || c
!= vis
->search_char
[0])
123 static size_t till_right(Vis
*vis
, Text
*txt
, size_t pos
) {
124 size_t hit
= to_right(vis
, txt
, pos
+1);
126 return text_char_prev(txt
, hit
);
130 static size_t to_left(Vis
*vis
, Text
*txt
, size_t pos
) {
131 return text_find_prev(txt
, pos
, vis
->search_char
);
134 static size_t till_left(Vis
*vis
, Text
*txt
, size_t pos
) {
135 size_t hit
= to_left(vis
, txt
, pos
-1);
137 return text_char_next(txt
, hit
);
141 static size_t to_line_right(Vis
*vis
, Text
*txt
, size_t pos
) {
143 if (pos
== text_line_end(txt
, pos
))
145 size_t hit
= text_line_find_next(txt
, pos
+1, vis
->search_char
);
146 if (!text_byte_get(txt
, hit
, &c
) || c
!= vis
->search_char
[0])
151 static size_t till_line_right(Vis
*vis
, Text
*txt
, size_t pos
) {
152 size_t hit
= to_line_right(vis
, txt
, pos
+1);
153 if (pos
== text_line_end(txt
, pos
))
156 return text_char_prev(txt
, hit
);
160 static size_t to_line_left(Vis
*vis
, Text
*txt
, size_t pos
) {
161 return text_line_find_prev(txt
, pos
, vis
->search_char
);
164 static size_t till_line_left(Vis
*vis
, Text
*txt
, size_t pos
) {
165 if (pos
== text_line_begin(txt
, pos
))
167 size_t hit
= to_line_left(vis
, txt
, pos
-1);
169 return text_char_next(txt
, hit
);
173 static size_t firstline(Text
*txt
, size_t pos
) {
174 return text_line_start(txt
, 0);
177 static size_t line(Vis
*vis
, Text
*txt
, size_t pos
) {
178 int count
= vis_count_get_default(vis
, 1);
179 return text_line_start(txt
, text_pos_by_lineno(txt
, count
));
182 static size_t lastline(Text
*txt
, size_t pos
) {
183 pos
= text_size(txt
);
184 return text_line_start(txt
, pos
> 0 ? pos
-1 : pos
);
187 static size_t column(Vis
*vis
, Text
*txt
, size_t pos
) {
188 return text_line_offset(txt
, pos
, vis_count_get_default(vis
, 0));
191 static size_t view_lines_top(Vis
*vis
, View
*view
) {
192 return view_screenline_goto(view
, vis_count_get_default(vis
, 1));
195 static size_t view_lines_middle(Vis
*vis
, View
*view
) {
196 int h
= view_height_get(view
);
197 return view_screenline_goto(view
, h
/2);
200 static size_t view_lines_bottom(Vis
*vis
, View
*view
) {
201 int h
= view_height_get(vis
->win
->view
);
202 return view_screenline_goto(vis
->win
->view
, h
- vis_count_get_default(vis
, 0));
205 static size_t window_nop(Vis
*vis
, Win
*win
, size_t pos
) {
209 static size_t bracket_match(Text
*txt
, size_t pos
) {
210 size_t hit
= text_bracket_match_symbol(txt
, pos
, "(){}[]<>'\"`", NULL
);
214 Iterator it
= text_iterator_get(txt
, pos
);
215 while (text_iterator_byte_get(&it
, ¤t
)) {
230 text_iterator_byte_next(&it
, NULL
);
235 static size_t percent(Vis
*vis
, Text
*txt
, size_t pos
) {
236 int ratio
= vis_count_get_default(vis
, 0);
239 return text_size(txt
) * ratio
/ 100;
242 static size_t byte(Vis
*vis
, Text
*txt
, size_t pos
) {
243 pos
= vis_count_get_default(vis
, 0);
244 size_t max
= text_size(txt
);
245 return pos
<= max
? pos
: max
;
248 static size_t byte_left(Vis
*vis
, Text
*txt
, size_t pos
) {
249 size_t off
= vis_count_get_default(vis
, 1);
250 return off
<= pos
? pos
-off
: 0;
253 static size_t byte_right(Vis
*vis
, Text
*txt
, size_t pos
) {
254 size_t off
= vis_count_get_default(vis
, 1);
255 size_t new = pos
+ off
;
256 size_t max
= text_size(txt
);
257 return new <= max
&& new > pos
? new : max
;
260 void vis_motion_type(Vis
*vis
, enum VisMotionType type
) {
261 vis
->action
.type
= type
;
264 int vis_motion_register(Vis
*vis
, void *data
, VisMotionFunction
*motion
) {
266 Movement
*move
= calloc(1, sizeof *move
);
273 if (array_add_ptr(&vis
->motions
, move
))
274 return VIS_MOVE_LAST
+ array_length(&vis
->motions
) - 1;
279 bool vis_motion(Vis
*vis
, enum VisMotion motion
, ...) {
281 va_start(ap
, motion
);
284 case VIS_MOVE_WORD_START_NEXT
:
285 if (vis
->action
.op
== &vis_operators
[VIS_OP_CHANGE
])
286 motion
= VIS_MOVE_WORD_NEXT
;
288 case VIS_MOVE_LONGWORD_START_NEXT
:
289 if (vis
->action
.op
== &vis_operators
[VIS_OP_CHANGE
])
290 motion
= VIS_MOVE_LONGWORD_NEXT
;
292 case VIS_MOVE_SEARCH_FORWARD
:
293 case VIS_MOVE_SEARCH_BACKWARD
:
295 const char *pattern
= va_arg(ap
, char*);
296 Regex
*regex
= vis_regex(vis
, pattern
);
301 text_regex_free(regex
);
302 if (motion
== VIS_MOVE_SEARCH_FORWARD
)
303 motion
= VIS_MOVE_SEARCH_REPEAT_FORWARD
;
305 motion
= VIS_MOVE_SEARCH_REPEAT_BACKWARD
;
306 vis
->search_direction
= motion
;
309 case VIS_MOVE_SEARCH_REPEAT
:
310 case VIS_MOVE_SEARCH_REPEAT_REVERSE
:
312 if (!vis
->search_direction
)
313 vis
->search_direction
= VIS_MOVE_SEARCH_REPEAT_FORWARD
;
314 if (motion
== VIS_MOVE_SEARCH_REPEAT
) {
315 motion
= vis
->search_direction
;
317 motion
= vis
->search_direction
== VIS_MOVE_SEARCH_REPEAT_FORWARD
?
318 VIS_MOVE_SEARCH_REPEAT_BACKWARD
:
319 VIS_MOVE_SEARCH_REPEAT_FORWARD
;
323 case VIS_MOVE_TO_RIGHT
:
324 case VIS_MOVE_TO_LEFT
:
325 case VIS_MOVE_TO_LINE_RIGHT
:
326 case VIS_MOVE_TO_LINE_LEFT
:
327 case VIS_MOVE_TILL_RIGHT
:
328 case VIS_MOVE_TILL_LEFT
:
329 case VIS_MOVE_TILL_LINE_RIGHT
:
330 case VIS_MOVE_TILL_LINE_LEFT
:
332 const char *key
= va_arg(ap
, char*);
335 strncpy(vis
->search_char
, key
, sizeof(vis
->search_char
));
336 vis
->search_char
[sizeof(vis
->search_char
)-1] = '\0';
337 vis
->last_totill
= motion
;
340 case VIS_MOVE_TOTILL_REPEAT
:
341 if (!vis
->last_totill
)
343 motion
= vis
->last_totill
;
345 case VIS_MOVE_TOTILL_REVERSE
:
346 switch (vis
->last_totill
) {
347 case VIS_MOVE_TO_RIGHT
:
348 motion
= VIS_MOVE_TO_LEFT
;
350 case VIS_MOVE_TO_LEFT
:
351 motion
= VIS_MOVE_TO_RIGHT
;
353 case VIS_MOVE_TO_LINE_RIGHT
:
354 motion
= VIS_MOVE_TO_LINE_LEFT
;
356 case VIS_MOVE_TO_LINE_LEFT
:
357 motion
= VIS_MOVE_TO_LINE_RIGHT
;
359 case VIS_MOVE_TILL_RIGHT
:
360 motion
= VIS_MOVE_TILL_LEFT
;
362 case VIS_MOVE_TILL_LEFT
:
363 motion
= VIS_MOVE_TILL_RIGHT
;
365 case VIS_MOVE_TILL_LINE_RIGHT
:
366 motion
= VIS_MOVE_TILL_LINE_LEFT
;
368 case VIS_MOVE_TILL_LINE_LEFT
:
369 motion
= VIS_MOVE_TILL_LINE_RIGHT
;
379 if (motion
< LENGTH(vis_motions
))
380 vis
->action
.movement
= &vis_motions
[motion
];
382 vis
->action
.movement
= array_get_ptr(&vis
->motions
, motion
- VIS_MOVE_LAST
);
384 if (!vis
->action
.movement
)
395 const Movement vis_motions
[] = {
396 [VIS_MOVE_LINE_UP
] = {
398 .type
= LINEWISE
|LINEWISE_INCLUSIVE
,
400 [VIS_MOVE_LINE_DOWN
] = {
401 .cur
= view_line_down
,
402 .type
= LINEWISE
|LINEWISE_INCLUSIVE
,
404 [VIS_MOVE_SCREEN_LINE_UP
] = {
405 .cur
= view_screenline_up
,
407 [VIS_MOVE_SCREEN_LINE_DOWN
] = {
408 .cur
= view_screenline_down
,
410 [VIS_MOVE_SCREEN_LINE_BEGIN
] = {
411 .cur
= view_screenline_begin
,
414 [VIS_MOVE_SCREEN_LINE_MIDDLE
] = {
415 .cur
= view_screenline_middle
,
418 [VIS_MOVE_SCREEN_LINE_END
] = {
419 .cur
= view_screenline_end
,
420 .type
= CHARWISE
|INCLUSIVE
,
422 [VIS_MOVE_LINE_PREV
] = {
423 .txt
= text_line_prev
,
425 [VIS_MOVE_LINE_BEGIN
] = {
426 .txt
= text_line_begin
,
429 [VIS_MOVE_LINE_START
] = {
430 .txt
= text_line_start
,
433 [VIS_MOVE_LINE_FINISH
] = {
434 .txt
= text_line_finish
,
435 .type
= INCLUSIVE
|IDEMPOTENT
,
437 [VIS_MOVE_LINE_END
] = {
438 .txt
= text_line_end
,
441 [VIS_MOVE_LINE_NEXT
] = {
442 .txt
= text_line_next
,
446 .type
= LINEWISE
|IDEMPOTENT
|JUMP
,
448 [VIS_MOVE_COLUMN
] = {
450 .type
= CHARWISE
|IDEMPOTENT
,
452 [VIS_MOVE_CHAR_PREV
] = {
453 .txt
= text_char_prev
,
456 [VIS_MOVE_CHAR_NEXT
] = {
457 .txt
= text_char_next
,
460 [VIS_MOVE_LINE_CHAR_PREV
] = {
461 .txt
= text_line_char_prev
,
464 [VIS_MOVE_LINE_CHAR_NEXT
] = {
465 .txt
= text_line_char_next
,
468 [VIS_MOVE_CODEPOINT_PREV
] = {
469 .txt
= text_codepoint_prev
,
472 [VIS_MOVE_CODEPOINT_NEXT
] = {
473 .txt
= text_codepoint_next
,
476 [VIS_MOVE_WORD_NEXT
] = {
478 .type
= CHARWISE
|IDEMPOTENT
,
480 [VIS_MOVE_WORD_START_PREV
] = {
481 .txt
= text_word_start_prev
,
484 [VIS_MOVE_WORD_START_NEXT
] = {
485 .txt
= text_word_start_next
,
488 [VIS_MOVE_WORD_END_PREV
] = {
489 .txt
= text_word_end_prev
,
490 .type
= CHARWISE
|INCLUSIVE
,
492 [VIS_MOVE_WORD_END_NEXT
] = {
493 .txt
= text_word_end_next
,
494 .type
= CHARWISE
|INCLUSIVE
,
496 [VIS_MOVE_LONGWORD_NEXT
] = {
497 .vis
= longword_next
,
498 .type
= CHARWISE
|IDEMPOTENT
,
500 [VIS_MOVE_LONGWORD_START_PREV
] = {
501 .txt
= text_longword_start_prev
,
504 [VIS_MOVE_LONGWORD_START_NEXT
] = {
505 .txt
= text_longword_start_next
,
508 [VIS_MOVE_LONGWORD_END_PREV
] = {
509 .txt
= text_longword_end_prev
,
510 .type
= CHARWISE
|INCLUSIVE
,
512 [VIS_MOVE_LONGWORD_END_NEXT
] = {
513 .txt
= text_longword_end_next
,
514 .type
= CHARWISE
|INCLUSIVE
,
516 [VIS_MOVE_SENTENCE_PREV
] = {
517 .txt
= text_sentence_prev
,
520 [VIS_MOVE_SENTENCE_NEXT
] = {
521 .txt
= text_sentence_next
,
524 [VIS_MOVE_PARAGRAPH_PREV
] = {
525 .txt
= text_paragraph_prev
,
526 .type
= LINEWISE
|JUMP
,
528 [VIS_MOVE_PARAGRAPH_NEXT
] = {
529 .txt
= text_paragraph_next
,
530 .type
= LINEWISE
|JUMP
,
532 [VIS_MOVE_BLOCK_START
] = {
533 .txt
= text_block_start
,
536 [VIS_MOVE_BLOCK_END
] = {
537 .txt
= text_block_end
,
540 [VIS_MOVE_PARENTHESIS_START
] = {
541 .txt
= text_parenthesis_start
,
544 [VIS_MOVE_PARENTHESIS_END
] = {
545 .txt
= text_parenthesis_end
,
548 [VIS_MOVE_BRACKET_MATCH
] = {
549 .txt
= bracket_match
,
550 .type
= INCLUSIVE
|JUMP
,
552 [VIS_MOVE_FILE_BEGIN
] = {
554 .type
= LINEWISE
|LINEWISE_INCLUSIVE
|JUMP
|IDEMPOTENT
,
556 [VIS_MOVE_FILE_END
] = {
558 .type
= LINEWISE
|LINEWISE_INCLUSIVE
|JUMP
|IDEMPOTENT
,
560 [VIS_MOVE_TO_LEFT
] = {
564 [VIS_MOVE_TO_RIGHT
] = {
566 .type
= INCLUSIVE
|COUNT_EXACT
,
568 [VIS_MOVE_TO_LINE_LEFT
] = {
572 [VIS_MOVE_TO_LINE_RIGHT
] = {
573 .vis
= to_line_right
,
574 .type
= INCLUSIVE
|COUNT_EXACT
,
576 [VIS_MOVE_TILL_LEFT
] = {
580 [VIS_MOVE_TILL_RIGHT
] = {
582 .type
= INCLUSIVE
|COUNT_EXACT
,
584 [VIS_MOVE_TILL_LINE_LEFT
] = {
585 .vis
= till_line_left
,
588 [VIS_MOVE_TILL_LINE_RIGHT
] = {
589 .vis
= till_line_right
,
590 .type
= INCLUSIVE
|COUNT_EXACT
,
592 [VIS_MOVE_SEARCH_WORD_FORWARD
] = {
593 .vis
= search_word_forward
,
596 [VIS_MOVE_SEARCH_WORD_BACKWARD
] = {
597 .vis
= search_word_backward
,
600 [VIS_MOVE_SEARCH_REPEAT_FORWARD
] = {
601 .vis
= search_forward
,
604 [VIS_MOVE_SEARCH_REPEAT_BACKWARD
] = {
605 .vis
= search_backward
,
608 [VIS_MOVE_WINDOW_LINE_TOP
] = {
609 .view
= view_lines_top
,
610 .type
= LINEWISE
|JUMP
|IDEMPOTENT
,
612 [VIS_MOVE_WINDOW_LINE_MIDDLE
] = {
613 .view
= view_lines_middle
,
614 .type
= LINEWISE
|JUMP
|IDEMPOTENT
,
616 [VIS_MOVE_WINDOW_LINE_BOTTOM
] = {
617 .view
= view_lines_bottom
,
618 .type
= LINEWISE
|JUMP
|IDEMPOTENT
,
624 [VIS_MOVE_PERCENT
] = {
632 [VIS_MOVE_BYTE_LEFT
] = {
636 [VIS_MOVE_BYTE_RIGHT
] = {