| 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 |