dte test coverage


Directory: ./
File: src/view.c
Date: 2025-07-19 20:13:10
Exec Total Coverage
Lines: 116 162 71.6%
Functions: 12 13 92.3%
Branches: 44 98 44.9%

Line Branch Exec Source
1 #include "view.h"
2 #include "block.h"
3 #include "buffer.h"
4 #include "indent.h"
5 #include "util/ascii.h"
6 #include "util/debug.h"
7 #include "util/numtostr.h"
8 #include "util/str-util.h"
9 #include "util/time-util.h"
10 #include "util/utf8.h"
11 #include "window.h"
12
13 475 void view_update_cursor_y(View *view)
14 {
15 475 const Buffer *buffer = view->buffer;
16 475 const Block *blk;
17 475 size_t nl = 0;
18
1/2
✓ Branch 0 (7→3) taken 475 times.
✗ Branch 1 (7→8) not taken.
475 block_for_each(blk, &buffer->blocks) {
19
1/2
✓ Branch 0 (3→4) taken 475 times.
✗ Branch 1 (3→6) not taken.
475 if (blk == view->cursor.blk) {
20 475 nl += count_nl(blk->data, view->cursor.offset);
21 475 view->cy = nl;
22 475 return;
23 }
24 nl += blk->nl;
25 }
26 BUG("unreachable");
27 }
28
29 29 void view_update_cursor_x(View *view)
30 {
31 29 StringView line;
32 29 const unsigned int tw = view->buffer->options.tab_width;
33 29 const size_t cx = get_current_line_and_offset(&view->cursor, &line);
34 29 long cx_char = 0;
35 29 long w = 0;
36
37
2/2
✓ Branch 0 (14→4) taken 79 times.
✓ Branch 1 (14→15) taken 29 times.
108 for (size_t idx = 0; idx < cx; cx_char++) {
38 79 unsigned char ch = line.data[idx];
39
1/2
✓ Branch 0 (4→5) taken 79 times.
✗ Branch 1 (4→11) not taken.
79 if (likely(ch < 0x80)) {
40 79 idx++;
41
1/2
✓ Branch 0 (5→6) taken 79 times.
✗ Branch 1 (5→7) not taken.
79 if (likely(!ascii_iscntrl(ch))) {
42 79 w++;
43 } else if (ch == '\t') {
44 w = next_indent_width(w, tw);
45 } else {
46 w += 2;
47 }
48 } else {
49 CodePoint u = u_get_nonascii(line.data, line.length, &idx);
50 w += u_char_width(u);
51 }
52 }
53
54 29 view->cx = cx;
55 29 view->cx_char = cx_char;
56 29 view->cx_display = w;
57 29 }
58
59 1 static bool view_is_cursor_visible(const View *v)
60 {
61
2/4
✓ Branch 0 (2→3) taken 1 times.
✗ Branch 1 (2→5) not taken.
✗ Branch 2 (3→4) not taken.
✓ Branch 3 (3→5) taken 1 times.
1 return v->cy < v->vy || v->cy > v->vy + v->window->edit_h - 1;
62 }
63
64 1 static void view_center_to_cursor(View *v)
65 {
66 1 size_t lines = v->buffer->nl;
67 1 Window *window = v->window;
68 1 unsigned int hh = window->edit_h / 2;
69
70
2/4
✓ Branch 0 (2→3) taken 1 times.
✗ Branch 1 (2→4) not taken.
✗ Branch 2 (3→4) not taken.
✓ Branch 3 (3→5) taken 1 times.
1 if (window->edit_h >= lines || v->cy < hh) {
71 v->vy = 0;
72 return;
73 }
74
75 1 v->vy = v->cy - hh;
76
1/2
✗ Branch 0 (5→6) not taken.
✓ Branch 1 (5→7) taken 1 times.
1 if (v->vy + window->edit_h > lines) {
77 // -1 makes one ~ line visible so that you know where the EOF is
78 v->vy -= v->vy + window->edit_h - lines - 1;
79 }
80 }
81
82 1 static void view_update_vx(View *v)
83 {
84 1 Window *window = v->window;
85 1 unsigned int c = 8;
86
87
1/2
✗ Branch 0 (2→3) not taken.
✓ Branch 1 (2→4) taken 1 times.
1 if (v->cx_display - v->vx >= window->edit_w) {
88 v->vx = (v->cx_display - window->edit_w + c) / c * c;
89 }
90
1/2
✗ Branch 0 (4→5) not taken.
✓ Branch 1 (4→6) taken 1 times.
1 if (v->cx_display < v->vx) {
91 v->vx = v->cx_display / c * c;
92 }
93 1 }
94
95 static void view_update_vy(View *v)
96 {
97 Window *window = v->window;
98 unsigned int margin = window_get_scroll_margin(window);
99 long max_y = v->vy + window->edit_h - 1 - margin;
100
101 if (v->cy < v->vy + margin) {
102 v->vy = MAX(v->cy - margin, 0);
103 } else if (v->cy > max_y) {
104 v->vy += v->cy - max_y;
105 max_y = v->buffer->nl - window->edit_h + 1;
106 if (v->vy > max_y && max_y >= 0) {
107 v->vy = max_y;
108 }
109 }
110 }
111
112 1 void view_update(View *v)
113 {
114 1 view_update_cursor_x(v);
115 1 view_update_cursor_y(v);
116 1 view_update_vx(v);
117
3/6
✓ Branch 0 (5→6) taken 1 times.
✗ Branch 1 (5→8) not taken.
✓ Branch 2 (6→7) taken 1 times.
✗ Branch 3 (6→9) not taken.
✓ Branch 4 (7→8) taken 1 times.
✗ Branch 5 (7→9) not taken.
1 if (v->force_center || (v->center_on_scroll && view_is_cursor_visible(v))) {
118 1 view_center_to_cursor(v);
119 } else {
120 view_update_vy(v);
121 }
122 1 v->force_center = false;
123 1 v->center_on_scroll = false;
124 1 }
125
126 60 long view_get_preferred_x(View *v)
127 {
128
2/2
✓ Branch 0 (2→3) taken 28 times.
✓ Branch 1 (2→5) taken 32 times.
60 if (v->preferred_x < 0) {
129 28 view_update_cursor_x(v);
130 28 v->preferred_x = v->cx_display;
131 }
132 60 return v->preferred_x;
133 }
134
135 24 bool view_can_close(const View *view)
136 {
137 24 const Buffer *buffer = view->buffer;
138
4/4
✓ Branch 0 (2→3) taken 5 times.
✓ Branch 1 (2→5) taken 19 times.
✓ Branch 2 (3→4) taken 4 times.
✓ Branch 3 (3→5) taken 1 times.
24 return !buffer_modified(buffer) || buffer->views.count > 1;
139 }
140
141 // Like window_remove_view(), but searching for the index of `view` in
142 // `view->window->views` by pointer
143 26 size_t view_remove(View *view)
144 {
145 26 Window *window = view->window;
146 26 size_t view_idx = ptr_array_index(&window->views, view);
147 26 window_remove_view_at_index(window, view_idx);
148 26 return view_idx;
149 }
150
151 4 size_t get_bounds_for_word_under_cursor(StringView line, size_t *cursor_offset)
152 {
153 // Move right, until over a word char (if not already)
154 4 size_t si = *cursor_offset;
155
1/2
✓ Branch 0 (7→3) taken 4 times.
✗ Branch 1 (7→8) not taken.
4 while (si < line.length) {
156 4 size_t i = si;
157
1/2
✗ Branch 0 (4→5) not taken.
✓ Branch 1 (4→6) taken 4 times.
4 if (u_is_word_char(u_get_char(line.data, line.length, &i))) {
158 break;
159 }
160 si = i;
161 }
162
163
1/2
✓ Branch 0 (8→13) taken 4 times.
✗ Branch 1 (8→24) not taken.
4 if (si == line.length) {
164 // No word char between cursor and EOL; no word
165 return 0;
166 }
167
168 // Move left, to start of word (if cursor is already within one)
169 8 size_t ei = si;
170
2/2
✓ Branch 0 (13→9) taken 4 times.
✓ Branch 1 (13→18) taken 4 times.
8 while (si > 0) {
171 4 size_t i = si;
172
1/2
✓ Branch 0 (10→11) taken 4 times.
✗ Branch 1 (10→12) not taken.
4 if (!u_is_word_char(u_prev_char(line.data, &i))) {
173 break;
174 }
175 4 si = i;
176 }
177
178 // Move right, to end of word
179
2/2
✓ Branch 0 (19→14) taken 4114 times.
✓ Branch 1 (19→20) taken 4 times.
4118 while (ei < line.length) {
180 4114 size_t i = ei;
181
1/2
✓ Branch 0 (15→16) taken 4114 times.
✗ Branch 1 (15→17) not taken.
4114 if (!u_is_word_char(u_get_char(line.data, line.length, &i))) {
182 break;
183 }
184 4114 ei = i;
185 }
186
187
1/2
✓ Branch 0 (20→21) taken 4 times.
✗ Branch 1 (20→24) not taken.
4 if (si == ei) {
188 // Zero length; no word
189 return 0;
190 }
191
192 // Update `cursor_offset` with start offset and return end offset
193 4 BUG_ON(ei == 0 || si >= ei);
194 4 *cursor_offset = si;
195 4 return ei;
196 }
197
198 4 StringView view_get_word_under_cursor(const View *view)
199 {
200 4 StringView line;
201 4 size_t cursor_offset_in_line = get_current_line_and_offset(&view->cursor, &line);
202 4 size_t start = cursor_offset_in_line;
203 4 size_t end = get_bounds_for_word_under_cursor(line, &start);
204
1/2
✓ Branch 0 (4→5) taken 4 times.
✗ Branch 1 (4→6) not taken.
4 return string_view(line.data + start, end ? end - start : 0);
205 }
206
207 1 String dump_buffer(const View *view)
208 {
209 1 const Buffer *buffer = view->buffer;
210 1 uintmax_t counts[2];
211 1 char sizestr[FILESIZE_STR_MAX];
212 1 buffer_count_blocks_and_bytes(buffer, counts);
213 1 BUG_ON(counts[0] < 1);
214 1 BUG_ON(!buffer->setup);
215 1 String buf = string_new(1024 + (DEBUG ? 24 * counts[0] : 0));
216
217 2 string_sprintf (
218 &buf,
219 "%s %s\n%s %lu\n%s %s\n%s %s\n%s %ju\n%s %zu\n%s %s\n",
220 " Name:", buffer_filename(buffer),
221 1 " ID:", buffer->id,
222 1 " Encoding:", buffer->encoding,
223 1 " Filetype:", buffer->options.filetype,
224 " Blocks:", counts[0],
225 1 " Lines:", buffer->nl,
226 " Size:", filesize_to_str(counts[1], sizestr)
227
228 );
229
230 1 if (
231
3/6
✓ Branch 0 (11→12) taken 1 times.
✗ Branch 1 (11→17) not taken.
✓ Branch 2 (12→13) taken 1 times.
✗ Branch 3 (12→17) not taken.
✓ Branch 4 (13→14) taken 1 times.
✗ Branch 5 (13→17) not taken.
1 buffer->stdout_buffer || buffer->temporary || buffer->readonly
232
3/6
✓ Branch 0 (14→15) taken 1 times.
✗ Branch 1 (14→17) not taken.
✓ Branch 2 (15→16) taken 1 times.
✗ Branch 3 (15→17) not taken.
✗ Branch 4 (16→17) not taken.
✓ Branch 5 (16→30) taken 1 times.
1 || buffer->locked || buffer->crlf_newlines || buffer->bom
233 ) {
234 string_sprintf (
235 &buf,
236 " Flags:%s%s%s%s%s%s\n",
237 buffer->stdout_buffer ? " STDOUT" : "",
238 buffer->temporary ? " TMP" : "",
239 buffer->readonly ? " RO" : "",
240 buffer->locked ? " LOCKED" : "",
241 buffer->crlf_newlines ? " CRLF" : "",
242 buffer->bom ? " BOM" : ""
243 );
244 }
245
246
1/2
✗ Branch 0 (30→31) not taken.
✓ Branch 1 (30→32) taken 1 times.
1 if (buffer->views.count > 1) {
247 string_sprintf(&buf, " Views: %zu\n", buffer->views.count);
248 }
249
250
1/2
✗ Branch 0 (32→33) not taken.
✓ Branch 1 (32→40) taken 1 times.
1 if (buffer->abs_filename) {
251 const FileInfo *file = &buffer->file;
252 const struct timespec *mtime = &file->mtime;
253 unsigned int perms = file->mode & 07777;
254 char tstr[TIME_STR_BUFSIZE];
255 char modestr[12];
256 string_sprintf (
257 &buf,
258 "\nLast stat:\n----------\n\n"
259 "%s %s\n%s %s\n%s -%s (%04o)\n%s %jd\n%s %jd\n%s %s\n%s %jd\n%s %ju\n",
260 " Path:", buffer->abs_filename,
261 " Modified:", timespec_to_str(mtime, tstr) ? tstr : "-",
262 " Mode:", file_permissions_to_str(file->mode, modestr), perms,
263 " User:", (intmax_t)file->uid,
264 " Group:", (intmax_t)file->gid,
265 " Size:", filesize_to_str(file->size, sizestr),
266 " Device:", (intmax_t)file->dev,
267 " Inode:", (uintmax_t)file->ino
268 );
269 }
270
271 1 if (DEBUG >= 1) {
272 1 const BlockIter *cursor = &view->cursor;
273 1 string_append_cstring(&buf, "\nBlocks:\n-------\n\n");
274 1 size_t i = 1;
275 1 const Block *b;
276
2/2
✓ Branch 0 (47→42) taken 1 times.
✓ Branch 1 (47→48) taken 1 times.
2 block_for_each(b, &buffer->blocks) {
277 1 string_sprintf(&buf, "%4zu: %zu/%zu nl=%zu", i++, b->size, b->alloc, b->nl);
278
1/2
✓ Branch 0 (43→44) taken 1 times.
✗ Branch 1 (43→45) not taken.
1 if (b == cursor->blk) {
279 1 string_sprintf(&buf, " (cursor; offset=%zu)", cursor->offset);
280 }
281 1 string_append_byte(&buf, '\n');
282 }
283 }
284
285 1 return buf;
286 }
287