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 |