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