dte test coverage


Directory: ./
File: src/view.c
Date: 2025-10-16 19:09:21
Exec Total Coverage
Lines: 117 164 71.3%
Functions: 12 13 92.3%
Branches: 45 100 45.0%

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 const unsigned int tw = view->buffer->options.tab_width;
32 29 const CurrentLineRef lr = get_current_line_and_offset(view->cursor);
33 29 const size_t cx = lr.cursor_offset;
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 = lr.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(lr.line.data, lr.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 long 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_xindex(&window->views, view);
147 26 window_remove_view_at_index(window, view_idx);
148 26 return view_idx;
149 }
150
151 4 WordBounds get_bounds_for_word_under_cursor(CurrentLineRef lr)
152 {
153 4 const size_t len = lr.line.length;
154 4 size_t si = lr.cursor_offset;
155 4 BUG_ON(si > len);
156
157 // Move right, until over a word char (if not already)
158
1/2
✓ Branch 0 (8→4) taken 4 times.
✗ Branch 1 (8→9) not taken.
4 while (si < len) {
159 4 size_t i = si;
160
1/2
✗ Branch 0 (5→6) not taken.
✓ Branch 1 (5→7) taken 4 times.
4 if (u_is_word_char(u_get_char(lr.line.data, len, &i))) {
161 break;
162 }
163 si = i;
164 }
165
166
1/2
✗ Branch 0 (9→10) not taken.
✓ Branch 1 (9→15) taken 4 times.
4 if (si == len) {
167 // No word char between cursor and EOL; no word
168 return (WordBounds){0};
169 }
170
171 // Move left, to start of word (if cursor is already within one)
172 8 size_t ei = si;
173
2/2
✓ Branch 0 (15→11) taken 4 times.
✓ Branch 1 (15→20) taken 4 times.
8 while (si) {
174 4 size_t i = si;
175
1/2
✓ Branch 0 (12→13) taken 4 times.
✗ Branch 1 (12→14) not taken.
4 if (!u_is_word_char(u_prev_char(lr.line.data, &i))) {
176 break;
177 }
178 4 si = i;
179 }
180
181 // Move right, to end of word
182
2/2
✓ Branch 0 (21→16) taken 4114 times.
✓ Branch 1 (21→22) taken 4 times.
4118 while (ei < len) {
183 4114 size_t i = ei;
184
1/2
✓ Branch 0 (17→18) taken 4114 times.
✗ Branch 1 (17→19) not taken.
4114 if (!u_is_word_char(u_get_char(lr.line.data, len, &i))) {
185 break;
186 }
187 4114 ei = i;
188 }
189
190 4 bool empty = (si == ei);
191 4 BUG_ON(si > ei);
192
193 4 return (WordBounds) {
194
1/2
✓ Branch 0 (24→25) taken 4 times.
✗ Branch 1 (24→26) not taken.
4 .start = empty ? 0 : si,
195
1/2
✓ Branch 0 (26→27) taken 4 times.
✗ Branch 1 (26→28) not taken.
4 .end = empty ? 0 : ei,
196 };
197 }
198
199 4 StringView view_get_word_under_cursor(const View *view)
200 {
201 4 CurrentLineRef lr = get_current_line_and_offset(view->cursor);
202 4 WordBounds wb = get_bounds_for_word_under_cursor(lr);
203
1/2
✓ Branch 0 (4→5) taken 4 times.
✗ Branch 1 (4→6) not taken.
4 return string_view(lr.line.data + wb.start, wb.end ? wb.end - wb.start : 0);
204 }
205
206 1 String dump_buffer(const View *view)
207 {
208 1 const Buffer *buffer = view->buffer;
209 1 uintmax_t counts[2];
210 1 char sizestr[FILESIZE_STR_MAX];
211 1 buffer_count_blocks_and_bytes(buffer, counts);
212 1 BUG_ON(counts[0] < 1);
213 1 BUG_ON(!buffer->setup);
214 1 String buf = string_new(1024 + (DEBUG ? 24 * counts[0] : 0));
215
216 2 string_sprintf (
217 &buf,
218 "%s %s\n%s %lu\n%s %s\n%s %s\n%s %ju\n%s %zu\n%s %s\n",
219 " Name:", buffer_filename(buffer),
220 1 " ID:", buffer->id,
221 1 " Encoding:", buffer->encoding,
222 1 " Filetype:", buffer->options.filetype,
223 " Blocks:", counts[0],
224 1 " Lines:", buffer->nl,
225 " Size:", filesize_to_str(counts[1], sizestr)
226 );
227
228 1 if (
229
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
230
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
231 ) {
232 string_sprintf (
233 &buf,
234 " Flags:%s%s%s%s%s%s\n",
235 buffer->stdout_buffer ? " STDOUT" : "",
236 buffer->temporary ? " TMP" : "",
237 buffer->readonly ? " RO" : "",
238 buffer->locked ? " LOCKED" : "",
239 buffer->crlf_newlines ? " CRLF" : "",
240 buffer->bom ? " BOM" : ""
241 );
242 }
243
244
1/2
✗ Branch 0 (30→31) not taken.
✓ Branch 1 (30→32) taken 1 times.
1 if (buffer->views.count > 1) {
245 string_sprintf(&buf, " Views: %zu\n", buffer->views.count);
246 }
247
248
1/2
✗ Branch 0 (32→33) not taken.
✓ Branch 1 (32→40) taken 1 times.
1 if (buffer->abs_filename) {
249 const FileInfo *file = &buffer->file;
250 const struct timespec *mtime = &file->mtime;
251 unsigned int perms = file->mode & 07777;
252 char tstr[TIME_STR_BUFSIZE];
253 char modestr[12];
254 string_sprintf (
255 &buf,
256 "\nLast stat:\n----------\n\n"
257 "%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",
258 " Path:", buffer->abs_filename,
259 " Modified:", timespec_to_str(mtime, tstr) ? tstr : "-",
260 " Mode:", file_permissions_to_str(file->mode, modestr), perms,
261 " User:", (intmax_t)file->uid,
262 " Group:", (intmax_t)file->gid,
263 " Size:", filesize_to_str(file->size, sizestr),
264 " Device:", (intmax_t)file->dev,
265 " Inode:", (uintmax_t)file->ino
266 );
267 }
268
269 1 if (DEBUG >= 1) {
270 1 const BlockIter *cursor = &view->cursor;
271 1 string_append_cstring(&buf, "\nBlocks:\n-------\n\n");
272 1 size_t i = 1;
273 1 const Block *b;
274
2/2
✓ Branch 0 (47→42) taken 1 times.
✓ Branch 1 (47→48) taken 1 times.
2 block_for_each(b, &buffer->blocks) {
275 1 string_sprintf(&buf, "%4zu: %zu/%zu nl=%zu", i++, b->size, b->alloc, b->nl);
276
1/2
✓ Branch 0 (43→44) taken 1 times.
✗ Branch 1 (43→45) not taken.
1 if (b == cursor->blk) {
277 1 string_sprintf(&buf, " (cursor; offset=%zu)", cursor->offset);
278 }
279 1 string_append_byte(&buf, '\n');
280 }
281 }
282
283 1 return buf;
284 }
285