dte test coverage


Directory: ./
File: src/insert.c
Date: 2025-05-08 15:05:54
Exec Total Coverage
Lines: 100 109 91.7%
Functions: 7 7 100.0%
Branches: 39 58 67.2%

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