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 | 299 | void view_update_cursor_y(View *view) | |
15 | { | ||
16 | 299 | const Buffer *buffer = view->buffer; | |
17 | 299 | const Block *blk; | |
18 | 299 | size_t nl = 0; | |
19 |
1/2✓ Branch 0 taken 299 times.
✗ Branch 1 not taken.
|
299 | block_for_each(blk, &buffer->blocks) { |
20 |
1/2✓ Branch 0 taken 299 times.
✗ Branch 1 not taken.
|
299 | if (blk == view->cursor.blk) { |
21 | 299 | nl += count_nl(blk->data, view->cursor.offset); | |
22 | 299 | view->cy = nl; | |
23 | 299 | return; | |
24 | } | ||
25 | ✗ | nl += blk->nl; | |
26 | } | ||
27 | − | BUG("unreachable"); | |
28 | } | ||
29 | |||
30 | 23 | void view_update_cursor_x(View *view) | |
31 | { | ||
32 | 23 | StringView line; | |
33 | 23 | const unsigned int tw = view->buffer->options.tab_width; | |
34 | 23 | const size_t cx = fetch_this_line(&view->cursor, &line); | |
35 | 23 | long cx_char = 0; | |
36 | 23 | long w = 0; | |
37 | |||
38 |
2/2✓ Branch 0 taken 66 times.
✓ Branch 1 taken 23 times.
|
89 | for (size_t idx = 0; idx < cx; cx_char++) { |
39 | 66 | CodePoint u = line.data[idx++]; | |
40 |
1/2✓ Branch 0 taken 66 times.
✗ Branch 1 not taken.
|
66 | if (likely(u < 0x80)) { |
41 |
1/2✓ Branch 0 taken 66 times.
✗ Branch 1 not taken.
|
66 | if (likely(!ascii_iscntrl(u))) { |
42 | 66 | w++; | |
43 | ✗ | } else if (u == '\t') { | |
44 | ✗ | w = next_indent_width(w, tw); | |
45 | } else { | ||
46 | ✗ | w += 2; | |
47 | } | ||
48 | } else { | ||
49 | ✗ | idx--; | |
50 | ✗ | u = u_get_nonascii(line.data, line.length, &idx); | |
51 | ✗ | w += u_char_width(u); | |
52 | } | ||
53 | } | ||
54 | |||
55 | 23 | view->cx = cx; | |
56 | 23 | view->cx_char = cx_char; | |
57 | 23 | view->cx_display = w; | |
58 | 23 | } | |
59 | |||
60 | ✗ | static bool view_is_cursor_visible(const View *v) | |
61 | { | ||
62 | ✗ | return v->cy < v->vy || v->cy > v->vy + v->window->edit_h - 1; | |
63 | } | ||
64 | |||
65 | ✗ | static void view_center_to_cursor(View *v) | |
66 | { | ||
67 | ✗ | size_t lines = v->buffer->nl; | |
68 | ✗ | Window *window = v->window; | |
69 | ✗ | unsigned int hh = window->edit_h / 2; | |
70 | |||
71 | ✗ | if (window->edit_h >= lines || v->cy < hh) { | |
72 | ✗ | v->vy = 0; | |
73 | ✗ | return; | |
74 | } | ||
75 | |||
76 | ✗ | v->vy = v->cy - hh; | |
77 | ✗ | 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 not taken.
✓ Branch 1 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 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (v->cx_display < v->vx) { |
92 | ✗ | v->vx = v->cx_display / c * c; | |
93 | } | ||
94 | 1 | } | |
95 | |||
96 | 1 | static void view_update_vy(View *v) | |
97 | { | ||
98 | 1 | Window *window = v->window; | |
99 | 1 | unsigned int margin = window_get_scroll_margin(window); | |
100 | 1 | long max_y = v->vy + window->edit_h - 1 - margin; | |
101 | |||
102 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (v->cy < v->vy + margin) { |
103 | ✗ | v->vy = MAX(v->cy - margin, 0); | |
104 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | } else if (v->cy > max_y) { |
105 | 1 | v->vy += v->cy - max_y; | |
106 | 1 | max_y = v->buffer->nl - window->edit_h + 1; | |
107 |
1/4✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
|
1 | if (v->vy > max_y && max_y >= 0) { |
108 | ✗ | v->vy = max_y; | |
109 | } | ||
110 | } | ||
111 | 1 | } | |
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 |
2/6✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
|
1 | if (v->force_center || (v->center_on_scroll && view_is_cursor_visible(v))) { |
119 | ✗ | view_center_to_cursor(v); | |
120 | } else { | ||
121 | 1 | view_update_vy(v); | |
122 | } | ||
123 | 1 | v->force_center = false; | |
124 | 1 | v->center_on_scroll = false; | |
125 | 1 | } | |
126 | |||
127 | 50 | long view_get_preferred_x(View *v) | |
128 | { | ||
129 |
2/2✓ Branch 0 taken 22 times.
✓ Branch 1 taken 28 times.
|
50 | if (v->preferred_x < 0) { |
130 | 22 | view_update_cursor_x(v); | |
131 | 22 | v->preferred_x = v->cx_display; | |
132 | } | ||
133 | 50 | return v->preferred_x; | |
134 | } | ||
135 | |||
136 | 13 | bool view_can_close(const View *view) | |
137 | { | ||
138 | 13 | const Buffer *buffer = view->buffer; | |
139 |
3/4✓ Branch 0 taken 1 times.
✓ Branch 1 taken 12 times.
✗ Branch 2 not taken.
✓ Branch 3 taken 1 times.
|
13 | return !buffer_modified(buffer) || buffer->views.count > 1; |
140 | } | ||
141 | |||
142 | ✗ | StringView get_word_under_cursor(StringView line, size_t cursor_offset) | |
143 | { | ||
144 | ✗ | size_t si = cursor_offset; | |
145 | ✗ | while (si < line.length) { | |
146 | ✗ | size_t i = si; | |
147 | ✗ | if (u_is_word_char(u_get_char(line.data, line.length, &i))) { | |
148 | break; | ||
149 | } | ||
150 | ✗ | si = i; | |
151 | } | ||
152 | |||
153 | ✗ | if (si == line.length) { | |
154 | ✗ | return string_view(NULL, 0); | |
155 | } | ||
156 | |||
157 | ✗ | size_t ei = si; | |
158 | ✗ | while (si > 0) { | |
159 | ✗ | size_t i = si; | |
160 | ✗ | if (!u_is_word_char(u_prev_char(line.data, &i))) { | |
161 | break; | ||
162 | } | ||
163 | ✗ | si = i; | |
164 | } | ||
165 | |||
166 | ✗ | while (ei < line.length) { | |
167 | ✗ | size_t i = ei; | |
168 | ✗ | if (!u_is_word_char(u_get_char(line.data, line.length, &i))) { | |
169 | break; | ||
170 | } | ||
171 | ✗ | ei = i; | |
172 | } | ||
173 | |||
174 | ✗ | return string_view(line.data + si, ei - si); | |
175 | } | ||
176 | |||
177 | ✗ | StringView view_get_word_under_cursor(const View *view) | |
178 | { | ||
179 | ✗ | StringView line; | |
180 | ✗ | size_t cursor_offset_in_line = fetch_this_line(&view->cursor, &line); | |
181 | ✗ | return get_word_under_cursor(line, cursor_offset_in_line); | |
182 | } | ||
183 | |||
184 | 1 | String dump_buffer(const View *view) | |
185 | { | ||
186 | 1 | const Buffer *buffer = view->buffer; | |
187 | 1 | uintmax_t counts[2]; | |
188 | 1 | char sizestr[FILESIZE_STR_MAX]; | |
189 | 1 | buffer_count_blocks_and_bytes(buffer, counts); | |
190 | 1 | BUG_ON(counts[0] < 1); | |
191 | 1 | BUG_ON(!buffer->setup); | |
192 | 1 | String buf = string_new(1024 + (DEBUG ? 24 * counts[0] : 0)); | |
193 | |||
194 | 2 | string_sprintf ( | |
195 | &buf, | ||
196 | "%s %s\n%s %lu\n%s %s\n%s %s\n%s %ju\n%s %zu\n%s %s\n", | ||
197 | " Name:", buffer_filename(buffer), | ||
198 | 1 | " ID:", buffer->id, | |
199 | 1 | " Encoding:", buffer->encoding, | |
200 | 1 | " Filetype:", buffer->options.filetype, | |
201 | " Blocks:", counts[0], | ||
202 | 1 | " Lines:", buffer->nl, | |
203 | " Size:", filesize_to_str(counts[1], sizestr) | ||
204 | |||
205 | ); | ||
206 | |||
207 | 1 | if ( | |
208 | buffer->stdout_buffer || buffer->temporary || buffer->readonly | ||
209 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | || buffer->locked || buffer->crlf_newlines || buffer->bom |
210 | ) { | ||
211 | ✗ | string_sprintf ( | |
212 | &buf, | ||
213 | " Flags:%s%s%s%s%s%s\n", | ||
214 | ✗ | buffer->stdout_buffer ? " STDOUT" : "", | |
215 | ✗ | buffer->temporary ? " TMP" : "", | |
216 | ✗ | buffer->readonly ? " RO" : "", | |
217 | ✗ | buffer->locked ? " LOCKED" : "", | |
218 | ✗ | buffer->crlf_newlines ? " CRLF" : "", | |
219 | ✗ | buffer->bom ? " BOM" : "" | |
220 | ); | ||
221 | } | ||
222 | |||
223 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (buffer->views.count > 1) { |
224 | ✗ | string_sprintf(&buf, " Views: %zu\n", buffer->views.count); | |
225 | } | ||
226 | |||
227 |
1/2✗ Branch 0 not taken.
✓ Branch 1 taken 1 times.
|
1 | if (buffer->abs_filename) { |
228 | ✗ | const FileInfo *file = &buffer->file; | |
229 | ✗ | const struct timespec *mtime = &file->mtime; | |
230 | ✗ | unsigned int perms = file->mode & 07777; | |
231 | ✗ | char tstr[TIME_STR_BUFSIZE]; | |
232 | ✗ | char modestr[12]; | |
233 | ✗ | string_sprintf ( | |
234 | &buf, | ||
235 | "\nLast stat:\n----------\n\n" | ||
236 | "%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", | ||
237 | ✗ | " Path:", buffer->abs_filename, | |
238 | ✗ | " Modified:", timespec_to_str(mtime, tstr, sizeof(tstr)) ? tstr : "-", | |
239 | ✗ | " Mode:", file_permissions_to_str(file->mode, modestr), perms, | |
240 | ✗ | " User:", (intmax_t)file->uid, | |
241 | ✗ | " Group:", (intmax_t)file->gid, | |
242 | ✗ | " Size:", filesize_to_str(file->size, sizestr), | |
243 | ✗ | " Device:", (intmax_t)file->dev, | |
244 | ✗ | " Inode:", (uintmax_t)file->ino | |
245 | ); | ||
246 | } | ||
247 | |||
248 | 1 | if (DEBUG >= 1) { | |
249 | 1 | const BlockIter *cursor = &view->cursor; | |
250 | 1 | string_append_cstring(&buf, "\nBlocks:\n-------\n\n"); | |
251 | 1 | size_t i = 1; | |
252 | 1 | const Block *b; | |
253 |
2/2✓ Branch 0 taken 1 times.
✓ Branch 1 taken 1 times.
|
2 | block_for_each(b, &buffer->blocks) { |
254 | 1 | string_sprintf(&buf, "%4zu: %zu/%zu nl=%zu", i++, b->size, b->alloc, b->nl); | |
255 |
1/2✓ Branch 0 taken 1 times.
✗ Branch 1 not taken.
|
1 | if (b == cursor->blk) { |
256 | 1 | string_sprintf(&buf, " (cursor; offset=%zu)", cursor->offset); | |
257 | } | ||
258 | 1 | string_append_byte(&buf, '\n'); | |
259 | } | ||
260 | } | ||
261 | |||
262 | 1 | return buf; | |
263 | } | ||
264 |