dte test coverage


Directory: ./
Coverage: low: ≥ 0% medium: ≥ 50.0% high: ≥ 85.0%
Coverage Exec / Excl / Total
Lines: 90.7% 97 / 0 / 107
Functions: 90.0% 9 / 0 / 10
Branches: 72.7% 48 / 0 / 66

src/search.c
Line Branch Exec Source
1 #include <stdlib.h>
2 #include "search.h"
3 #include "block-iter.h"
4 #include "buffer.h"
5 #include "regexp.h"
6 #include "util/ascii.h"
7 #include "util/xmalloc.h"
8
9 // Recurses at most once
10 // NOLINTNEXTLINE(misc-no-recursion)
11 8 static bool do_search_fwd(BlockIter *bi, const regex_t *regex, bool skip)
12 {
13 8 int flags = block_iter_is_bol(bi) ? 0 : REG_NOTBOL;
14
15 16 do {
16
2/2
✓ Branch 3 → 4 taken 14 times.
✓ Branch 3 → 15 taken 2 times.
16 if (block_iter_is_eof(bi)) {
17 6 return false;
18 }
19
20 14 regmatch_t match;
21 14 StringView line = block_iter_get_line(bi);
22
23 // NOTE: If this is the first iteration then `line.data` contains
24 // a partial line (text starting from the cursor position) and if
25 // `match.rm_so` is 0 then the match is at the beginning of the
26 // text, which is the same as the cursor position.
27
2/2
✓ Branch 6 → 7 taken 4 times.
✓ Branch 6 → 12 taken 10 times.
14 if (regexp_exec(regex, line, 1, &match, flags)) {
28
3/4
✓ Branch 7 → 8 taken 1 time.
✓ Branch 7 → 11 taken 3 times.
✓ Branch 8 → 9 taken 1 time.
✗ Branch 8 → 11 not taken.
4 if (skip && match.rm_so == 0) {
29 // Ignore match at current cursor position
30
31 // It's always safe to skip one byte, because every line
32 // has a newline that's not included in line.data
33 1 block_iter_skip_bytes(bi, MAX(match.rm_eo, 1));
34
35 1 return do_search_fwd(bi, regex, false);
36 }
37
38 3 block_iter_skip_bytes(bi, match.rm_so);
39 3 return true;
40 }
41
42 10 skip = false; // Not at cursor position any more
43 10 flags = 0;
44
2/2
✓ Branch 13 → 3 taken 8 times.
✓ Branch 13 → 16 taken 2 times.
10 } while (block_iter_next_line(bi));
45
46 return false;
47 }
48
49 3 static bool do_search_bwd(BlockIter *bi, const regex_t *regex, ssize_t cx, bool skip)
50 {
51
2/2
✓ Branch 2 → 3 taken 1 time.
✓ Branch 2 → 4 taken 2 times.
3 if (block_iter_is_eof(bi)) {
52 1 goto next;
53 }
54
55 6 do {
56 6 const StringView line = block_iter_get_line(bi);
57 6 StringView slice = line;
58 6 regmatch_t match;
59 6 int flags = 0;
60 6 regoff_t offset = -1;
61 6 regoff_t pos = 0;
62
63
3/4
✓ Branch 12 → 13 taken 8 times.
✗ Branch 12 → 15 not taken.
✓ Branch 14 → 6 taken 3 times.
✓ Branch 14 → 15 taken 5 times.
8 while (pos <= line.length && regexp_exec(regex, slice, 1, &match, flags)) {
64 3 flags = REG_NOTBOL;
65
2/2
✓ Branch 6 → 7 taken 2 times.
✓ Branch 6 → 10 taken 1 time.
3 if (cx >= 0) {
66
2/2
✓ Branch 7 → 8 taken 1 time.
✓ Branch 7 → 15 taken 1 time.
2 if (pos + match.rm_so >= cx) {
67 // Ignore match at or after cursor
68 break;
69 }
70
1/4
✗ Branch 8 → 9 not taken.
✓ Branch 8 → 10 taken 1 time.
✗ Branch 9 → 10 not taken.
✗ Branch 9 → 15 not taken.
1 if (skip && pos + match.rm_eo > cx) {
71 // Search -rw should not find word under cursor
72 break;
73 }
74 }
75
76 // This might be what we want (last match before cursor)
77 2 offset = pos + match.rm_so;
78 2 pos += match.rm_eo;
79 2 slice = strview_from_slice(line.data, pos, line.length);
80
81
1/2
✓ Branch 11 → 12 taken 2 times.
✗ Branch 11 → 15 not taken.
2 if (match.rm_so == match.rm_eo) {
82 // Zero length match
83 break;
84 }
85 }
86
87
2/2
✓ Branch 15 → 16 taken 2 times.
✓ Branch 15 → 18 taken 4 times.
6 if (offset >= 0) {
88 2 block_iter_skip_bytes(bi, offset);
89 2 return true;
90 }
91
92 4 next:
93 5 cx = -1;
94
2/2
✓ Branch 20 → 4 taken 4 times.
✓ Branch 20 → 21 taken 1 time.
5 } while (block_iter_prev_line(bi));
95
96 return false;
97 }
98
99 7 static bool search_fwd(View *view, BlockIter *bi, const regex_t *regex, bool skip)
100 {
101
2/2
✓ Branch 3 → 4 taken 3 times.
✓ Branch 3 → 6 taken 4 times.
7 if (!do_search_fwd(bi, regex, skip)) {
102 return false;
103 }
104
105 3 view->cursor = *bi;
106 3 view->center_on_scroll = true;
107 3 view_reset_preferred_x(view);
108 3 return true;
109 }
110
111 3 static bool search_bwd(View *view, BlockIter *bi, const regex_t *regex, ssize_t cx, bool skip)
112 {
113
2/2
✓ Branch 3 → 4 taken 2 times.
✓ Branch 3 → 6 taken 1 time.
3 if (!do_search_bwd(bi, regex, cx, skip)) {
114 return false;
115 }
116
117 2 view->cursor = *bi;
118 2 view->center_on_scroll = true;
119 2 view_reset_preferred_x(view);
120 2 return true;
121 }
122
123 1 bool search_tag(View *view, ErrorBuffer *ebuf, const char *pattern)
124 {
125 // DEFAULT_REGEX_FLAGS is not used here because pattern has been
126 // escaped by parse_ex_pattern() for use as a POSIX BRE
127 1 regex_t regex;
128 1 int err = regcomp(&regex, pattern, REG_NEWLINE);
129
1/2
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 5 taken 1 time.
1 if (unlikely(err)) {
130 regexp_error_msg(ebuf, &regex, pattern, err);
131 }
132
133 1 BlockIter bi = block_iter(view->buffer);
134 1 bool found = search_fwd(view, &bi, &regex, false);
135 1 regfree(&regex);
136
137
1/2
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 9 taken 1 time.
1 if (!found) {
138 // Don't center view to cursor unnecessarily
139 view->force_center = false;
140 return error_msg(ebuf, "Tag not found");
141 }
142
143 1 view->center_on_scroll = true;
144 1 return true;
145 }
146
147 static bool has_upper(const char *str)
148 {
149 return strview_contains_char_type(strview(str), ASCII_UPPER);
150 }
151
152 5 static bool update_regex(SearchState *search, ErrorBuffer *ebuf, SearchCaseSensitivity cs)
153 {
154 5 const char *pattern = search->pattern;
155
2/6
✓ Branch 2 → 3 taken 5 times.
✗ Branch 2 → 6 not taken.
✗ Branch 3 → 4 not taken.
✓ Branch 3 → 6 taken 5 times.
✗ Branch 4 → 5 not taken.
✗ Branch 4 → 6 not taken.
5 bool icase = (cs == CSS_FALSE) || (cs == CSS_AUTO && !has_upper(pattern));
156 int flags = REG_NEWLINE | (icase ? REG_ICASE : 0);
157
2/2
✓ Branch 6 → 7 taken 3 times.
✓ Branch 6 → 15 taken 2 times.
5 if (flags == search->re_flags) {
158 return true;
159 }
160
161
1/2
✗ Branch 7 → 8 not taken.
✓ Branch 7 → 10 taken 3 times.
3 if (search->re_flags) {
162 regfree(&search->regex);
163 search->re_flags = 0;
164 }
165
166
1/2
✓ Branch 11 → 12 taken 3 times.
✗ Branch 11 → 13 not taken.
3 if (regexp_compile(ebuf, &search->regex, pattern, flags)) {
167 3 search->re_flags = flags;
168 3 return true;
169 }
170
171 regfree(&search->regex);
172 return false;
173 }
174
175 14 void search_free_regexp(SearchState *search)
176 {
177
2/2
✓ Branch 2 → 3 taken 3 times.
✓ Branch 2 → 5 taken 11 times.
14 if (search->re_flags) {
178 3 regfree(&search->regex);
179 3 search->re_flags = 0;
180 }
181 14 free(search->pattern);
182 14 }
183
184 3 void search_set_regexp(SearchState *search, const char *pattern)
185 {
186 3 search_free_regexp(search);
187 3 search->pattern = xstrdup(pattern);
188 3 }
189
190 5 bool do_search_next(View *view, SearchState *search, ErrorBuffer *ebuf, SearchCaseSensitivity cs, bool skip)
191 {
192
1/2
✗ Branch 2 → 3 not taken.
✓ Branch 2 → 4 taken 5 times.
5 if (!search->pattern) {
193 return error_msg(ebuf, "No previous search pattern");
194 }
195
1/2
✓ Branch 5 → 6 taken 5 times.
✗ Branch 5 → 21 not taken.
5 if (!update_regex(search, ebuf, cs)) {
196 return false;
197 }
198
199 5 BlockIter bi = view->cursor;
200 5 regex_t *regex = &search->regex;
201
2/2
✓ Branch 6 → 7 taken 3 times.
✓ Branch 6 → 13 taken 2 times.
5 if (!search->reverse) {
202
1/2
✓ Branch 8 → 9 taken 3 times.
✗ Branch 8 → 21 not taken.
3 if (search_fwd(view, &bi, regex, true)) {
203 return true;
204 }
205 3 block_iter_bof(&bi);
206
2/2
✓ Branch 11 → 12 taken 2 times.
✓ Branch 11 → 20 taken 1 time.
3 if (search_fwd(view, &bi, regex, false)) {
207 2 return info_msg(ebuf, "Continuing at top");
208 }
209 } else {
210 2 size_t cursor_x = block_iter_bol(&bi);
211
2/2
✓ Branch 15 → 16 taken 1 time.
✓ Branch 15 → 21 taken 1 time.
2 if (search_bwd(view, &bi, regex, cursor_x, skip)) {
212 return true;
213 }
214 1 block_iter_eof(&bi);
215
1/2
✓ Branch 18 → 19 taken 1 time.
✗ Branch 18 → 20 not taken.
1 if (search_bwd(view, &bi, regex, -1, false)) {
216 1 return info_msg(ebuf, "Continuing at bottom");
217 }
218 }
219
220 1 return error_msg(ebuf, "Pattern '%s' not found", search->pattern);
221 }
222