dte test coverage


Directory: ./
File: src/indent.c
Date: 2025-05-08 15:05:54
Exec Total Coverage
Lines: 102 114 89.5%
Functions: 8 8 100.0%
Branches: 44 54 81.5%

Line Branch Exec Source
1 #include <string.h>
2 #include <sys/types.h>
3 #include "indent.h"
4 #include "regexp.h"
5 #include "util/xmalloc.h"
6
7 44 char *make_indent(const LocalOptions *options, size_t width)
8 {
9
2/2
✓ Branch 0 (2→3) taken 27 times.
✓ Branch 1 (2→10) taken 17 times.
44 if (width == 0) {
10 return NULL;
11 }
12
13
2/2
✓ Branch 0 (3→4) taken 23 times.
✓ Branch 1 (3→6) taken 4 times.
27 if (use_spaces_for_indent(options)) {
14 23 char *str = xmalloc(width + 1);
15 23 str[width] = '\0';
16 23 return memset(str, ' ', width);
17 }
18
19 4 size_t tw = options->tab_width;
20 4 size_t ntabs = indent_level(width, tw);
21 4 size_t nspaces = indent_remainder(width, tw);
22 4 size_t n = ntabs + nspaces;
23 4 char *str = xmalloc(n + 1);
24 4 memset(str + ntabs, ' ', nspaces);
25 4 str[n] = '\0';
26 4 return memset(str, '\t', ntabs);
27 }
28
29 // Return true if the contents of `line` triggers an additional level
30 // of auto-indent on the next line
31 36 static bool line_contents_increases_indent (
32 const LocalOptions *options,
33 const StringView *line
34 ) {
35 36 static regex_t re1, re2;
36 36 static bool compiled;
37
2/2
✓ Branch 0 (2→3) taken 1 times.
✓ Branch 1 (2→6) taken 35 times.
36 if (!compiled) {
38 // TODO: Make these patterns configurable via a local option
39 1 static const char pat1[] = "\\{[\t ]*(//.*|/\\*.*\\*/[\t ]*)?$";
40 1 static const char pat2[] = "\\}[\t ]*(//.*|/\\*.*\\*/[\t ]*)?$";
41 1 regexp_compile_or_fatal_error(&re1, pat1, REG_NEWLINE | REG_NOSUB);
42 1 regexp_compile_or_fatal_error(&re2, pat2, REG_NEWLINE | REG_NOSUB);
43 1 compiled = true;
44 }
45
46
2/2
✓ Branch 0 (6→7) taken 13 times.
✓ Branch 1 (6→13) taken 23 times.
36 if (options->brace_indent) {
47 13 regmatch_t m;
48
2/2
✓ Branch 0 (8→9) taken 12 times.
✓ Branch 1 (8→12) taken 1 times.
13 if (regexp_exec(&re1, line->data, line->length, 0, &m, 0)) {
49 1 return true;
50 }
51
1/2
✓ Branch 0 (10→11) taken 12 times.
✗ Branch 1 (10→12) not taken.
12 if (regexp_exec(&re2, line->data, line->length, 0, &m, 0)) {
52 return false;
53 }
54 }
55
56 35 const InternedRegexp *ir = options->indent_regex;
57
2/2
✓ Branch 0 (13→14) taken 2 times.
✓ Branch 1 (13→17) taken 33 times.
35 if (!ir) {
58 return false;
59 }
60
61 2 BUG_ON(ir->str[0] == '\0');
62 2 regmatch_t m;
63 2 return regexp_exec(&ir->re, line->data, line->length, 0, &m, 0);
64 }
65
66 36 char *get_indent_for_next_line(const LocalOptions *options, const StringView *line)
67 {
68 36 size_t curr_width = get_indent_width(line, options->tab_width);
69 36 size_t next_width = next_indent_width(curr_width, options->indent_width);
70 36 bool increase = line_contents_increases_indent(options, line);
71
2/2
✓ Branch 0 (5→6) taken 34 times.
✓ Branch 1 (5→7) taken 2 times.
70 return make_indent(options, increase ? next_width : curr_width);
72 }
73
74 64 IndentInfo get_indent_info(const LocalOptions *options, const StringView *line)
75 {
76 64 const char *buf = line->data;
77 64 const size_t len = line->length;
78 64 const size_t tw = options->tab_width;
79 64 const size_t iw = options->indent_width;
80 64 const bool space_indent = use_spaces_for_indent(options);
81 64 IndentInfo info = {.sane = true};
82 64 size_t spaces = 0;
83 64 size_t tabs = 0;
84 64 size_t pos = 0;
85
86
2/2
✓ Branch 0 (16→3) taken 631 times.
✓ Branch 1 (16→17) taken 5 times.
636 for (; pos < len; pos++) {
87
2/2
✓ Branch 0 (3→4) taken 564 times.
✓ Branch 1 (3→5) taken 67 times.
631 if (buf[pos] == ' ') {
88 564 info.width++;
89 564 spaces++;
90
2/2
✓ Branch 0 (5→6) taken 8 times.
✓ Branch 1 (5→17) taken 59 times.
67 } else if (buf[pos] == '\t') {
91 8 info.width = next_indent_width(info.width, tw);
92 8 tabs++;
93 } else {
94 break;
95 }
96
4/4
✓ Branch 0 (9→10) taken 146 times.
✓ Branch 1 (9→15) taken 426 times.
✓ Branch 2 (10→11) taken 145 times.
✓ Branch 3 (10→15) taken 1 times.
572 if (indent_remainder(info.width, iw) == 0 && info.sane) {
97
2/2
✓ Branch 0 (11→12) taken 137 times.
✓ Branch 1 (11→13) taken 8 times.
145 info.sane = space_indent ? !tabs : !spaces;
98 }
99 }
100
101 64 info.level = indent_level(info.width, iw);
102 64 info.wsonly = (pos == len);
103 64 info.bytes = spaces + tabs;
104 64 return info;
105 }
106
107 44 size_t get_indent_width(const StringView *line, unsigned int tab_width)
108 {
109 44 const char *buf = line->data;
110 44 size_t width = 0;
111
2/2
✓ Branch 0 (8→3) taken 129 times.
✓ Branch 1 (8→9) taken 3 times.
132 for (size_t i = 0, n = line->length; i < n; i++) {
112
2/2
✓ Branch 0 (3→4) taken 88 times.
✓ Branch 1 (3→5) taken 41 times.
129 if (buf[i] == ' ') {
113 88 width++;
114
1/2
✗ Branch 0 (5→6) not taken.
✓ Branch 1 (5→9) taken 41 times.
41 } else if (buf[i] == '\t') {
115 width = next_indent_width(width, tab_width);
116 } else {
117 break;
118 }
119 }
120 44 return width;
121 }
122
123 22 static ssize_t get_current_indent_bytes (
124 const char *buf,
125 size_t cursor_offset,
126 unsigned int iw,
127 unsigned int tw
128 ) {
129 22 size_t bytes = 0;
130 22 size_t width = 0;
131
132
2/2
✓ Branch 0 (11→3) taken 20 times.
✓ Branch 1 (11→12) taken 2 times.
22 for (size_t i = 0; i < cursor_offset; i++) {
133
1/2
✓ Branch 0 (4→5) taken 20 times.
✗ Branch 1 (4→6) not taken.
20 if (indent_remainder(width, iw) == 0) {
134 20 bytes = 0;
135 20 width = 0;
136 }
137
1/3
✗ Branch 0 (6→7) not taken.
✗ Branch 1 (6→9) not taken.
✓ Branch 2 (6→15) taken 20 times.
20 switch (buf[i]) {
138 case '\t':
139 width = next_indent_width(width, tw);
140 break;
141 case ' ':
142 width++;
143 break;
144 default:
145 // Cursor not at indentation
146 return -1;
147 }
148 bytes++;
149 }
150
151
1/2
✓ Branch 0 (13→14) taken 2 times.
✗ Branch 1 (13→15) not taken.
2 if (indent_remainder(width, iw)) {
152 // Cursor at middle of indentation level
153 return -1;
154 }
155
156 2 return (ssize_t)bytes;
157 }
158
159 3 size_t get_indent_level_bytes_left(const LocalOptions *options, const BlockIter *cursor)
160 {
161 3 BlockIter bol = *cursor;
162 3 size_t cursor_offset = block_iter_bol(&bol);
163
1/2
✓ Branch 0 (3→4) taken 3 times.
✗ Branch 1 (3→7) not taken.
3 if (cursor_offset == 0) {
164 return 0; // cursor at BOL
165 }
166
167 3 StringView line = block_iter_get_line(&bol);
168 3 unsigned int iw = options->indent_width;
169 3 unsigned int tw = options->tab_width;
170 3 ssize_t ibytes = get_current_indent_bytes(line.data, cursor_offset, iw, tw);
171 3 return MAX(ibytes, 0);
172 }
173
174 19 size_t get_indent_level_bytes_right(const LocalOptions *options, const BlockIter *cursor)
175 {
176 19 unsigned int iw = options->indent_width;
177 19 unsigned int tw = options->tab_width;
178 19 StringView line;
179 19 size_t cursor_offset = get_current_line_and_offset(cursor, &line);
180 19 ssize_t ibytes = get_current_indent_bytes(line.data, cursor_offset, iw, tw);
181
2/2
✓ Branch 0 (4→5) taken 2 times.
✓ Branch 1 (4→15) taken 17 times.
19 if (ibytes < 0) {
182 return 0;
183 }
184
185 2 size_t width = 0;
186
1/2
✓ Branch 0 (14→6) taken 3 times.
✗ Branch 1 (14→15) not taken.
3 for (size_t i = cursor_offset, n = line.length; i < n; i++) {
187
2/3
✗ Branch 0 (6→7) not taken.
✓ Branch 1 (6→9) taken 1 times.
✓ Branch 2 (6→15) taken 2 times.
3 switch (line.data[i]) {
188 case '\t':
189 width = next_indent_width(width, tw);
190 break;
191 1 case ' ':
192 1 width++;
193 1 break;
194 default:
195 // No full indentation level at cursor position
196 return 0;
197 }
198
1/2
✗ Branch 0 (11→12) not taken.
✓ Branch 1 (11→13) taken 1 times.
1 if (indent_remainder(width, iw) == 0) {
199 return i - cursor_offset + 1;
200 }
201 }
202
203 return 0;
204 }
205