dte test coverage


Directory: ./
File: src/insert.c
Date: 2024-12-21 16:03:22
Exec Total Coverage
Lines: 79 119 66.4%
Functions: 5 6 83.3%
Branches: 23 58 39.7%

Line Branch Exec Source
1 #include <stdlib.h>
2 #include <string.h>
3 #include "insert.h"
4 #include "block-iter.h"
5 #include "buffer.h"
6 #include "change.h"
7 #include "indent.h"
8 #include "misc.h"
9 #include "options.h"
10 #include "selection.h"
11 #include "util/utf8.h"
12
13 41 void insert_text(View *view, const char *text, size_t size, bool move_after)
14 {
15 41 size_t del_count = 0;
16
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 41 times.
41 if (view->selection) {
17 del_count = prepare_selection(view);
18 unselect(view);
19 }
20 41 buffer_replace_bytes(view, del_count, text, size);
21
2/2
✓ Branch 0 taken 21 times.
✓ Branch 1 taken 20 times.
41 if (move_after) {
22 21 block_iter_skip_bytes(&view->cursor, size);
23 }
24 41 }
25
26 3 void new_line(View *view, bool above)
27 {
28
1/4
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
3 if (above && block_iter_prev_line(&view->cursor) == 0) {
29 // Already on first line; insert newline at bof
30 block_iter_bol(&view->cursor);
31 buffer_insert_bytes(view, "\n", 1);
32 return;
33 }
34
35 3 const LocalOptions *options = &view->buffer->options;
36 3 char *ins = NULL;
37 3 block_iter_eol(&view->cursor);
38
39
1/2
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
3 if (options->auto_indent) {
40 3 BlockIter bi = view->cursor;
41
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (block_iter_find_non_empty_line_bwd(&bi)) {
42 3 StringView line = block_iter_get_line(&bi);
43 3 ins = get_indent_for_next_line(options, &line);
44 }
45 }
46
47 3 size_t ins_count;
48
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 3 times.
3 if (ins) {
49 ins_count = strlen(ins);
50 memmove(ins + 1, ins, ins_count);
51 ins[0] = '\n';
52 ins_count++;
53 buffer_insert_bytes(view, ins, ins_count);
54 free(ins);
55 } else {
56 3 ins_count = 1;
57 3 buffer_insert_bytes(view, "\n", 1);
58 }
59
60 3 block_iter_skip_bytes(&view->cursor, ins_count);
61 }
62
63 // Go to beginning of whitespace (tabs and spaces) under cursor and
64 // return number of whitespace bytes surrounding cursor
65 14 static inline size_t goto_beginning_of_whitespace(BlockIter *cursor)
66 {
67 14 BlockIter tmp = *cursor;
68 14 return block_iter_skip_blanks_fwd(&tmp) + block_iter_skip_blanks_bwd(cursor);
69 }
70
71 14 static void insert_nl(View *view)
72 {
73 // Prepare deleted text (selection or whitespace around cursor)
74 14 size_t del_count = 0;
75
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 14 times.
14 if (view->selection) {
76 del_count = prepare_selection(view);
77 unselect(view);
78 } else {
79 // Trim whitespace around cursor
80 14 del_count = goto_beginning_of_whitespace(&view->cursor);
81 }
82
83 // Prepare inserted indentation
84 14 const LocalOptions *options = &view->buffer->options;
85 14 char *ins = NULL;
86
1/2
✓ Branch 0 taken 14 times.
✗ Branch 1 not taken.
14 if (options->auto_indent) {
87 // Current line will be split at cursor position
88 14 BlockIter bi = view->cursor;
89 14 size_t len = block_iter_bol(&bi);
90 14 StringView line = block_iter_get_line(&bi);
91 14 line.length = len;
92
2/2
✓ Branch 0 taken 3 times.
✓ Branch 1 taken 11 times.
14 if (strview_isblank(&line)) {
93 // This line is (or will become) white space only; find previous,
94 // non whitespace only line
95
2/4
✓ Branch 0 taken 3 times.
✗ Branch 1 not taken.
✓ Branch 2 taken 3 times.
✗ Branch 3 not taken.
3 if (block_iter_prev_line(&bi) && block_iter_find_non_empty_line_bwd(&bi)) {
96 3 line = block_iter_get_line(&bi);
97 3 ins = get_indent_for_next_line(options, &line);
98 }
99 } else {
100 11 ins = get_indent_for_next_line(options, &line);
101 }
102 }
103
104 14 size_t ins_count;
105 14 begin_change(CHANGE_MERGE_NONE);
106
107
2/2
✓ Branch 0 taken 8 times.
✓ Branch 1 taken 6 times.
14 if (ins) {
108 8 ins_count = strlen(ins);
109 8 memmove(ins + 1, ins, ins_count);
110 8 ins[0] = '\n'; // Add newline before indent
111 8 ins_count++;
112 8 buffer_replace_bytes(view, del_count, ins, ins_count);
113 8 free(ins);
114 } else {
115 6 ins_count = 1;
116 6 buffer_replace_bytes(view, del_count, "\n", ins_count);
117 }
118
119 14 end_change();
120 14 block_iter_skip_bytes(&view->cursor, ins_count); // Move after inserted text
121 14 }
122
123 static int get_indent_of_matching_brace(const View *view)
124 {
125 unsigned int tab_width = view->buffer->options.tab_width;
126 BlockIter bi = view->cursor;
127 StringView line;
128 int level = 0;
129
130 while (block_iter_prev_line(&bi)) {
131 fetch_this_line(&bi, &line);
132 if (line_has_opening_brace(line)) {
133 if (level++ == 0) {
134 return get_indent_width(&line, tab_width);
135 }
136 }
137 if (line_has_closing_brace(line)) {
138 level--;
139 }
140 }
141
142 return -1;
143 }
144
145 77 void insert_ch(View *view, CodePoint ch)
146 {
147
2/2
✓ Branch 0 taken 14 times.
✓ Branch 1 taken 63 times.
77 if (ch == '\n') {
148 14 insert_nl(view);
149 14 return;
150 }
151
152 63 const Buffer *buffer = view->buffer;
153 63 const LocalOptions *options = &buffer->options;
154 63 char buf[8];
155 63 char *ins = buf;
156 63 char *alloc = NULL;
157 63 size_t del_count = 0;
158 63 size_t ins_count = 0;
159
160
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 63 times.
63 if (view->selection) {
161 // Prepare deleted text (selection)
162 del_count = prepare_selection(view);
163 unselect(view);
164
1/2
✗ Branch 0 not taken.
✓ Branch 1 taken 63 times.
63 } else if (options->overwrite) {
165 // Delete character under cursor unless we're at end of line
166 BlockIter bi = view->cursor;
167 del_count = block_iter_is_eol(&bi) ? 0 : block_iter_next_column(&bi);
168
1/6
✗ Branch 0 not taken.
✓ Branch 1 taken 63 times.
✗ Branch 2 not taken.
✗ Branch 3 not taken.
✗ Branch 4 not taken.
✗ Branch 5 not taken.
63 } else if (ch == '}' && options->auto_indent && options->brace_indent) {
169 StringView curlr;
170 fetch_this_line(&view->cursor, &curlr);
171 if (strview_isblank(&curlr)) {
172 int width = get_indent_of_matching_brace(view);
173 if (width >= 0) {
174 // Replace current (ws only) line with some indent + '}'
175 block_iter_bol(&view->cursor);
176 del_count = curlr.length;
177 if (width) {
178 alloc = make_indent(options, width);
179 ins = alloc;
180 ins_count = strlen(ins);
181 // '}' will be replace the terminating NUL
182 }
183 }
184 }
185 }
186
187 // Prepare inserted text
188
3/4
✓ Branch 0 taken 11 times.
✓ Branch 1 taken 52 times.
✓ Branch 2 taken 11 times.
✗ Branch 3 not taken.
63 if (ch == '\t' && options->expand_tab) {
189 11 ins_count = options->indent_width;
190 11 static_assert(sizeof(buf) >= INDENT_WIDTH_MAX);
191 11 memset(ins, ' ', ins_count);
192 } else {
193 52 ins_count += u_set_char_raw(ins + ins_count, ch);
194 }
195
196 // Record change
197 63 begin_change(del_count ? CHANGE_MERGE_NONE : CHANGE_MERGE_INSERT);
198 63 buffer_replace_bytes(view, del_count, ins, ins_count);
199 63 end_change();
200 63 free(alloc);
201
202 // Move after inserted text
203 63 block_iter_skip_bytes(&view->cursor, ins_count);
204 }
205