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