5 #include "text-motions.h"
6 #include "text-objects.h"
10 /** utility functions */
12 static Regex
*search_word(Vis
*vis
, Text
*txt
, size_t pos
) {
14 Filerange word
= text_object_word(txt
, pos
);
15 if (!text_range_valid(&word
))
17 char *buf
= text_bytes_alloc0(txt
, word
.start
, text_range_size(&word
));
20 snprintf(expr
, sizeof(expr
), "[[:<:]]%s[[:>:]]", buf
);
21 Regex
*regex
= vis_regex(vis
, expr
);
23 snprintf(expr
, sizeof(expr
), "\\<%s\\>", buf
);
24 regex
= vis_regex(vis
, expr
);
30 /** motion implementations */
32 static size_t search_word_forward(Vis
*vis
, Text
*txt
, size_t pos
) {
33 Regex
*regex
= search_word(vis
, txt
, pos
);
35 pos
= text_search_forward(txt
, pos
, regex
);
36 text_regex_free(regex
);
40 static size_t search_word_backward(Vis
*vis
, Text
*txt
, size_t pos
) {
41 Regex
*regex
= search_word(vis
, txt
, pos
);
43 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 mark_goto(Vis
*vis
, File
*file
, size_t pos
) {
65 return text_mark_get(file
->text
, file
->marks
[vis
->action
.mark
]);
68 static size_t mark_line_goto(Vis
*vis
, File
*file
, size_t pos
) {
69 return text_line_start(file
->text
, mark_goto(vis
, file
, pos
));
72 static size_t to(Vis
*vis
, Text
*txt
, size_t pos
) {
74 if (pos
== text_line_end(txt
, pos
))
76 size_t hit
= text_line_find_next(txt
, pos
+1, vis
->search_char
);
77 if (!text_byte_get(txt
, hit
, &c
) || c
!= vis
->search_char
[0])
82 static size_t till(Vis
*vis
, Text
*txt
, size_t pos
) {
83 size_t hit
= to(vis
, txt
, pos
+1);
84 if (pos
== text_line_end(txt
, pos
))
87 return text_char_prev(txt
, hit
);
91 static size_t to_left(Vis
*vis
, Text
*txt
, size_t pos
) {
92 return text_line_find_prev(txt
, pos
, vis
->search_char
);
95 static size_t till_left(Vis
*vis
, Text
*txt
, size_t pos
) {
96 if (pos
== text_line_begin(txt
, pos
))
98 size_t hit
= to_left(vis
, txt
, pos
-1);
100 return text_char_next(txt
, hit
);
104 static size_t line(Vis
*vis
, Text
*txt
, size_t pos
) {
105 return text_pos_by_lineno(txt
, vis_count_get_default(vis
, 1));
108 static size_t column(Vis
*vis
, Text
*txt
, size_t pos
) {
109 return text_line_offset(txt
, pos
, vis_count_get_default(vis
, 0));
112 static size_t view_lines_top(Vis
*vis
, View
*view
) {
113 return view_screenline_goto(view
, vis_count_get_default(vis
, 1));
116 static size_t view_lines_middle(Vis
*vis
, View
*view
) {
117 int h
= view_height_get(view
);
118 return view_screenline_goto(view
, h
/2);
121 static size_t view_lines_bottom(Vis
*vis
, View
*view
) {
122 int h
= view_height_get(vis
->win
->view
);
123 return view_screenline_goto(vis
->win
->view
, h
- vis_count_get_default(vis
, 0));
126 static size_t window_changelist_next(Vis
*vis
, Win
*win
, size_t pos
) {
127 ChangeList
*cl
= &win
->changelist
;
128 Text
*txt
= win
->file
->text
;
129 time_t state
= text_state(txt
);
130 if (cl
->state
!= state
)
132 else if (cl
->index
> 0 && pos
== cl
->pos
)
134 size_t newpos
= text_history_get(txt
, cl
->index
);
143 static size_t window_changelist_prev(Vis
*vis
, Win
*win
, size_t pos
) {
144 ChangeList
*cl
= &win
->changelist
;
145 Text
*txt
= win
->file
->text
;
146 time_t state
= text_state(txt
);
147 if (cl
->state
!= state
)
149 else if (pos
== cl
->pos
)
150 win
->changelist
.index
++;
151 size_t newpos
= text_history_get(txt
, cl
->index
);
160 static size_t window_jumplist_next(Vis
*vis
, Win
*win
, size_t cur
) {
161 while (win
->jumplist
) {
162 Mark mark
= ringbuf_next(win
->jumplist
);
165 size_t pos
= text_mark_get(win
->file
->text
, mark
);
166 if (pos
!= EPOS
&& pos
!= cur
)
172 static size_t window_jumplist_prev(Vis
*vis
, Win
*win
, size_t cur
) {
173 while (win
->jumplist
) {
174 Mark mark
= ringbuf_prev(win
->jumplist
);
177 size_t pos
= text_mark_get(win
->file
->text
, mark
);
178 if (pos
!= EPOS
&& pos
!= cur
)
184 static size_t window_nop(Vis
*vis
, Win
*win
, size_t pos
) {
188 static size_t bracket_match(Text
*txt
, size_t pos
) {
189 size_t hit
= text_bracket_match_symbol(txt
, pos
, "(){}[]");
193 Iterator it
= text_iterator_get(txt
, pos
);
194 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 void vis_motion_type(Vis
*vis
, enum VisMotionType type
) {
217 vis
->action
.type
= type
;
220 int vis_motion_register(Vis
*vis
, enum VisMotionType type
, void *data
,
221 size_t (*motion
)(Vis
*, Win
*, void*, size_t pos
)) {
223 Movement
*move
= calloc(1, sizeof *move
);
231 if (array_add_ptr(&vis
->motions
, move
))
232 return VIS_MOVE_LAST
+ array_length(&vis
->motions
) - 1;
237 bool vis_motion(Vis
*vis
, enum VisMotion motion
, ...) {
239 va_start(ap
, motion
);
242 case VIS_MOVE_WORD_START_NEXT
:
243 if (vis
->action
.op
== &vis_operators
[VIS_OP_CHANGE
])
244 motion
= VIS_MOVE_WORD_END_NEXT
;
246 case VIS_MOVE_LONGWORD_START_NEXT
:
247 if (vis
->action
.op
== &vis_operators
[VIS_OP_CHANGE
])
248 motion
= VIS_MOVE_LONGWORD_END_NEXT
;
250 case VIS_MOVE_SEARCH_FORWARD
:
251 case VIS_MOVE_SEARCH_BACKWARD
:
253 const char *pattern
= va_arg(ap
, char*);
254 Regex
*regex
= vis_regex(vis
, pattern
);
259 text_regex_free(regex
);
260 if (motion
== VIS_MOVE_SEARCH_FORWARD
)
261 motion
= VIS_MOVE_SEARCH_NEXT
;
263 motion
= VIS_MOVE_SEARCH_PREV
;
266 case VIS_MOVE_RIGHT_TO
:
267 case VIS_MOVE_LEFT_TO
:
268 case VIS_MOVE_RIGHT_TILL
:
269 case VIS_MOVE_LEFT_TILL
:
271 const char *key
= va_arg(ap
, char*);
274 strncpy(vis
->search_char
, key
, sizeof(vis
->search_char
));
275 vis
->search_char
[sizeof(vis
->search_char
)-1] = '\0';
276 vis
->last_totill
= motion
;
279 case VIS_MOVE_TOTILL_REPEAT
:
280 if (!vis
->last_totill
)
282 motion
= vis
->last_totill
;
284 case VIS_MOVE_TOTILL_REVERSE
:
285 switch (vis
->last_totill
) {
286 case VIS_MOVE_RIGHT_TO
:
287 motion
= VIS_MOVE_LEFT_TO
;
289 case VIS_MOVE_LEFT_TO
:
290 motion
= VIS_MOVE_RIGHT_TO
;
292 case VIS_MOVE_RIGHT_TILL
:
293 motion
= VIS_MOVE_LEFT_TILL
;
295 case VIS_MOVE_LEFT_TILL
:
296 motion
= VIS_MOVE_RIGHT_TILL
;
303 case VIS_MOVE_MARK_LINE
:
305 int mark
= va_arg(ap
, int);
306 if (VIS_MARK_a
<= mark
&& mark
< VIS_MARK_INVALID
)
307 vis
->action
.mark
= mark
;
316 if (motion
< LENGTH(vis_motions
))
317 vis
->action
.movement
= &vis_motions
[motion
];
319 vis
->action
.movement
= array_get_ptr(&vis
->motions
, motion
- VIS_MOVE_LAST
);
321 if (!vis
->action
.movement
)
325 action_do(vis
, &vis
->action
);
332 const Movement vis_motions
[] = {
333 [VIS_MOVE_LINE_UP
] = { .cur
= view_line_up
, .type
= LINEWISE
|LINEWISE_INCLUSIVE
},
334 [VIS_MOVE_LINE_DOWN
] = { .cur
= view_line_down
, .type
= LINEWISE
|LINEWISE_INCLUSIVE
},
335 [VIS_MOVE_SCREEN_LINE_UP
] = { .cur
= view_screenline_up
, },
336 [VIS_MOVE_SCREEN_LINE_DOWN
] = { .cur
= view_screenline_down
, },
337 [VIS_MOVE_SCREEN_LINE_BEGIN
] = { .cur
= view_screenline_begin
, .type
= CHARWISE
},
338 [VIS_MOVE_SCREEN_LINE_MIDDLE
] = { .cur
= view_screenline_middle
, .type
= CHARWISE
},
339 [VIS_MOVE_SCREEN_LINE_END
] = { .cur
= view_screenline_end
, .type
= CHARWISE
|INCLUSIVE
},
340 [VIS_MOVE_LINE_PREV
] = { .txt
= text_line_prev
, },
341 [VIS_MOVE_LINE_BEGIN
] = { .txt
= text_line_begin
, },
342 [VIS_MOVE_LINE_START
] = { .txt
= text_line_start
, },
343 [VIS_MOVE_LINE_FINISH
] = { .txt
= text_line_finish
, .type
= INCLUSIVE
},
344 [VIS_MOVE_LINE_LASTCHAR
] = { .txt
= text_line_lastchar
, .type
= INCLUSIVE
},
345 [VIS_MOVE_LINE_END
] = { .txt
= text_line_end
, },
346 [VIS_MOVE_LINE_NEXT
] = { .txt
= text_line_next
, },
347 [VIS_MOVE_LINE
] = { .vis
= line
, .type
= LINEWISE
|IDEMPOTENT
|JUMP
},
348 [VIS_MOVE_COLUMN
] = { .vis
= column
, .type
= CHARWISE
|IDEMPOTENT
},
349 [VIS_MOVE_CHAR_PREV
] = { .txt
= text_char_prev
, .type
= CHARWISE
},
350 [VIS_MOVE_CHAR_NEXT
] = { .txt
= text_char_next
, .type
= CHARWISE
},
351 [VIS_MOVE_LINE_CHAR_PREV
] = { .txt
= text_line_char_prev
, .type
= CHARWISE
},
352 [VIS_MOVE_LINE_CHAR_NEXT
] = { .txt
= text_line_char_next
, .type
= CHARWISE
},
353 [VIS_MOVE_WORD_START_PREV
] = { .txt
= text_word_start_prev
, .type
= CHARWISE
},
354 [VIS_MOVE_WORD_START_NEXT
] = { .txt
= text_word_start_next
, .type
= CHARWISE
},
355 [VIS_MOVE_WORD_END_PREV
] = { .txt
= text_word_end_prev
, .type
= CHARWISE
|INCLUSIVE
},
356 [VIS_MOVE_WORD_END_NEXT
] = { .txt
= text_word_end_next
, .type
= CHARWISE
|INCLUSIVE
},
357 [VIS_MOVE_LONGWORD_START_PREV
] = { .txt
= text_longword_start_prev
, .type
= CHARWISE
},
358 [VIS_MOVE_LONGWORD_START_NEXT
] = { .txt
= text_longword_start_next
, .type
= CHARWISE
},
359 [VIS_MOVE_LONGWORD_END_PREV
] = { .txt
= text_longword_end_prev
, .type
= CHARWISE
|INCLUSIVE
},
360 [VIS_MOVE_LONGWORD_END_NEXT
] = { .txt
= text_longword_end_next
, .type
= CHARWISE
|INCLUSIVE
},
361 [VIS_MOVE_SENTENCE_PREV
] = { .txt
= text_sentence_prev
, .type
= CHARWISE
},
362 [VIS_MOVE_SENTENCE_NEXT
] = { .txt
= text_sentence_next
, .type
= CHARWISE
},
363 [VIS_MOVE_PARAGRAPH_PREV
] = { .txt
= text_paragraph_prev
, .type
= LINEWISE
|JUMP
},
364 [VIS_MOVE_PARAGRAPH_NEXT
] = { .txt
= text_paragraph_next
, .type
= LINEWISE
|JUMP
},
365 [VIS_MOVE_FUNCTION_START_PREV
] = { .txt
= text_function_start_prev
, .type
= LINEWISE
|JUMP
},
366 [VIS_MOVE_FUNCTION_START_NEXT
] = { .txt
= text_function_start_next
, .type
= LINEWISE
|JUMP
},
367 [VIS_MOVE_FUNCTION_END_PREV
] = { .txt
= text_function_end_prev
, .type
= LINEWISE
|JUMP
},
368 [VIS_MOVE_FUNCTION_END_NEXT
] = { .txt
= text_function_end_next
, .type
= LINEWISE
|JUMP
},
369 [VIS_MOVE_BLOCK_START
] = { .txt
= text_block_start
, .type
= JUMP
},
370 [VIS_MOVE_BLOCK_END
] = { .txt
= text_block_end
, .type
= JUMP
},
371 [VIS_MOVE_PARENTHESE_START
] = { .txt
= text_parenthese_start
, .type
= JUMP
},
372 [VIS_MOVE_PARENTHESE_END
] = { .txt
= text_parenthese_end
, .type
= JUMP
},
373 [VIS_MOVE_BRACKET_MATCH
] = { .txt
= bracket_match
, .type
= INCLUSIVE
|JUMP
},
374 [VIS_MOVE_FILE_BEGIN
] = { .txt
= text_begin
, .type
= LINEWISE
|JUMP
},
375 [VIS_MOVE_FILE_END
] = { .txt
= text_end
, .type
= LINEWISE
|JUMP
},
376 [VIS_MOVE_LEFT_TO
] = { .vis
= to_left
, },
377 [VIS_MOVE_RIGHT_TO
] = { .vis
= to
, .type
= INCLUSIVE
},
378 [VIS_MOVE_LEFT_TILL
] = { .vis
= till_left
, },
379 [VIS_MOVE_RIGHT_TILL
] = { .vis
= till
, .type
= INCLUSIVE
},
380 [VIS_MOVE_MARK
] = { .file
= mark_goto
, .type
= JUMP
|IDEMPOTENT
},
381 [VIS_MOVE_MARK_LINE
] = { .file
= mark_line_goto
, .type
= LINEWISE
|JUMP
|IDEMPOTENT
},
382 [VIS_MOVE_SEARCH_WORD_FORWARD
] = { .vis
= search_word_forward
, .type
= JUMP
},
383 [VIS_MOVE_SEARCH_WORD_BACKWARD
]= { .vis
= search_word_backward
, .type
= JUMP
},
384 [VIS_MOVE_SEARCH_NEXT
] = { .vis
= search_forward
, .type
= JUMP
},
385 [VIS_MOVE_SEARCH_PREV
] = { .vis
= search_backward
, .type
= JUMP
},
386 [VIS_MOVE_WINDOW_LINE_TOP
] = { .view
= view_lines_top
, .type
= LINEWISE
|JUMP
|IDEMPOTENT
},
387 [VIS_MOVE_WINDOW_LINE_MIDDLE
] = { .view
= view_lines_middle
, .type
= LINEWISE
|JUMP
|IDEMPOTENT
},
388 [VIS_MOVE_WINDOW_LINE_BOTTOM
] = { .view
= view_lines_bottom
, .type
= LINEWISE
|JUMP
|IDEMPOTENT
},
389 [VIS_MOVE_CHANGELIST_NEXT
] = { .win
= window_changelist_next
, .type
= INCLUSIVE
},
390 [VIS_MOVE_CHANGELIST_PREV
] = { .win
= window_changelist_prev
, .type
= INCLUSIVE
},
391 [VIS_MOVE_JUMPLIST_NEXT
] = { .win
= window_jumplist_next
, .type
= INCLUSIVE
},
392 [VIS_MOVE_JUMPLIST_PREV
] = { .win
= window_jumplist_prev
, .type
= INCLUSIVE
},
393 [VIS_MOVE_NOP
] = { .win
= window_nop
, .type
= IDEMPOTENT
},
394 [VIS_MOVE_PERCENT
] = { .vis
= percent
, .type
= IDEMPOTENT
},