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 |