dte test coverage


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 50.0% high: ≥ 85.0%
Coverage Exec / Excl / Total
Lines: 85.1% 114 / 0 / 134
Functions: 100.0% 3 / 0 / 3
Branches: 71.2% 47 / 4 / 70

src/replace.c
Line Branch Exec Source
1 #include <stdlib.h>
2 #include "replace.h"
3 #include "buffer.h"
4 #include "change.h"
5 #include "command/error.h"
6 #include "editor.h"
7 #include "regexp.h"
8 #include "selection.h"
9 #include "ui.h"
10 #include "util/debug.h"
11 #include "util/string.h"
12 #include "util/xmalloc.h"
13 #include "view.h"
14 #include "window.h"
15
16 24 static void build_replacement (
17 String *buf,
18 const char *line,
19 const char *format,
20 const regmatch_t *matches
21 ) {
22
2/2
✓ Branch 15 → 3 taken 50 times.
✓ Branch 15 → 16 taken 24 times.
74 for (size_t i = 0; format[i]; ) {
23 50 char ch = format[i++];
24 50 size_t match_idx;
25
2/2
✓ Branch 3 → 4 taken 11 times.
✓ Branch 3 → 9 taken 39 times.
50 if (ch == '\\') {
26
1/2
✓ Branch 4 → 5 taken 11 times.
✗ Branch 4 → 16 not taken.
11 if (unlikely(format[i] == '\0')) {
27 break;
28 }
29 11 ch = format[i++];
30
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 8 taken 11 times.
11 if (ch < '1' || ch > '9') {
31 string_append_byte(buf, ch);
32 continue;
33 }
34 11 match_idx = ch - '0';
35
2/2
✓ Branch 9 → 10 taken 27 times.
✓ Branch 9 → 12 taken 12 times.
39 } else if (ch == '&') {
36 match_idx = 0;
37 } else {
38 27 string_append_byte(buf, ch);
39 27 continue;
40 }
41 23 const regmatch_t *match = &matches[match_idx];
42 23 regoff_t len = match->rm_eo - match->rm_so;
43
1/2
✓ Branch 12 → 13 taken 23 times.
✗ Branch 12 → 14 not taken.
23 if (len > 0) {
44 23 string_append_buf(buf, line + match->rm_so, (size_t)len);
45 }
46 }
47 24 }
48
49 /*
50 * s/abc/x
51 *
52 * string to match against
53 * -------------------------------------------
54 * "foo abc bar abc baz" "foo abc bar abc baz"
55 * "foo x bar abc baz" " bar abc baz"
56 */
57 21 static unsigned int replace_on_line (
58 EditorState *e,
59 StringView line,
60 regex_t *re,
61 const char *format,
62 BlockIter *bi,
63 ReplaceFlags *flagsp
64 ) {
65
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 6 taken 21 times.
21 if (!line.data) {
66 BUG_ON(line.length);
67 line.data = "";
68 }
69
70 21 const char *buf = line.data;
71 21 View *view = e->view;
72 21 char *alloc = NULL;
73 21 ReplaceFlags flags = *flagsp;
74 21 regmatch_t matches[32];
75 21 size_t pos = 0;
76 21 int eflags = 0;
77 21 unsigned int nr = 0;
78
79
2/2
✓ Branch 32 → 7 taken 24 times.
✓ Branch 32 → 33 taken 9 times.
54 while (regexp_exec (
80 re,
81 buf + pos,
82 33 line.length - pos,
83 ARRAYLEN(matches),
84 matches,
85 eflags
86 )) {
87 24 regoff_t match_len = matches[0].rm_eo - matches[0].rm_so;
88 24 bool skip = false;
89
90 // Move cursor to beginning of the text to replace
91 24 block_iter_skip_bytes(bi, matches[0].rm_so);
92 24 view->cursor = *bi;
93
94
1/2
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 14 taken 24 times.
24 if (flags & REPLACE_CONFIRM) {
95 e->screen_update |= UPDATE_CURRENT_BUFFER;
96 switch (status_prompt(e, "Replace? [Y/n/a/q]", "ynaq")) {
97 case 'y':
98 break;
99 case 'n':
100 skip = true;
101 break;
102 case 'a':
103 flags &= ~REPLACE_CONFIRM;
104 *flagsp = flags;
105
106 // Record rest of the changes as one chain
107 begin_change_chain();
108 break;
109 case 'q':
110 case 0:
111 *flagsp = flags | REPLACE_CANCEL;
112 goto out;
113 }
114 }
115
116 if (skip) {
117 // Move cursor after the matched text
118 block_iter_skip_bytes(&view->cursor, match_len);
119 } else {
120 24 String b = STRING_INIT;
121 24 build_replacement(&b, buf + pos, format, matches);
122
123 // line ref is invalidated by modification
124
3/4
✓ Branch 15 → 16 taken 14 times.
✓ Branch 15 → 20 taken 10 times.
✓ Branch 16 → 17 taken 14 times.
✗ Branch 16 → 20 not taken.
24 if (buf == line.data && line.length != 0) {
125 14 BUG_ON(alloc);
126 14 alloc = xmemdup(buf, line.length);
127 14 buf = alloc;
128 }
129
130 24 buffer_replace_bytes(view, match_len, b.buffer, b.len);
131 24 nr++;
132
133 // Update selection length
134
2/2
✓ Branch 21 → 22 taken 6 times.
✓ Branch 21 → 23 taken 18 times.
24 if (view->selection) {
135 6 view->sel_eo += b.len;
136 6 view->sel_eo -= match_len;
137 }
138
139 // Move cursor after the replaced text
140 24 block_iter_skip_bytes(&view->cursor, b.len);
141 24 string_free(&b);
142 }
143 24 *bi = view->cursor;
144
145
1/2
✗ Branch 26 → 27 not taken.
✓ Branch 26 → 28 taken 24 times.
24 if (!match_len) {
146 break;
147 }
148
149
2/2
✓ Branch 28 → 29 taken 12 times.
✓ Branch 28 → 30 taken 12 times.
24 if (!(flags & REPLACE_GLOBAL)) {
150 break;
151 }
152
153 12 pos += matches[0].rm_so + match_len;
154
155 // Don't match beginning of line again
156 12 eflags = REG_NOTBOL;
157 }
158
159 21 out:
160 21 free(alloc);
161 21 return nr;
162 }
163
164 12 bool reg_replace(EditorState *e, const char *pattern, const char *format, ReplaceFlags flags)
165 {
166 12 ErrorBuffer *ebuf = &e->err;
167
2/2
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 4 taken 11 times.
12 if (unlikely(pattern[0] == '\0')) {
168 1 return error_msg(ebuf, "Search pattern must contain at least 1 character");
169 }
170
171 11 int re_flags = REG_NEWLINE;
172 11 re_flags |= (flags & REPLACE_IGNORE_CASE) ? REG_ICASE : 0;
173 11 re_flags |= (flags & REPLACE_BASIC) ? 0 : DEFAULT_REGEX_FLAGS;
174
175 11 regex_t re;
176 11 int err = regcomp(&re, pattern, re_flags);
177
1/2
✗ Branch 5 → 6 not taken.
✓ Branch 5 → 7 taken 11 times.
11 if (unlikely(err)) {
178 return regexp_error_msg(ebuf, &re, pattern, err);
179 }
180
181 11 View *view = e->view;
182 11 size_t nr_bytes = 0;
183 11 BlockIter bi;
184 11 bool swapped;
185
2/2
✓ Branch 7 → 8 taken 2 times.
✓ Branch 7 → 10 taken 9 times.
11 if (view->selection) {
186 2 SelectionInfo info = init_selection(view);
187 2 bi = info.si;
188 2 view->cursor = info.si;
189 2 view->sel_so = info.so;
190 2 view->sel_eo = info.eo;
191 2 nr_bytes = info.eo - info.so;
192 2 swapped = info.swapped;
193 } else {
194 9 const Block *blk;
195
2/2
✓ Branch 12 → 11 taken 9 times.
✓ Branch 12 → 13 taken 9 times.
18 block_for_each(blk, &view->buffer->blocks) {
196 9 nr_bytes += blk->size;
197 }
198 9 bi = block_iter(view->buffer); // BOF
199 9 swapped = false;
200 }
201
202 // Record multiple changes as one chain only when replacing all
203
1/2
✓ Branch 14 → 15 taken 11 times.
✗ Branch 14 → 16 not taken.
11 if (!(flags & REPLACE_CONFIRM)) {
204 11 begin_change_chain();
205 }
206
207 11 unsigned int nr_substitutions = 0;
208 11 size_t nr_lines = 0;
209 10 while (1) {
210 21 StringView line = block_iter_get_line(&bi);
211
212 // Number of bytes to process
213 21 size_t count = line.length;
214
1/2
✗ Branch 18 → 19 not taken.
✓ Branch 18 → 20 taken 21 times.
21 if (line.length > nr_bytes) {
215 // End of selection is not full line
216 line.length = nr_bytes;
217 }
218
219 21 unsigned int nr = replace_on_line(e, line, &re, format, &bi, &flags);
220
2/2
✓ Branch 21 → 22 taken 14 times.
✓ Branch 21 → 23 taken 7 times.
21 if (nr) {
221 14 nr_substitutions += nr;
222 14 nr_lines++;
223 }
224
225
3/4
✓ Branch 23 → 24 taken 21 times.
✗ Branch 23 → 27 not taken.
✓ Branch 24 → 25 taken 10 times.
✓ Branch 24 → 27 taken 11 times.
21 if (flags & REPLACE_CANCEL || count + 1 >= nr_bytes) {
226 break;
227 }
228
229 10 nr_bytes -= count + 1;
230 10 block_iter_next_line(&bi);
231 }
232
233
1/2
✓ Branch 27 → 28 taken 11 times.
✗ Branch 27 → 29 not taken.
11 if (!(flags & REPLACE_CONFIRM)) {
234 11 end_change_chain(view);
235 }
236
237 11 regfree(&re);
238
239
2/2
✓ Branch 30 → 31 taken 10 times.
✓ Branch 30 → 36 taken 1 time.
11 if (nr_substitutions) {
240
4/4
✓ Branch 31 → 32 taken 8 times.
✓ Branch 31 → 33 taken 2 times.
✓ Branch 33 → 34 taken 6 times.
✓ Branch 33 → 35 taken 4 times.
24 info_msg (
241 ebuf,
242 "%u substitution%s on %zu line%s",
243 nr_substitutions,
244 (nr_substitutions > 1) ? "s" : "",
245 nr_lines,
246 (nr_lines > 1) ? "s" : ""
247 );
248
1/2
✓ Branch 36 → 37 taken 1 time.
✗ Branch 36 → 38 not taken.
1 } else if (!(flags & REPLACE_CANCEL)) {
249 1 error_msg(ebuf, "Pattern '%s' not found", pattern);
250 }
251
252
2/2
✓ Branch 38 → 39 taken 2 times.
✓ Branch 38 → 45 taken 9 times.
11 if (view->selection) {
253 // Undo what init_selection() did
254
1/2
✓ Branch 39 → 40 taken 2 times.
✗ Branch 39 → 41 not taken.
2 if (view->sel_eo) {
255 2 view->sel_eo--;
256 }
257
1/2
✓ Branch 41 → 42 taken 2 times.
✗ Branch 41 → 43 not taken.
2 if (swapped) {
258 2 ssize_t tmp = view->sel_so;
259 2 view->sel_so = view->sel_eo;
260 2 view->sel_eo = tmp;
261 }
262 2 block_iter_goto_offset(&view->cursor, view->sel_eo);
263 2 view->sel_eo = SEL_EO_RECALC;
264 }
265
266 11 return (nr_substitutions > 0);
267 }
268