dte test coverage


Directory: ./
File: src/view.c
Date: 2025-05-08 15:05:54
Exec Total Coverage
Lines: 111 157 70.7%
Functions: 11 12 91.7%
Branches: 44 98 44.9%

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