Line | Branch | Exec | Source |
---|---|---|---|
1 | #include "move.h" | ||
2 | #include "buffer.h" | ||
3 | #include "indent.h" | ||
4 | #include "util/ascii.h" | ||
5 | #include "util/debug.h" | ||
6 | #include "util/macros.h" | ||
7 | #include "util/utf8.h" | ||
8 | |||
9 | typedef enum { | ||
10 | CT_SPACE, | ||
11 | CT_NEWLINE, | ||
12 | CT_WORD, | ||
13 | CT_OTHER, | ||
14 | } CharTypeEnum; | ||
15 | |||
16 | 59 | void move_to_preferred_x(View *view, long preferred_x) | |
17 | { | ||
18 | 59 | const LocalOptions *options = &view->buffer->options; | |
19 | 59 | view->preferred_x = preferred_x; | |
20 | 59 | block_iter_bol(&view->cursor); | |
21 | 59 | StringView line = block_iter_get_line(&view->cursor); | |
22 | |||
23 |
4/4✓ Branch 0 (4→5) taken 24 times.
✓ Branch 1 (4→12) taken 35 times.
✓ Branch 2 (5→6) taken 9 times.
✓ Branch 3 (5→12) taken 15 times.
|
59 | if (options->emulate_tab && view->preferred_x < line.length) { |
24 | 9 | const size_t iw = options->indent_width; | |
25 | 9 | const size_t ilevel = indent_level(view->preferred_x, iw); | |
26 |
3/4✓ Branch 0 (10→11) taken 77 times.
✗ Branch 1 (10→12) not taken.
✓ Branch 2 (11→7) taken 68 times.
✓ Branch 3 (11→12) taken 9 times.
|
77 | for (size_t i = 0; i < line.length && line.data[i] == ' '; i++) { |
27 |
1/2✗ Branch 0 (7→8) not taken.
✓ Branch 1 (7→10) taken 68 times.
|
68 | if (i + 1 == (ilevel + 1) * iw) { |
28 | // Force cursor to beginning of the indentation level | ||
29 | ✗ | view->cursor.offset += ilevel * iw; | |
30 | ✗ | return; | |
31 | } | ||
32 | } | ||
33 | } | ||
34 | |||
35 | 59 | const unsigned int tw = options->tab_width; | |
36 | 59 | unsigned long x = 0; | |
37 | 59 | size_t i = 0; | |
38 |
4/4✓ Branch 0 (24→25) taken 283 times.
✓ Branch 1 (24→26) taken 40 times.
✓ Branch 2 (25→13) taken 264 times.
✓ Branch 3 (25→26) taken 19 times.
|
323 | while (x < view->preferred_x && i < line.length) { |
39 | 264 | CodePoint u = line.data[i++]; | |
40 |
1/2✓ Branch 0 (13→14) taken 264 times.
✗ Branch 1 (13→20) not taken.
|
264 | if (likely(u < 0x80)) { |
41 |
2/2✓ Branch 0 (14→15) taken 262 times.
✓ Branch 1 (14→16) taken 2 times.
|
264 | if (likely(!ascii_iscntrl(u))) { |
42 | 262 | x++; | |
43 |
1/2✓ Branch 0 (16→17) taken 2 times.
✗ Branch 1 (16→18) not taken.
|
2 | } else if (u == '\t') { |
44 | 2 | x = next_indent_width(x, tw); | |
45 | ✗ | } else if (u == '\n') { | |
46 | break; | ||
47 | } else { | ||
48 | ✗ | x += 2; | |
49 | } | ||
50 | } else { | ||
51 | ✗ | const size_t next = i; | |
52 | ✗ | i--; | |
53 | ✗ | u = u_get_nonascii(line.data, line.length, &i); | |
54 | ✗ | x += u_char_width(u); | |
55 | ✗ | if (x > view->preferred_x) { | |
56 | ✗ | i = next; | |
57 | ✗ | break; | |
58 | } | ||
59 | } | ||
60 | } | ||
61 |
1/2✗ Branch 0 (26→27) not taken.
✓ Branch 1 (26→28) taken 59 times.
|
59 | if (x > view->preferred_x) { |
62 | ✗ | i--; | |
63 | } | ||
64 | 59 | view->cursor.offset += i; | |
65 | |||
66 | // If cursor stopped on a zero-width char, move to the next spacing char | ||
67 | 59 | CodePoint u; | |
68 |
3/4✓ Branch 0 (29→30) taken 55 times.
✓ Branch 1 (29→33) taken 4 times.
✗ Branch 2 (31→32) not taken.
✓ Branch 3 (31→33) taken 55 times.
|
59 | if (block_iter_get_char(&view->cursor, &u) && u_is_zero_width(u)) { |
69 | ✗ | block_iter_next_column(&view->cursor); | |
70 | } | ||
71 | } | ||
72 | |||
73 | 16 | void move_cursor_left(View *view) | |
74 | { | ||
75 | 16 | const LocalOptions *options = &view->buffer->options; | |
76 |
2/2✓ Branch 0 (2→3) taken 2 times.
✓ Branch 1 (2→8) taken 14 times.
|
16 | if (options->emulate_tab) { |
77 | 2 | size_t size = get_indent_level_bytes_left(options, &view->cursor); | |
78 |
1/2✗ Branch 0 (4→5) not taken.
✓ Branch 1 (4→8) taken 2 times.
|
2 | if (size) { |
79 | ✗ | block_iter_back_bytes(&view->cursor, size); | |
80 | ✗ | view_reset_preferred_x(view); | |
81 | ✗ | return; | |
82 | } | ||
83 | } | ||
84 | 16 | block_iter_prev_column(&view->cursor); | |
85 | 16 | view_reset_preferred_x(view); | |
86 | } | ||
87 | |||
88 | 31 | void move_cursor_right(View *view) | |
89 | { | ||
90 | 31 | const LocalOptions *options = &view->buffer->options; | |
91 |
2/2✓ Branch 0 (2→3) taken 15 times.
✓ Branch 1 (2→8) taken 16 times.
|
31 | if (options->emulate_tab) { |
92 | 15 | size_t size = get_indent_level_bytes_right(options, &view->cursor); | |
93 |
1/2✗ Branch 0 (4→5) not taken.
✓ Branch 1 (4→8) taken 15 times.
|
15 | if (size) { |
94 | ✗ | block_iter_skip_bytes(&view->cursor, size); | |
95 | ✗ | view_reset_preferred_x(view); | |
96 | ✗ | return; | |
97 | } | ||
98 | } | ||
99 | 31 | block_iter_next_column(&view->cursor); | |
100 | 31 | view_reset_preferred_x(view); | |
101 | } | ||
102 | |||
103 | 7 | void move_bol(View *view) | |
104 | { | ||
105 | 7 | block_iter_bol(&view->cursor); | |
106 | 7 | view_reset_preferred_x(view); | |
107 | 7 | } | |
108 | |||
109 | 7 | void move_bol_smart(View *view, SmartBolType type) | |
110 | { | ||
111 |
1/2✓ Branch 0 (2→3) taken 7 times.
✗ Branch 1 (2→5) not taken.
|
7 | if (type == BOL_SIMPLE) { |
112 | 7 | move_bol(view); | |
113 | 7 | return; | |
114 | } | ||
115 | |||
116 | ✗ | StringView line; | |
117 | ✗ | size_t cursor_offset = fetch_this_line(&view->cursor, &line); | |
118 | ✗ | bool at_bol = (cursor_offset == 0); | |
119 | ✗ | if (at_bol && type == BOL_INDENT) { | |
120 | // At BOL and not using toggle; nothing to do | ||
121 | ✗ | goto out; | |
122 | } | ||
123 | |||
124 | ✗ | size_t indent = ascii_blank_prefix_length(line.data, line.length); | |
125 | ✗ | if (at_bol) { | |
126 | // At BOL and using toggle; move right to first non-blank char | ||
127 | ✗ | block_iter_skip_bytes(&view->cursor, indent); | |
128 | ✗ | goto out; | |
129 | } | ||
130 | |||
131 | // Not at BOL; move left (either to BOL or leftmost non-blank) depending on | ||
132 | // `type` and whether the cursor is before or after the leftmost non-blank | ||
133 | ✗ | size_t co = cursor_offset; | |
134 | ✗ | size_t move = (co > indent && type != BOL_TOGGLE_LR) ? co - indent : co; | |
135 | ✗ | block_iter_back_bytes(&view->cursor, move); | |
136 | |||
137 | ✗ | out: | |
138 | ✗ | view_reset_preferred_x(view); | |
139 | } | ||
140 | |||
141 | 6 | void move_eol(View *view) | |
142 | { | ||
143 | 6 | block_iter_eol(&view->cursor); | |
144 | 6 | view_reset_preferred_x(view); | |
145 | 6 | } | |
146 | |||
147 | 19 | void move_up(View *view, long count) | |
148 | { | ||
149 | 19 | const long x = view_get_preferred_x(view); | |
150 |
2/2✓ Branch 0 (7→4) taken 20 times.
✓ Branch 1 (7→8) taken 16 times.
|
36 | while (count > 0) { |
151 |
2/2✓ Branch 0 (5→6) taken 17 times.
✓ Branch 1 (5→8) taken 3 times.
|
20 | if (!block_iter_prev_line(&view->cursor)) { |
152 | break; | ||
153 | } | ||
154 | 17 | count--; | |
155 | } | ||
156 | 19 | move_to_preferred_x(view, x); | |
157 | 19 | } | |
158 | |||
159 | 15 | void move_down(View *view, long count) | |
160 | { | ||
161 | 15 | const long x = view_get_preferred_x(view); | |
162 |
2/2✓ Branch 0 (7→4) taken 12 times.
✓ Branch 1 (7→8) taken 15 times.
|
27 | while (count > 0) { |
163 |
1/2✓ Branch 0 (5→6) taken 12 times.
✗ Branch 1 (5→8) not taken.
|
12 | if (!block_iter_eat_line(&view->cursor)) { |
164 | break; | ||
165 | } | ||
166 | 12 | count--; | |
167 | } | ||
168 | 15 | move_to_preferred_x(view, x); | |
169 | 15 | } | |
170 | |||
171 | 7 | void move_bof(View *view) | |
172 | { | ||
173 | 7 | block_iter_bof(&view->cursor); | |
174 | 7 | view_reset_preferred_x(view); | |
175 | 7 | } | |
176 | |||
177 | 6 | void move_eof(View *view) | |
178 | { | ||
179 | 6 | block_iter_eof(&view->cursor); | |
180 | 6 | view_reset_preferred_x(view); | |
181 | 6 | } | |
182 | |||
183 | 3 | void move_to_line(View *view, size_t line) | |
184 | { | ||
185 | 3 | BUG_ON(line == 0); | |
186 | 3 | view->center_on_scroll = true; | |
187 | 3 | block_iter_goto_line(&view->cursor, line - 1); | |
188 | 3 | } | |
189 | |||
190 | ✗ | void move_to_column(View *view, size_t column) | |
191 | { | ||
192 | ✗ | BUG_ON(column == 0); | |
193 | ✗ | block_iter_bol(&view->cursor); | |
194 | ✗ | while (column-- > 1) { | |
195 | ✗ | CodePoint u; | |
196 | ✗ | if (!block_iter_next_char(&view->cursor, &u)) { | |
197 | break; | ||
198 | } | ||
199 | ✗ | if (u == '\n') { | |
200 | ✗ | block_iter_prev_char(&view->cursor, &u); | |
201 | ✗ | break; | |
202 | } | ||
203 | } | ||
204 | ✗ | view_reset_preferred_x(view); | |
205 | ✗ | } | |
206 | |||
207 | 2 | void move_to_filepos(View *view, size_t line, size_t column) | |
208 | { | ||
209 | 2 | move_to_line(view, line); | |
210 | 2 | BUG_ON(!block_iter_is_bol(&view->cursor)); | |
211 |
1/2✗ Branch 0 (5→6) not taken.
✓ Branch 1 (5→7) taken 2 times.
|
2 | if (column != 1) { |
212 | ✗ | move_to_column(view, column); | |
213 | } | ||
214 | 2 | view_reset_preferred_x(view); | |
215 | 2 | } | |
216 | |||
217 | 61 | static CharTypeEnum get_char_type(CodePoint u) | |
218 | { | ||
219 |
2/2✓ Branch 0 (2→3) taken 53 times.
✓ Branch 1 (2→6) taken 8 times.
|
61 | if (u == '\n') { |
220 | return CT_NEWLINE; | ||
221 | } | ||
222 |
2/2✓ Branch 0 (3→4) taken 47 times.
✓ Branch 1 (3→6) taken 6 times.
|
53 | if (u_is_breakable_whitespace(u)) { |
223 | return CT_SPACE; | ||
224 | } | ||
225 |
1/2✓ Branch 0 (4→5) taken 47 times.
✗ Branch 1 (4→6) not taken.
|
47 | if (u_is_word_char(u)) { |
226 | 47 | return CT_WORD; | |
227 | } | ||
228 | return CT_OTHER; | ||
229 | } | ||
230 | |||
231 | 10 | static bool get_current_char_type(BlockIter *bi, CharTypeEnum *type) | |
232 | { | ||
233 | 10 | CodePoint u; | |
234 |
1/2✓ Branch 0 (3→4) taken 10 times.
✗ Branch 1 (3→5) not taken.
|
10 | if (!block_iter_get_char(bi, &u)) { |
235 | return false; | ||
236 | } | ||
237 | |||
238 | 10 | *type = get_char_type(u); | |
239 | 10 | return true; | |
240 | } | ||
241 | |||
242 | 15 | static size_t skip_fwd_char_type(BlockIter *bi, CharTypeEnum type) | |
243 | { | ||
244 | 15 | size_t count = 0; | |
245 | 15 | CodePoint u; | |
246 |
1/2✓ Branch 0 (8→3) taken 45 times.
✗ Branch 1 (8→9) not taken.
|
45 | while (block_iter_next_char(bi, &u)) { |
247 |
2/2✓ Branch 0 (3→4) taken 15 times.
✓ Branch 1 (3→6) taken 30 times.
|
45 | if (get_char_type(u) != type) { |
248 | 15 | block_iter_prev_char(bi, &u); | |
249 | 15 | break; | |
250 | } | ||
251 | 30 | count += u_char_size(u); | |
252 | } | ||
253 | 15 | return count; | |
254 | } | ||
255 | |||
256 | 5 | static size_t skip_bwd_char_type(BlockIter *bi, CharTypeEnum type) | |
257 | { | ||
258 | 5 | size_t count = 0; | |
259 | 5 | CodePoint u; | |
260 |
2/2✓ Branch 0 (8→3) taken 4 times.
✓ Branch 1 (8→9) taken 2 times.
|
6 | while (block_iter_prev_char(bi, &u)) { |
261 |
2/2✓ Branch 0 (3→4) taken 3 times.
✓ Branch 1 (3→6) taken 1 times.
|
4 | if (get_char_type(u) != type) { |
262 | 3 | block_iter_next_char(bi, &u); | |
263 | 3 | break; | |
264 | } | ||
265 | 1 | count += u_char_size(u); | |
266 | } | ||
267 | 5 | return count; | |
268 | } | ||
269 | |||
270 | 5 | size_t word_fwd(BlockIter *bi, bool skip_non_word) | |
271 | { | ||
272 | 5 | size_t count = 0; | |
273 | 10 | CharTypeEnum type; | |
274 | |||
275 | 15 | while (1) { | |
276 | 10 | count += skip_fwd_char_type(bi, CT_SPACE); | |
277 |
1/2✓ Branch 0 (5→6) taken 10 times.
✗ Branch 1 (5→11) not taken.
|
10 | if (!get_current_char_type(bi, &type)) { |
278 | return count; | ||
279 | } | ||
280 | |||
281 |
2/2✓ Branch 0 (6→7) taken 5 times.
✓ Branch 1 (6→9) taken 5 times.
|
10 | if ( |
282 | count | ||
283 |
3/4✓ Branch 0 (7→8) taken 3 times.
✓ Branch 1 (7→11) taken 2 times.
✗ Branch 2 (8→9) not taken.
✓ Branch 3 (8→11) taken 3 times.
|
5 | && (!skip_non_word || (type == CT_WORD || type == CT_NEWLINE)) |
284 | ) { | ||
285 | return count; | ||
286 | } | ||
287 | |||
288 | 5 | count += skip_fwd_char_type(bi, type); | |
289 | } | ||
290 | } | ||
291 | |||
292 | 3 | size_t word_bwd(BlockIter *bi, bool skip_non_word) | |
293 | { | ||
294 | 3 | size_t count = 0; | |
295 | 3 | CharTypeEnum type; | |
296 | 3 | CodePoint u; | |
297 | |||
298 | 3 | do { | |
299 | 3 | count += skip_bwd_char_type(bi, CT_SPACE); | |
300 |
2/2✓ Branch 0 (5→6) taken 2 times.
✓ Branch 1 (5→9) taken 1 times.
|
3 | if (!block_iter_prev_char(bi, &u)) { |
301 | return count; | ||
302 | } | ||
303 | |||
304 | 2 | type = get_char_type(u); | |
305 | 2 | count += u_char_size(u); | |
306 | 2 | count += skip_bwd_char_type(bi, type); | |
307 |
1/4✗ Branch 0 (7→8) not taken.
✓ Branch 1 (7→9) taken 2 times.
✗ Branch 2 (8→3) not taken.
✗ Branch 3 (8→9) not taken.
|
2 | } while (skip_non_word && type != CT_WORD && type != CT_NEWLINE); |
308 | return count; | ||
309 | } | ||
310 |