dte test coverage


Directory: ./
File: src/move.c
Date: 2025-09-07 23:01:39
Exec Total Coverage
Lines: 132 172 76.7%
Functions: 17 18 94.4%
Branches: 56 90 62.2%

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