src/edit.c
| Line | Branch | Exec | Source |
|---|---|---|---|
| 1 | #include <string.h> | ||
| 2 | #include "edit.h" | ||
| 3 | #include "block.h" | ||
| 4 | #include "buffer.h" | ||
| 5 | #include "syntax/highlight.h" | ||
| 6 | #include "util/debug.h" | ||
| 7 | #include "util/list.h" | ||
| 8 | #include "util/xmalloc.h" | ||
| 9 | |||
| 10 | enum { | ||
| 11 | BLOCK_EDIT_SIZE = 512 | ||
| 12 | }; | ||
| 13 | |||
| 14 | 468 | static void sanity_check_blocks(const View *view, bool check_newlines) | |
| 15 | { | ||
| 16 | 468 | if (!DEBUG_ASSERTIONS_ENABLED) { | |
| 17 | return; | ||
| 18 | } | ||
| 19 | |||
| 20 | // NOLINTBEGIN(bugprone-assert-side-effect) | ||
| 21 | 468 | const Buffer *buffer = view->buffer; | |
| 22 | 468 | const Block *blk = buffer_get_first_block(buffer); | |
| 23 | 468 | const Block *cursor_blk = view->cursor.blk; | |
| 24 | 468 | BUG_ON(list_empty(&buffer->blocks)); | |
| 25 | 468 | BUG_ON(view->cursor.offset > cursor_blk->size); | |
| 26 | |||
| 27 |
2/2✓ Branch 6 → 7 taken 19 times.
✓ Branch 6 → 14 taken 449 times.
|
468 | if (blk->size == 0) { |
| 28 | // The only time a zero-sized block is valid is when it's the | ||
| 29 | // first and only block | ||
| 30 | 19 | BUG_ON(block_has_next(blk, &buffer->blocks)); | |
| 31 | 19 | BUG_ON(block_has_prev(blk, &buffer->blocks)); | |
| 32 | 19 | BUG_ON(cursor_blk != blk); | |
| 33 | 19 | block_sanity_check(blk); | |
| 34 | 19 | return; | |
| 35 | } | ||
| 36 | |||
| 37 | 449 | unsigned int cursor_seen = 0; | |
| 38 |
2/2✓ Branch 27 → 15 taken 453 times.
✓ Branch 27 → 28 taken 449 times.
|
902 | block_for_each(blk, &buffer->blocks) { |
| 39 | 453 | block_sanity_check(blk); | |
| 40 | 453 | cursor_seen += (blk == cursor_blk); | |
| 41 | 453 | BUG_ON(blk->size == 0); | |
| 42 | |||
| 43 | // Non-empty blocks must ALWAYS end with a newline, since | ||
| 44 | // lines MAY NOT straddle multiple blocks | ||
| 45 | 453 | BUG_ON(check_newlines && blk->data[blk->size - 1] != '\n'); | |
| 46 | 453 | BUG_ON(blk->nl < 1); | |
| 47 | 453 | BUG_ON(DEBUG > 2 && count_nl(blk->data, blk->size) != blk->nl); | |
| 48 | } | ||
| 49 | |||
| 50 | 449 | BUG_ON(cursor_seen != 1); | |
| 51 | // NOLINTEND(bugprone-assert-side-effect) | ||
| 52 | } | ||
| 53 | |||
| 54 | 515 | static size_t copy_count_nl(char *dst, const char *src, size_t len) | |
| 55 | { | ||
| 56 | 515 | size_t nl = 0; | |
| 57 |
2/2✓ Branch 6 → 3 taken 28801 times.
✓ Branch 6 → 7 taken 515 times.
|
29316 | for (size_t i = 0; i < len; i++) { |
| 58 | 28801 | dst[i] = src[i]; | |
| 59 |
2/2✓ Branch 3 → 4 taken 239 times.
✓ Branch 3 → 5 taken 28562 times.
|
28801 | if (src[i] == '\n') { |
| 60 | 239 | nl++; | |
| 61 | } | ||
| 62 | } | ||
| 63 | 515 | return nl; | |
| 64 | } | ||
| 65 | |||
| 66 | 335 | static size_t insert_to_current(BlockIter *cursor, StringView text) | |
| 67 | { | ||
| 68 | 335 | Block *blk = cursor->blk; | |
| 69 | 335 | size_t offset = cursor->offset; | |
| 70 | 335 | size_t size = blk->size + text.length; | |
| 71 | 335 | block_grow(blk, size); | |
| 72 | 335 | memmove(blk->data + offset + text.length, blk->data + offset, blk->size - offset); | |
| 73 | 335 | size_t nl = copy_count_nl(blk->data + offset, text.data, text.length); | |
| 74 | 335 | blk->nl += nl; | |
| 75 | 335 | blk->size = size; | |
| 76 | 335 | return nl; | |
| 77 | } | ||
| 78 | |||
| 79 | /* | ||
| 80 | * Combine current block and new data into smaller blocks: | ||
| 81 | * | ||
| 82 | * • Block _must_ contain whole lines | ||
| 83 | * • Block _must_ contain at least one line | ||
| 84 | * • Preferred maximum size of block is BLOCK_EDIT_SIZE | ||
| 85 | * • Size of any block can be larger than BLOCK_EDIT_SIZE only if there's | ||
| 86 | * a very long line | ||
| 87 | */ | ||
| 88 | 2 | static size_t split_and_insert(BlockIter *cursor, StringView ins) | |
| 89 | { | ||
| 90 | 2 | Block *blk = cursor->blk; | |
| 91 | 2 | ListHead *prev_node = blk->node.prev; | |
| 92 | 2 | const char *buf1 = blk->data; | |
| 93 | 2 | const char *buf2 = ins.data; | |
| 94 | 2 | const char *buf3 = blk->data + cursor->offset; | |
| 95 | 2 | size_t size1 = cursor->offset; | |
| 96 | 2 | size_t size2 = ins.length; | |
| 97 | 2 | size_t size3 = blk->size - size1; | |
| 98 | 2 | size_t total = size1 + size2 + size3; | |
| 99 | 2 | size_t start = 0; // Beginning of new block | |
| 100 | 2 | size_t size = 0; // Size of new block | |
| 101 | 2 | size_t pos = 0; // Current position | |
| 102 | 2 | size_t nl_added = 0; | |
| 103 | |||
| 104 |
2/2✓ Branch 42 → 3 taken 28 times.
✓ Branch 42 → 43 taken 2 times.
|
30 | while (start < total) { |
| 105 | // Size of new block if next line would be added | ||
| 106 | 28 | size_t new_size = 0; | |
| 107 | 28 | size_t copied = 0; | |
| 108 | |||
| 109 |
2/2✓ Branch 3 → 4 taken 5 times.
✓ Branch 3 → 6 taken 23 times.
|
28 | if (pos < size1) { |
| 110 | 5 | const char *nl = memchr(buf1 + pos, '\n', size1 - pos); | |
| 111 |
2/2✓ Branch 4 → 5 taken 4 times.
✓ Branch 4 → 6 taken 1 time.
|
5 | if (nl) { |
| 112 | 4 | new_size = nl - buf1 + 1 - start; | |
| 113 | } | ||
| 114 | } | ||
| 115 | |||
| 116 |
3/4✗ Branch 5 → 6 not taken.
✓ Branch 5 → 11 taken 4 times.
✓ Branch 6 → 7 taken 20 times.
✓ Branch 6 → 11 taken 4 times.
|
28 | if (!new_size && pos < size1 + size2) { |
| 117 | 20 | size_t offset = 0; | |
| 118 |
2/2✓ Branch 7 → 8 taken 18 times.
✓ Branch 7 → 9 taken 2 times.
|
20 | if (pos > size1) { |
| 119 | 18 | offset = pos - size1; | |
| 120 | } | ||
| 121 | |||
| 122 | 20 | const char *nl = memchr(buf2 + offset, '\n', size2 - offset); | |
| 123 |
1/2✓ Branch 9 → 10 taken 20 times.
✗ Branch 9 → 11 not taken.
|
20 | if (nl) { |
| 124 | 20 | new_size = size1 + nl - buf2 + 1 - start; | |
| 125 | } | ||
| 126 | } | ||
| 127 | |||
| 128 |
2/2✓ Branch 11 → 12 taken 4 times.
✓ Branch 11 → 17 taken 24 times.
|
28 | if (!new_size && pos < total) { |
| 129 | 4 | size_t offset = 0; | |
| 130 |
2/2✓ Branch 12 → 13 taken 2 times.
✓ Branch 12 → 14 taken 2 times.
|
4 | if (pos > size1 + size2) { |
| 131 | 2 | offset = pos - size1 - size2; | |
| 132 | } | ||
| 133 | |||
| 134 | 4 | const char *nl = memchr(buf3 + offset, '\n', size3 - offset); | |
| 135 |
1/2✓ Branch 14 → 15 taken 4 times.
✗ Branch 14 → 16 not taken.
|
4 | if (nl) { |
| 136 | 4 | new_size = size1 + size2 + nl - buf3 + 1 - start; | |
| 137 | } else { | ||
| 138 | ✗ | new_size = total - start; | |
| 139 | } | ||
| 140 | } | ||
| 141 | |||
| 142 |
2/2✓ Branch 17 → 18 taken 25 times.
✓ Branch 17 → 20 taken 3 times.
|
28 | if (new_size <= BLOCK_EDIT_SIZE) { |
| 143 | // Fits | ||
| 144 | 25 | size = new_size; | |
| 145 | 25 | pos = start + new_size; | |
| 146 |
2/2✓ Branch 18 → 19 taken 23 times.
✓ Branch 18 → 22 taken 2 times.
|
25 | if (pos < total) { |
| 147 | 23 | continue; | |
| 148 | } | ||
| 149 | } else { | ||
| 150 | // Does not fit | ||
| 151 |
1/2✗ Branch 20 → 21 not taken.
✓ Branch 20 → 24 taken 3 times.
|
3 | if (!size) { |
| 152 | // One block containing one very long line | ||
| 153 | ✗ | size = new_size; | |
| 154 | ✗ | pos = start + new_size; | |
| 155 | } | ||
| 156 | } | ||
| 157 | |||
| 158 | 2 | BUG_ON(!size); | |
| 159 | 5 | Block *new = block_new(size); | |
| 160 |
2/2✓ Branch 25 → 26 taken 1 time.
✓ Branch 25 → 28 taken 4 times.
|
5 | if (start < size1) { |
| 161 | 1 | size_t avail = size1 - start; | |
| 162 | 1 | size_t count = MIN(size, avail); | |
| 163 | 1 | new->nl += copy_count_nl(new->data, buf1 + start, count); | |
| 164 | 1 | copied += count; | |
| 165 | 1 | start += count; | |
| 166 | } | ||
| 167 |
3/4✓ Branch 28 → 29 taken 5 times.
✗ Branch 28 → 32 not taken.
✓ Branch 29 → 30 taken 4 times.
✓ Branch 29 → 32 taken 1 time.
|
5 | if (start >= size1 && start < size1 + size2) { |
| 168 | 4 | size_t offset = start - size1; | |
| 169 | 4 | size_t avail = size2 - offset; | |
| 170 | 4 | size_t count = MIN(size - copied, avail); | |
| 171 | 4 | new->nl += copy_count_nl(new->data + copied, buf2 + offset, count); | |
| 172 | 4 | copied += count; | |
| 173 | 4 | start += count; | |
| 174 | } | ||
| 175 |
2/2✓ Branch 32 → 33 taken 3 times.
✓ Branch 32 → 37 taken 2 times.
|
5 | if (start >= size1 + size2) { |
| 176 | 3 | size_t offset = start - size1 - size2; | |
| 177 | 3 | size_t avail = size3 - offset; | |
| 178 | 3 | size_t count = size - copied; | |
| 179 | 3 | BUG_ON(count > avail); | |
| 180 | 3 | new->nl += copy_count_nl(new->data + copied, buf3 + offset, count); | |
| 181 | 3 | copied += count; | |
| 182 | 3 | start += count; | |
| 183 | } | ||
| 184 | |||
| 185 | 5 | new->size = size; | |
| 186 | 5 | BUG_ON(copied != size); | |
| 187 | 5 | list_insert_before(&new->node, &blk->node); | |
| 188 | |||
| 189 | 5 | nl_added += new->nl; | |
| 190 | 5 | size = 0; | |
| 191 | } | ||
| 192 | |||
| 193 | 2 | cursor->blk = BLOCK(prev_node->next); | |
| 194 |
1/2✗ Branch 45 → 44 not taken.
✓ Branch 45 → 46 taken 2 times.
|
2 | while (cursor->offset > cursor->blk->size) { |
| 195 | ✗ | cursor->offset -= cursor->blk->size; | |
| 196 | ✗ | cursor->blk = block_next(cursor->blk); | |
| 197 | } | ||
| 198 | |||
| 199 | 2 | nl_added -= blk->nl; | |
| 200 | 2 | block_free(blk); | |
| 201 | 2 | return nl_added; | |
| 202 | } | ||
| 203 | |||
| 204 | 337 | static size_t insert_bytes(BlockIter *cursor, StringView bytes) | |
| 205 | { | ||
| 206 | // Blocks must contain whole lines. | ||
| 207 | // Last char of `bytes` might not be a newline. | ||
| 208 | 337 | block_iter_normalize(cursor); | |
| 209 | |||
| 210 | 337 | Block *blk = cursor->blk; | |
| 211 | 337 | size_t new_size = blk->size + bytes.length; | |
| 212 |
4/4✓ Branch 3 → 4 taken 11 times.
✓ Branch 3 → 5 taken 326 times.
✓ Branch 4 → 5 taken 7 times.
✓ Branch 4 → 6 taken 4 times.
|
337 | if (new_size <= blk->alloc || new_size <= BLOCK_EDIT_SIZE) { |
| 213 | 333 | return insert_to_current(cursor, bytes); | |
| 214 | } | ||
| 215 | |||
| 216 |
4/4✓ Branch 6 → 7 taken 3 times.
✓ Branch 6 → 9 taken 1 time.
✓ Branch 7 → 8 taken 2 times.
✓ Branch 7 → 9 taken 1 time.
|
4 | if (blk->nl <= 1 && !strview_memchr(bytes, '\n')) { |
| 217 | // Can't split this possibly very long line. | ||
| 218 | // insert_to_current() is much faster than split_and_insert(). | ||
| 219 | 2 | return insert_to_current(cursor, bytes); | |
| 220 | } | ||
| 221 | |||
| 222 | 2 | return split_and_insert(cursor, bytes); | |
| 223 | } | ||
| 224 | |||
| 225 | 337 | void do_insert(View *view, StringView text) | |
| 226 | { | ||
| 227 | 337 | Buffer *buffer = view->buffer; | |
| 228 | 337 | size_t nl = insert_bytes(&view->cursor, text); | |
| 229 | 337 | buffer->nl += nl; | |
| 230 | 337 | sanity_check_blocks(view, true); | |
| 231 | |||
| 232 | 337 | view_update_cursor_y(view); | |
| 233 |
2/2✓ Branch 5 → 6 taken 213 times.
✓ Branch 5 → 7 taken 124 times.
|
337 | buffer_mark_lines_changed(buffer, view->cy, nl ? LONG_MAX : view->cy); |
| 234 |
2/2✓ Branch 8 → 9 taken 2 times.
✓ Branch 8 → 10 taken 335 times.
|
337 | if (buffer->syntax) { |
| 235 | 2 | hl_insert(&buffer->line_start_states, view->cy, nl); | |
| 236 | } | ||
| 237 | 337 | } | |
| 238 | |||
| 239 | 19 | static bool only_block(const Buffer *buffer, const Block *blk) | |
| 240 | { | ||
| 241 | 19 | const ListHead *head = &buffer->blocks; | |
| 242 |
2/4✓ Branch 2 → 3 taken 19 times.
✗ Branch 2 → 5 not taken.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 19 times.
|
19 | return !block_has_next(blk, head) && !block_has_prev(blk, head); |
| 243 | } | ||
| 244 | |||
| 245 | 90 | char *do_delete(View *view, size_t len, bool sanity_check_newlines) | |
| 246 | { | ||
| 247 |
1/2✓ Branch 2 → 3 taken 90 times.
✗ Branch 2 → 34 not taken.
|
90 | if (len == 0) { |
| 248 | return NULL; | ||
| 249 | } | ||
| 250 | |||
| 251 | 90 | ListHead *saved_prev_node = NULL; | |
| 252 | 90 | Block *blk = view->cursor.blk; | |
| 253 | 90 | size_t offset = view->cursor.offset; | |
| 254 |
2/2✓ Branch 3 → 4 taken 39 times.
✓ Branch 3 → 5 taken 51 times.
|
90 | if (!offset) { |
| 255 | // The block where the cursor is can become empty and thus | ||
| 256 | // may be deleted | ||
| 257 | 39 | saved_prev_node = blk->node.prev; | |
| 258 | } | ||
| 259 | |||
| 260 | 90 | Buffer *buffer = view->buffer; | |
| 261 | 90 | char *deleted = xmalloc(len); | |
| 262 | 90 | size_t pos = 0; | |
| 263 | 90 | size_t deleted_nl = 0; | |
| 264 | |||
| 265 | 90 | while (pos < len) { | |
| 266 | 90 | ListHead *next = blk->node.next; | |
| 267 | 90 | size_t avail = blk->size - offset; | |
| 268 | 90 | size_t count = MIN(len - pos, avail); | |
| 269 | 90 | char *ptr = blk->data + offset; | |
| 270 | 90 | size_t nl = copy_count_nl(deleted + pos, ptr, count); | |
| 271 |
2/2✓ Branch 8 → 9 taken 63 times.
✓ Branch 8 → 10 taken 27 times.
|
90 | if (count < avail) { |
| 272 | 63 | memmove(ptr, ptr + count, avail - count); | |
| 273 | } | ||
| 274 | |||
| 275 | 90 | deleted_nl += nl; | |
| 276 | 90 | buffer->nl -= nl; | |
| 277 | 90 | blk->nl -= nl; | |
| 278 | 90 | blk->size -= count; | |
| 279 |
3/4✓ Branch 10 → 11 taken 19 times.
✓ Branch 10 → 13 taken 71 times.
✗ Branch 11 → 12 not taken.
✓ Branch 11 → 13 taken 19 times.
|
90 | if (!blk->size && !only_block(buffer, blk)) { |
| 280 | ✗ | block_free(blk); | |
| 281 | } | ||
| 282 | |||
| 283 | 90 | offset = 0; | |
| 284 | 90 | pos += count; | |
| 285 | 90 | blk = BLOCK(next); | |
| 286 | 270 | BUG_ON(pos < len && next == &buffer->blocks); | |
| 287 | } | ||
| 288 | |||
| 289 |
2/2✓ Branch 18 → 19 taken 39 times.
✓ Branch 18 → 22 taken 51 times.
|
90 | if (saved_prev_node) { |
| 290 | // Cursor was at beginning of a block that was possibly deleted | ||
| 291 |
1/2✗ Branch 19 → 20 not taken.
✓ Branch 19 → 21 taken 39 times.
|
39 | if (saved_prev_node->next == &buffer->blocks) { |
| 292 | ✗ | view->cursor.blk = BLOCK(saved_prev_node); | |
| 293 | ✗ | view->cursor.offset = view->cursor.blk->size; | |
| 294 | } else { | ||
| 295 | 39 | view->cursor.blk = BLOCK(saved_prev_node->next); | |
| 296 | } | ||
| 297 | } | ||
| 298 | |||
| 299 | 90 | blk = view->cursor.blk; | |
| 300 | |||
| 301 | 90 | if ( | |
| 302 |
2/2✓ Branch 22 → 23 taken 71 times.
✓ Branch 22 → 27 taken 19 times.
|
90 | blk->size |
| 303 |
1/2✗ Branch 23 → 24 not taken.
✓ Branch 23 → 27 taken 71 times.
|
71 | && blk->data[blk->size - 1] != '\n' |
| 304 | ✗ | && block_has_next(blk, &buffer->blocks) | |
| 305 | ) { | ||
| 306 | ✗ | Block *next = block_next(blk); | |
| 307 | ✗ | size_t size = blk->size + next->size; | |
| 308 | ✗ | block_grow(blk, size); | |
| 309 | ✗ | memcpy(blk->data + blk->size, next->data, next->size); | |
| 310 | ✗ | blk->size = size; | |
| 311 | ✗ | blk->nl += next->nl; | |
| 312 | ✗ | block_free(next); | |
| 313 | } | ||
| 314 | |||
| 315 | 90 | sanity_check_blocks(view, sanity_check_newlines); | |
| 316 | |||
| 317 | 90 | view_update_cursor_y(view); | |
| 318 |
2/2✓ Branch 29 → 30 taken 51 times.
✓ Branch 29 → 31 taken 39 times.
|
90 | buffer_mark_lines_changed(buffer, view->cy, deleted_nl ? LONG_MAX : view->cy); |
| 319 | |||
| 320 |
1/2✗ Branch 32 → 33 not taken.
✓ Branch 32 → 34 taken 90 times.
|
90 | if (buffer->syntax) { |
| 321 | ✗ | hl_delete(&buffer->line_start_states, view->cy, deleted_nl); | |
| 322 | } | ||
| 323 | |||
| 324 | return deleted; | ||
| 325 | } | ||
| 326 | |||
| 327 | 46 | char *do_replace(View *view, size_t del, StringView ins) | |
| 328 | { | ||
| 329 | 46 | BUG_ON(del == 0); | |
| 330 | 46 | BUG_ON(ins.length == 0); | |
| 331 | 46 | block_iter_normalize(&view->cursor); | |
| 332 | |||
| 333 | 46 | Block *blk = view->cursor.blk; | |
| 334 | 46 | size_t offset = view->cursor.offset; | |
| 335 | 46 | size_t avail = blk->size - offset; | |
| 336 |
2/2✓ Branch 7 → 8 taken 5 times.
✓ Branch 7 → 9 taken 41 times.
|
46 | if (del >= avail) { |
| 337 | 5 | goto slow; | |
| 338 | } | ||
| 339 | |||
| 340 | 41 | size_t new_size = blk->size + ins.length - del; | |
| 341 |
1/2✗ Branch 9 → 10 not taken.
✓ Branch 9 → 13 taken 41 times.
|
41 | if (new_size > BLOCK_EDIT_SIZE) { |
| 342 | // Should split | ||
| 343 | ✗ | if (blk->nl > 1 || strview_memchr(ins, '\n')) { | |
| 344 | // Most likely can be split | ||
| 345 | ✗ | goto slow; | |
| 346 | } | ||
| 347 | } | ||
| 348 | |||
| 349 | 41 | block_grow(blk, new_size); | |
| 350 | |||
| 351 | // Modification is limited to one block | ||
| 352 | 41 | Buffer *buffer = view->buffer; | |
| 353 | 41 | char *ptr = blk->data + offset; | |
| 354 | 41 | char *deleted = xmalloc(del); | |
| 355 | 41 | size_t del_nl = copy_count_nl(deleted, ptr, del); | |
| 356 | 41 | blk->nl -= del_nl; | |
| 357 | 41 | buffer->nl -= del_nl; | |
| 358 | |||
| 359 |
2/2✓ Branch 16 → 17 taken 28 times.
✓ Branch 16 → 18 taken 13 times.
|
41 | if (del != ins.length) { |
| 360 | 28 | memmove(ptr + ins.length, ptr + del, avail - del); | |
| 361 | } | ||
| 362 | |||
| 363 | 41 | size_t ins_nl = copy_count_nl(ptr, ins.data, ins.length); | |
| 364 | 41 | blk->nl += ins_nl; | |
| 365 | 41 | buffer->nl += ins_nl; | |
| 366 | 41 | blk->size = new_size; | |
| 367 | 41 | sanity_check_blocks(view, true); | |
| 368 | 41 | view_update_cursor_y(view); | |
| 369 | |||
| 370 | // If the number of inserted and removed bytes are the same, some | ||
| 371 | // line(s) changed but the lines after them didn't move up or down | ||
| 372 |
2/2✓ Branch 21 → 22 taken 27 times.
✓ Branch 21 → 23 taken 14 times.
|
41 | long max = (del_nl == ins_nl) ? view->cy + del_nl : LONG_MAX; |
| 373 | 41 | buffer_mark_lines_changed(buffer, view->cy, max); | |
| 374 | |||
| 375 |
1/2✗ Branch 24 → 25 not taken.
✓ Branch 24 → 32 taken 41 times.
|
41 | if (buffer->syntax) { |
| 376 | ✗ | hl_delete(&buffer->line_start_states, view->cy, del_nl); | |
| 377 | ✗ | hl_insert(&buffer->line_start_states, view->cy, ins_nl); | |
| 378 | } | ||
| 379 | |||
| 380 | return deleted; | ||
| 381 | |||
| 382 | 5 | slow: | |
| 383 | // The "sanity_check_newlines" argument of do_delete() is false here | ||
| 384 | // because it may be removing a terminating newline that do_insert() | ||
| 385 | // is going to insert again at a different position: | ||
| 386 | 5 | deleted = do_delete(view, del, false); | |
| 387 | 5 | BUG_ON(!deleted); // do_delete() only returns NULL if len is 0 | |
| 388 | 5 | do_insert(view, ins); | |
| 389 | 5 | return deleted; | |
| 390 | } | ||
| 391 |