5 #include "text-motions.h"
6 #include "text-objects.h"
9 /** utility functions */
11 static bool search_word(Vis
*vis
, Text
*txt
, size_t pos
) {
13 Filerange word
= text_object_word(txt
, pos
);
14 if (!text_range_valid(&word
))
16 char *buf
= text_bytes_alloc0(txt
, word
.start
, text_range_size(&word
));
19 snprintf(expr
, sizeof(expr
), "\\<%s\\>", buf
);
21 return text_regex_compile(vis
->search_pattern
, expr
, REG_EXTENDED
) == 0;
24 /** motion implementations */
26 static size_t search_word_forward(Vis
*vis
, Text
*txt
, size_t pos
) {
27 if (search_word(vis
, txt
, pos
))
28 pos
= text_search_forward(txt
, pos
, vis
->search_pattern
);
32 static size_t search_word_backward(Vis
*vis
, Text
*txt
, size_t pos
) {
33 if (search_word(vis
, txt
, pos
))
34 pos
= text_search_backward(txt
, pos
, vis
->search_pattern
);
38 static size_t search_forward(Vis
*vis
, Text
*txt
, size_t pos
) {
39 return text_search_forward(txt
, pos
, vis
->search_pattern
);
42 static size_t search_backward(Vis
*vis
, Text
*txt
, size_t pos
) {
43 return text_search_backward(txt
, pos
, vis
->search_pattern
);
46 static size_t mark_goto(Vis
*vis
, File
*file
, size_t pos
) {
47 return text_mark_get(file
->text
, file
->marks
[vis
->action
.mark
]);
50 static size_t mark_line_goto(Vis
*vis
, File
*file
, size_t pos
) {
51 return text_line_start(file
->text
, mark_goto(vis
, file
, pos
));
54 static size_t to(Vis
*vis
, Text
*txt
, size_t pos
) {
56 if (pos
== text_line_end(txt
, pos
))
58 size_t hit
= text_line_find_next(txt
, pos
+1, vis
->search_char
);
59 if (!text_byte_get(txt
, hit
, &c
) || c
!= vis
->search_char
[0])
64 static size_t till(Vis
*vis
, Text
*txt
, size_t pos
) {
65 size_t hit
= to(vis
, txt
, pos
+1);
66 if (pos
== text_line_end(txt
, pos
))
69 return text_char_prev(txt
, hit
);
73 static size_t to_left(Vis
*vis
, Text
*txt
, size_t pos
) {
75 if (pos
== text_line_begin(txt
, pos
))
77 size_t hit
= text_line_find_prev(txt
, pos
-1, vis
->search_char
);
78 if (!text_byte_get(txt
, hit
, &c
) || c
!= vis
->search_char
[0])
83 static size_t till_left(Vis
*vis
, Text
*txt
, size_t pos
) {
84 if (pos
== text_line_begin(txt
, pos
))
86 size_t hit
= to_left(vis
, txt
, pos
-1);
88 return text_char_next(txt
, hit
);
92 static size_t line(Vis
*vis
, Text
*txt
, size_t pos
) {
93 return text_pos_by_lineno(txt
, vis_count_get_default(vis
, 1));
96 static size_t column(Vis
*vis
, Text
*txt
, size_t pos
) {
97 return text_line_offset(txt
, pos
, vis_count_get_default(vis
, 0));
100 static size_t view_lines_top(Vis
*vis
, View
*view
) {
101 return view_screenline_goto(view
, vis_count_get_default(vis
, 1));
104 static size_t view_lines_middle(Vis
*vis
, View
*view
) {
105 int h
= view_height_get(view
);
106 return view_screenline_goto(view
, h
/2);
109 static size_t view_lines_bottom(Vis
*vis
, View
*view
) {
110 int h
= view_height_get(vis
->win
->view
);
111 return view_screenline_goto(vis
->win
->view
, h
- vis_count_get_default(vis
, 0));
114 static size_t window_changelist_next(Vis
*vis
, Win
*win
, size_t pos
) {
115 ChangeList
*cl
= &win
->changelist
;
116 Text
*txt
= win
->file
->text
;
117 time_t state
= text_state(txt
);
118 if (cl
->state
!= state
)
120 else if (cl
->index
> 0 && pos
== cl
->pos
)
122 size_t newpos
= text_history_get(txt
, cl
->index
);
131 static size_t window_changelist_prev(Vis
*vis
, Win
*win
, size_t pos
) {
132 ChangeList
*cl
= &win
->changelist
;
133 Text
*txt
= win
->file
->text
;
134 time_t state
= text_state(txt
);
135 if (cl
->state
!= state
)
137 else if (pos
== cl
->pos
)
138 win
->changelist
.index
++;
139 size_t newpos
= text_history_get(txt
, cl
->index
);
148 static size_t window_jumplist_next(Vis
*vis
, Win
*win
, size_t cur
) {
149 while (win
->jumplist
) {
150 Mark mark
= ringbuf_next(win
->jumplist
);
153 size_t pos
= text_mark_get(win
->file
->text
, mark
);
154 if (pos
!= EPOS
&& pos
!= cur
)
160 static size_t window_jumplist_prev(Vis
*vis
, Win
*win
, size_t cur
) {
161 while (win
->jumplist
) {
162 Mark mark
= ringbuf_prev(win
->jumplist
);
165 size_t pos
= text_mark_get(win
->file
->text
, mark
);
166 if (pos
!= EPOS
&& pos
!= cur
)
172 static size_t window_nop(Vis
*vis
, Win
*win
, size_t pos
) {
176 static size_t bracket_match(Text
*txt
, size_t pos
) {
177 size_t hit
= text_bracket_match_symbol(txt
, pos
, "(){}[]");
181 Iterator it
= text_iterator_get(txt
, pos
);
182 while (text_iterator_byte_get(&it
, ¤t
)) {
192 text_iterator_byte_next(&it
, NULL
);
197 static size_t percent(Vis
*vis
, Text
*txt
, size_t pos
) {
198 int ratio
= vis_count_get_default(vis
, 0);
201 return text_size(txt
) * ratio
/ 100;
204 void vis_motion_type(Vis
*vis
, enum VisMotionType type
) {
205 vis
->action
.type
= type
;
208 bool vis_motion(Vis
*vis
, enum VisMotion motion
, ...) {
210 va_start(ap
, motion
);
213 case VIS_MOVE_WORD_START_NEXT
:
214 if (vis
->action
.op
== &vis_operators
[VIS_OP_CHANGE
])
215 motion
= VIS_MOVE_WORD_END_NEXT
;
217 case VIS_MOVE_LONGWORD_START_NEXT
:
218 if (vis
->action
.op
== &vis_operators
[VIS_OP_CHANGE
])
219 motion
= VIS_MOVE_LONGWORD_END_NEXT
;
221 case VIS_MOVE_SEARCH_FORWARD
:
222 case VIS_MOVE_SEARCH_BACKWARD
:
224 const char *pattern
= va_arg(ap
, char*);
225 if (text_regex_compile(vis
->search_pattern
, pattern
, REG_EXTENDED
)) {
229 if (motion
== VIS_MOVE_SEARCH_FORWARD
)
230 motion
= VIS_MOVE_SEARCH_NEXT
;
232 motion
= VIS_MOVE_SEARCH_PREV
;
235 case VIS_MOVE_RIGHT_TO
:
236 case VIS_MOVE_LEFT_TO
:
237 case VIS_MOVE_RIGHT_TILL
:
238 case VIS_MOVE_LEFT_TILL
:
240 const char *key
= va_arg(ap
, char*);
243 strncpy(vis
->search_char
, key
, sizeof(vis
->search_char
));
244 vis
->search_char
[sizeof(vis
->search_char
)-1] = '\0';
245 vis
->last_totill
= motion
;
248 case VIS_MOVE_TOTILL_REPEAT
:
249 if (!vis
->last_totill
)
251 motion
= vis
->last_totill
;
253 case VIS_MOVE_TOTILL_REVERSE
:
254 switch (vis
->last_totill
) {
255 case VIS_MOVE_RIGHT_TO
:
256 motion
= VIS_MOVE_LEFT_TO
;
258 case VIS_MOVE_LEFT_TO
:
259 motion
= VIS_MOVE_RIGHT_TO
;
261 case VIS_MOVE_RIGHT_TILL
:
262 motion
= VIS_MOVE_LEFT_TILL
;
264 case VIS_MOVE_LEFT_TILL
:
265 motion
= VIS_MOVE_RIGHT_TILL
;
272 case VIS_MOVE_MARK_LINE
:
274 int mark
= va_arg(ap
, int);
275 if (VIS_MARK_a
<= mark
&& mark
< VIS_MARK_INVALID
)
276 vis
->action
.mark
= mark
;
285 vis
->action
.movement
= &vis_motions
[motion
];
287 action_do(vis
, &vis
->action
);
294 Movement vis_motions
[] = {
295 [VIS_MOVE_LINE_UP
] = { .cur
= view_line_up
, .type
= LINEWISE
},
296 [VIS_MOVE_LINE_DOWN
] = { .cur
= view_line_down
, .type
= LINEWISE
},
297 [VIS_MOVE_SCREEN_LINE_UP
] = { .cur
= view_screenline_up
, },
298 [VIS_MOVE_SCREEN_LINE_DOWN
] = { .cur
= view_screenline_down
, },
299 [VIS_MOVE_SCREEN_LINE_BEGIN
] = { .cur
= view_screenline_begin
, .type
= CHARWISE
},
300 [VIS_MOVE_SCREEN_LINE_MIDDLE
] = { .cur
= view_screenline_middle
, .type
= CHARWISE
},
301 [VIS_MOVE_SCREEN_LINE_END
] = { .cur
= view_screenline_end
, .type
= CHARWISE
|INCLUSIVE
},
302 [VIS_MOVE_LINE_PREV
] = { .txt
= text_line_prev
, },
303 [VIS_MOVE_LINE_BEGIN
] = { .txt
= text_line_begin
, },
304 [VIS_MOVE_LINE_START
] = { .txt
= text_line_start
, },
305 [VIS_MOVE_LINE_FINISH
] = { .txt
= text_line_finish
, .type
= INCLUSIVE
},
306 [VIS_MOVE_LINE_LASTCHAR
] = { .txt
= text_line_lastchar
, .type
= INCLUSIVE
},
307 [VIS_MOVE_LINE_END
] = { .txt
= text_line_end
, },
308 [VIS_MOVE_LINE_NEXT
] = { .txt
= text_line_next
, },
309 [VIS_MOVE_LINE
] = { .vis
= line
, .type
= LINEWISE
|IDEMPOTENT
|JUMP
},
310 [VIS_MOVE_COLUMN
] = { .vis
= column
, .type
= CHARWISE
|IDEMPOTENT
},
311 [VIS_MOVE_CHAR_PREV
] = { .txt
= text_char_prev
, .type
= CHARWISE
},
312 [VIS_MOVE_CHAR_NEXT
] = { .txt
= text_char_next
, .type
= CHARWISE
},
313 [VIS_MOVE_LINE_CHAR_PREV
] = { .txt
= text_line_char_prev
, .type
= CHARWISE
},
314 [VIS_MOVE_LINE_CHAR_NEXT
] = { .txt
= text_line_char_next
, .type
= CHARWISE
},
315 [VIS_MOVE_WORD_START_PREV
] = { .txt
= text_word_start_prev
, .type
= CHARWISE
},
316 [VIS_MOVE_WORD_START_NEXT
] = { .txt
= text_word_start_next
, .type
= CHARWISE
},
317 [VIS_MOVE_WORD_END_PREV
] = { .txt
= text_word_end_prev
, .type
= CHARWISE
|INCLUSIVE
},
318 [VIS_MOVE_WORD_END_NEXT
] = { .txt
= text_word_end_next
, .type
= CHARWISE
|INCLUSIVE
},
319 [VIS_MOVE_LONGWORD_START_PREV
] = { .txt
= text_longword_start_prev
, .type
= CHARWISE
},
320 [VIS_MOVE_LONGWORD_START_NEXT
] = { .txt
= text_longword_start_next
, .type
= CHARWISE
},
321 [VIS_MOVE_LONGWORD_END_PREV
] = { .txt
= text_longword_end_prev
, .type
= CHARWISE
|INCLUSIVE
},
322 [VIS_MOVE_LONGWORD_END_NEXT
] = { .txt
= text_longword_end_next
, .type
= CHARWISE
|INCLUSIVE
},
323 [VIS_MOVE_SENTENCE_PREV
] = { .txt
= text_sentence_prev
, .type
= CHARWISE
},
324 [VIS_MOVE_SENTENCE_NEXT
] = { .txt
= text_sentence_next
, .type
= CHARWISE
},
325 [VIS_MOVE_PARAGRAPH_PREV
] = { .txt
= text_paragraph_prev
, .type
= LINEWISE
|JUMP
},
326 [VIS_MOVE_PARAGRAPH_NEXT
] = { .txt
= text_paragraph_next
, .type
= LINEWISE
|JUMP
},
327 [VIS_MOVE_FUNCTION_START_PREV
] = { .txt
= text_function_start_prev
, .type
= LINEWISE
|JUMP
},
328 [VIS_MOVE_FUNCTION_START_NEXT
] = { .txt
= text_function_start_next
, .type
= LINEWISE
|JUMP
},
329 [VIS_MOVE_FUNCTION_END_PREV
] = { .txt
= text_function_end_prev
, .type
= LINEWISE
|JUMP
},
330 [VIS_MOVE_FUNCTION_END_NEXT
] = { .txt
= text_function_end_next
, .type
= LINEWISE
|JUMP
},
331 [VIS_MOVE_BRACKET_MATCH
] = { .txt
= bracket_match
, .type
= INCLUSIVE
|JUMP
},
332 [VIS_MOVE_FILE_BEGIN
] = { .txt
= text_begin
, .type
= LINEWISE
|JUMP
},
333 [VIS_MOVE_FILE_END
] = { .txt
= text_end
, .type
= LINEWISE
|JUMP
},
334 [VIS_MOVE_LEFT_TO
] = { .vis
= to_left
, },
335 [VIS_MOVE_RIGHT_TO
] = { .vis
= to
, .type
= INCLUSIVE
},
336 [VIS_MOVE_LEFT_TILL
] = { .vis
= till_left
, },
337 [VIS_MOVE_RIGHT_TILL
] = { .vis
= till
, .type
= INCLUSIVE
},
338 [VIS_MOVE_MARK
] = { .file
= mark_goto
, .type
= JUMP
|IDEMPOTENT
},
339 [VIS_MOVE_MARK_LINE
] = { .file
= mark_line_goto
, .type
= LINEWISE
|JUMP
|IDEMPOTENT
},
340 [VIS_MOVE_SEARCH_WORD_FORWARD
] = { .vis
= search_word_forward
, .type
= JUMP
},
341 [VIS_MOVE_SEARCH_WORD_BACKWARD
]= { .vis
= search_word_backward
, .type
= JUMP
},
342 [VIS_MOVE_SEARCH_NEXT
] = { .vis
= search_forward
, .type
= JUMP
},
343 [VIS_MOVE_SEARCH_PREV
] = { .vis
= search_backward
, .type
= JUMP
},
344 [VIS_MOVE_WINDOW_LINE_TOP
] = { .view
= view_lines_top
, .type
= LINEWISE
|JUMP
|IDEMPOTENT
},
345 [VIS_MOVE_WINDOW_LINE_MIDDLE
] = { .view
= view_lines_middle
, .type
= LINEWISE
|JUMP
|IDEMPOTENT
},
346 [VIS_MOVE_WINDOW_LINE_BOTTOM
] = { .view
= view_lines_bottom
, .type
= LINEWISE
|JUMP
|IDEMPOTENT
},
347 [VIS_MOVE_CHANGELIST_NEXT
] = { .win
= window_changelist_next
, .type
= INCLUSIVE
},
348 [VIS_MOVE_CHANGELIST_PREV
] = { .win
= window_changelist_prev
, .type
= INCLUSIVE
},
349 [VIS_MOVE_JUMPLIST_NEXT
] = { .win
= window_jumplist_next
, .type
= INCLUSIVE
},
350 [VIS_MOVE_JUMPLIST_PREV
] = { .win
= window_jumplist_prev
, .type
= INCLUSIVE
},
351 [VIS_MOVE_NOP
] = { .win
= window_nop
, .type
= IDEMPOTENT
},
352 [VIS_MOVE_PERCENT
] = { .vis
= percent
, .type
= IDEMPOTENT
},