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
)) {
204 text_iterator_byte_next(&it
, NULL
);
209 static size_t percent(Vis
*vis
, Text
*txt
, size_t pos
) {
210 int ratio
= vis_count_get_default(vis
, 0);
213 return text_size(txt
) * ratio
/ 100;
216 static size_t byte(Vis
*vis
, Text
*txt
, size_t pos
) {
217 pos
= vis_count_get_default(vis
, 0);
218 size_t max
= text_size(txt
);
219 return pos
<= max
? pos
: max
;
222 static size_t byte_left(Vis
*vis
, Text
*txt
, size_t pos
) {
223 size_t off
= vis_count_get_default(vis
, 1);
224 return off
<= pos
? pos
-off
: 0;
227 static size_t byte_right(Vis
*vis
, Text
*txt
, size_t pos
) {
228 size_t off
= vis_count_get_default(vis
, 1);
229 size_t new = pos
+ off
;
230 size_t max
= text_size(txt
);
231 return new <= max
&& new > pos
? new : max
;
234 void vis_motion_type(Vis
*vis
, enum VisMotionType type
) {
235 vis
->action
.type
= type
;
238 int vis_motion_register(Vis
*vis
, void *data
, VisMotionFunction
*motion
) {
240 Movement
*move
= calloc(1, sizeof *move
);
247 if (array_add_ptr(&vis
->motions
, move
))
248 return VIS_MOVE_LAST
+ array_length(&vis
->motions
) - 1;
253 bool vis_motion(Vis
*vis
, enum VisMotion motion
, ...) {
255 va_start(ap
, motion
);
258 case VIS_MOVE_WORD_START_NEXT
:
259 if (vis
->action
.op
== &vis_operators
[VIS_OP_CHANGE
])
260 motion
= VIS_MOVE_WORD_NEXT
;
262 case VIS_MOVE_LONGWORD_START_NEXT
:
263 if (vis
->action
.op
== &vis_operators
[VIS_OP_CHANGE
])
264 motion
= VIS_MOVE_LONGWORD_NEXT
;
266 case VIS_MOVE_SEARCH_FORWARD
:
267 case VIS_MOVE_SEARCH_BACKWARD
:
269 const char *pattern
= va_arg(ap
, char*);
270 Regex
*regex
= vis_regex(vis
, pattern
);
275 text_regex_free(regex
);
276 if (motion
== VIS_MOVE_SEARCH_FORWARD
)
277 motion
= VIS_MOVE_SEARCH_REPEAT_FORWARD
;
279 motion
= VIS_MOVE_SEARCH_REPEAT_BACKWARD
;
280 vis
->search_direction
= motion
;
283 case VIS_MOVE_SEARCH_REPEAT
:
284 case VIS_MOVE_SEARCH_REPEAT_REVERSE
:
286 if (!vis
->search_direction
)
287 vis
->search_direction
= VIS_MOVE_SEARCH_REPEAT_FORWARD
;
288 if (motion
== VIS_MOVE_SEARCH_REPEAT
) {
289 motion
= vis
->search_direction
;
291 motion
= vis
->search_direction
== VIS_MOVE_SEARCH_REPEAT_FORWARD
?
292 VIS_MOVE_SEARCH_REPEAT_BACKWARD
:
293 VIS_MOVE_SEARCH_REPEAT_FORWARD
;
297 case VIS_MOVE_RIGHT_TO
:
298 case VIS_MOVE_LEFT_TO
:
299 case VIS_MOVE_RIGHT_TILL
:
300 case VIS_MOVE_LEFT_TILL
:
302 const char *key
= va_arg(ap
, char*);
305 strncpy(vis
->search_char
, key
, sizeof(vis
->search_char
));
306 vis
->search_char
[sizeof(vis
->search_char
)-1] = '\0';
307 vis
->last_totill
= motion
;
310 case VIS_MOVE_TOTILL_REPEAT
:
311 if (!vis
->last_totill
)
313 motion
= vis
->last_totill
;
315 case VIS_MOVE_TOTILL_REVERSE
:
316 switch (vis
->last_totill
) {
317 case VIS_MOVE_RIGHT_TO
:
318 motion
= VIS_MOVE_LEFT_TO
;
320 case VIS_MOVE_LEFT_TO
:
321 motion
= VIS_MOVE_RIGHT_TO
;
323 case VIS_MOVE_RIGHT_TILL
:
324 motion
= VIS_MOVE_LEFT_TILL
;
326 case VIS_MOVE_LEFT_TILL
:
327 motion
= VIS_MOVE_RIGHT_TILL
;
337 if (motion
< LENGTH(vis_motions
))
338 vis
->action
.movement
= &vis_motions
[motion
];
340 vis
->action
.movement
= array_get_ptr(&vis
->motions
, motion
- VIS_MOVE_LAST
);
342 if (!vis
->action
.movement
)
353 const Movement vis_motions
[] = {
354 [VIS_MOVE_LINE_UP
] = {
356 .type
= LINEWISE
|LINEWISE_INCLUSIVE
,
358 [VIS_MOVE_LINE_DOWN
] = {
359 .cur
= view_line_down
,
360 .type
= LINEWISE
|LINEWISE_INCLUSIVE
,
362 [VIS_MOVE_SCREEN_LINE_UP
] = {
363 .cur
= view_screenline_up
,
365 [VIS_MOVE_SCREEN_LINE_DOWN
] = {
366 .cur
= view_screenline_down
,
368 [VIS_MOVE_SCREEN_LINE_BEGIN
] = {
369 .cur
= view_screenline_begin
,
372 [VIS_MOVE_SCREEN_LINE_MIDDLE
] = {
373 .cur
= view_screenline_middle
,
376 [VIS_MOVE_SCREEN_LINE_END
] = {
377 .cur
= view_screenline_end
,
378 .type
= CHARWISE
|INCLUSIVE
,
380 [VIS_MOVE_LINE_PREV
] = {
381 .txt
= text_line_prev
,
383 [VIS_MOVE_LINE_BEGIN
] = {
384 .txt
= text_line_begin
,
387 [VIS_MOVE_LINE_START
] = {
388 .txt
= text_line_start
,
391 [VIS_MOVE_LINE_FINISH
] = {
392 .txt
= text_line_finish
,
393 .type
= INCLUSIVE
|IDEMPOTENT
,
395 [VIS_MOVE_LINE_END
] = {
396 .txt
= text_line_end
,
399 [VIS_MOVE_LINE_NEXT
] = {
400 .txt
= text_line_next
,
404 .type
= LINEWISE
|IDEMPOTENT
|JUMP
,
406 [VIS_MOVE_COLUMN
] = {
408 .type
= CHARWISE
|IDEMPOTENT
,
410 [VIS_MOVE_CHAR_PREV
] = {
411 .txt
= text_char_prev
,
414 [VIS_MOVE_CHAR_NEXT
] = {
415 .txt
= text_char_next
,
418 [VIS_MOVE_LINE_CHAR_PREV
] = {
419 .txt
= text_line_char_prev
,
422 [VIS_MOVE_LINE_CHAR_NEXT
] = {
423 .txt
= text_line_char_next
,
426 [VIS_MOVE_CODEPOINT_PREV
] = {
427 .txt
= text_codepoint_prev
,
430 [VIS_MOVE_CODEPOINT_NEXT
] = {
431 .txt
= text_codepoint_next
,
434 [VIS_MOVE_WORD_NEXT
] = {
436 .type
= CHARWISE
|IDEMPOTENT
,
438 [VIS_MOVE_WORD_START_PREV
] = {
439 .txt
= text_word_start_prev
,
442 [VIS_MOVE_WORD_START_NEXT
] = {
443 .txt
= text_word_start_next
,
446 [VIS_MOVE_WORD_END_PREV
] = {
447 .txt
= text_word_end_prev
,
448 .type
= CHARWISE
|INCLUSIVE
,
450 [VIS_MOVE_WORD_END_NEXT
] = {
451 .txt
= text_word_end_next
,
452 .type
= CHARWISE
|INCLUSIVE
,
454 [VIS_MOVE_LONGWORD_NEXT
] = {
455 .vis
= longword_next
,
456 .type
= CHARWISE
|IDEMPOTENT
,
458 [VIS_MOVE_LONGWORD_START_PREV
] = {
459 .txt
= text_longword_start_prev
,
462 [VIS_MOVE_LONGWORD_START_NEXT
] = {
463 .txt
= text_longword_start_next
,
466 [VIS_MOVE_LONGWORD_END_PREV
] = {
467 .txt
= text_longword_end_prev
,
468 .type
= CHARWISE
|INCLUSIVE
,
470 [VIS_MOVE_LONGWORD_END_NEXT
] = {
471 .txt
= text_longword_end_next
,
472 .type
= CHARWISE
|INCLUSIVE
,
474 [VIS_MOVE_SENTENCE_PREV
] = {
475 .txt
= text_sentence_prev
,
478 [VIS_MOVE_SENTENCE_NEXT
] = {
479 .txt
= text_sentence_next
,
482 [VIS_MOVE_PARAGRAPH_PREV
] = {
483 .txt
= text_paragraph_prev
,
484 .type
= LINEWISE
|JUMP
,
486 [VIS_MOVE_PARAGRAPH_NEXT
] = {
487 .txt
= text_paragraph_next
,
488 .type
= LINEWISE
|JUMP
,
490 [VIS_MOVE_BLOCK_START
] = {
491 .txt
= text_block_start
,
494 [VIS_MOVE_BLOCK_END
] = {
495 .txt
= text_block_end
,
498 [VIS_MOVE_PARENTHESIS_START
] = {
499 .txt
= text_parenthesis_start
,
502 [VIS_MOVE_PARENTHESIS_END
] = {
503 .txt
= text_parenthesis_end
,
506 [VIS_MOVE_BRACKET_MATCH
] = {
507 .txt
= bracket_match
,
508 .type
= INCLUSIVE
|JUMP
,
510 [VIS_MOVE_FILE_BEGIN
] = {
512 .type
= LINEWISE
|LINEWISE_INCLUSIVE
|JUMP
|IDEMPOTENT
,
514 [VIS_MOVE_FILE_END
] = {
516 .type
= LINEWISE
|LINEWISE_INCLUSIVE
|JUMP
|IDEMPOTENT
,
518 [VIS_MOVE_LEFT_TO
] = {
522 [VIS_MOVE_RIGHT_TO
] = {
524 .type
= INCLUSIVE
|COUNT_EXACT
,
526 [VIS_MOVE_LEFT_TILL
] = {
530 [VIS_MOVE_RIGHT_TILL
] = {
532 .type
= INCLUSIVE
|COUNT_EXACT
,
534 [VIS_MOVE_SEARCH_WORD_FORWARD
] = {
535 .vis
= search_word_forward
,
538 [VIS_MOVE_SEARCH_WORD_BACKWARD
] = {
539 .vis
= search_word_backward
,
542 [VIS_MOVE_SEARCH_REPEAT_FORWARD
] = {
543 .vis
= search_forward
,
546 [VIS_MOVE_SEARCH_REPEAT_BACKWARD
] = {
547 .vis
= search_backward
,
550 [VIS_MOVE_WINDOW_LINE_TOP
] = {
551 .view
= view_lines_top
,
552 .type
= LINEWISE
|JUMP
|IDEMPOTENT
,
554 [VIS_MOVE_WINDOW_LINE_MIDDLE
] = {
555 .view
= view_lines_middle
,
556 .type
= LINEWISE
|JUMP
|IDEMPOTENT
,
558 [VIS_MOVE_WINDOW_LINE_BOTTOM
] = {
559 .view
= view_lines_bottom
,
560 .type
= LINEWISE
|JUMP
|IDEMPOTENT
,
566 [VIS_MOVE_PERCENT
] = {
574 [VIS_MOVE_BYTE_LEFT
] = {
578 [VIS_MOVE_BYTE_RIGHT
] = {