dte test coverage


Directory: ./
File: src/move.c
Date: 2025-05-08 15:05:54
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/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