File Coverage

include/eshu_css.h
Criterion Covered Total %
statement 98 100 98.0
branch 65 80 81.2
condition n/a
subroutine n/a
pod n/a
total 163 180 90.5


line stmt bran cond sub pod time code
1             /*
2             * eshu_css.h — CSS indentation scanner
3             *
4             * Brace-based nesting like C, but with CSS-specific quoting,
5             * comment syntax (no // line comments), and url() skipping.
6             */
7              
8             #ifndef ESHU_CSS_H
9             #define ESHU_CSS_H
10              
11             #include "eshu.h"
12              
13             /* ══════════════════════════════════════════════════════════════════
14             * Scanner context
15             * ══════════════════════════════════════════════════════════════════ */
16              
17             typedef struct {
18             int depth;
19             enum eshu_state state;
20             eshu_config_t cfg;
21             } eshu_css_ctx_t;
22              
23 31           static void eshu_css_ctx_init(eshu_css_ctx_t *ctx, const eshu_config_t *cfg) {
24 31           ctx->depth = 0;
25 31           ctx->state = ESHU_CODE;
26 31           ctx->cfg = *cfg;
27 31           }
28              
29             /* ══════════════════════════════════════════════════════════════════
30             * Scan a line to compute depth changes
31             *
32             * Sets *pre_adj = depth change BEFORE indenting (leading '}')
33             * Sets *post_adj = net depth change from rest of line
34             * ══════════════════════════════════════════════════════════════════ */
35              
36 166           static void eshu_css_scan_line(eshu_css_ctx_t *ctx,
37             const char *content, const char *eol,
38             int *pre_adj, int *post_adj) {
39 166           const char *p = content;
40 166           int first_close_counted = 0;
41 166           *pre_adj = 0;
42 166           *post_adj = 0;
43              
44 1645 100         while (p < eol) {
45             /* ── Block comment state ── */
46 1479 100         if (ctx->state == ESHU_CSS_COMMENT) {
47 106 100         if (p + 1 < eol && p[0] == '*' && p[1] == '/') {
    100          
    100          
48 7           ctx->state = ESHU_CODE;
49 7           p += 2;
50 7           continue;
51             }
52 99           p++;
53 99           continue;
54             }
55              
56             /* ── Double-quoted string ── */
57 1373 100         if (ctx->state == ESHU_CSS_STRING_DQ) {
58 19 100         if (*p == '\\' && p + 1 < eol) {
    50          
59 2           p += 2; /* skip escaped char */
60 2           continue;
61             }
62 17 100         if (*p == '"') {
63 2           ctx->state = ESHU_CODE;
64             }
65 17           p++;
66 17           continue;
67             }
68              
69             /* ── Single-quoted string ── */
70 1354 100         if (ctx->state == ESHU_CSS_STRING_SQ) {
71 10 50         if (*p == '\\' && p + 1 < eol) {
    0          
72 0           p += 2;
73 0           continue;
74             }
75 10 100         if (*p == '\'') {
76 1           ctx->state = ESHU_CODE;
77             }
78 10           p++;
79 10           continue;
80             }
81              
82             /* ── url() content ── */
83 1344 100         if (ctx->state == ESHU_CSS_URL) {
84 60 100         if (*p == ')') {
85 4           ctx->state = ESHU_CODE;
86             }
87 60           p++;
88 60           continue;
89             }
90              
91             /* ── Normal CODE state ── */
92              
93             /* Block comment: /* */
94 1284 100         if (p + 1 < eol && p[0] == '/' && p[1] == '*') {
    100          
    50          
95 7           ctx->state = ESHU_CSS_COMMENT;
96 7           p += 2;
97 7           continue;
98             }
99              
100             /* Double-quoted string */
101 1277 100         if (*p == '"') {
102 3           ctx->state = ESHU_CSS_STRING_DQ;
103 3           p++;
104 3           continue;
105             }
106              
107             /* Single-quoted string */
108 1274 100         if (*p == '\'') {
109 1           ctx->state = ESHU_CSS_STRING_SQ;
110 1           p++;
111 1           continue;
112             }
113              
114             /* url( — skip content until ) */
115 1273 100         if (p + 3 < eol && p[0] == 'u' && p[1] == 'r' && p[2] == 'l' && p[3] == '(') {
    100          
    100          
    50          
    50          
116 4           ctx->state = ESHU_CSS_URL;
117 4           p += 4;
118 4           continue;
119             }
120              
121             /* Opening brace */
122 1269 100         if (*p == '{') {
123 51           *post_adj += 1;
124 51           p++;
125 51           continue;
126             }
127              
128             /* Closing brace */
129 1218 100         if (*p == '}') {
130 50 100         if (p == content && !first_close_counted) {
    50          
131             /* Leading '}' — apply as pre-adjust */
132 48           *pre_adj -= 1;
133 48           first_close_counted = 1;
134             } else {
135 2           *post_adj -= 1;
136             }
137 50           p++;
138 50           continue;
139             }
140              
141 1168           p++;
142             }
143 166           }
144              
145             /* ══════════════════════════════════════════════════════════════════
146             * Process a single line
147             * ══════════════════════════════════════════════════════════════════ */
148              
149 167           static void eshu_css_process_line(eshu_css_ctx_t *ctx,
150             const char *line, const char *eol,
151             eshu_buf_t *out) {
152 167           const char *content = eshu_skip_leading_ws(line);
153 167           int content_len = (int)(eol - content);
154 167 100         int is_blank = (content == eol || *content == '\n');
    50          
155              
156             /* Blank line */
157 167 100         if (is_blank) {
158 1 50         if (*eol == '\n') eshu_buf_putc(out, '\n');
159 1           return;
160             }
161              
162             /* Determine depth adjustments */
163             {
164 166           int pre_adj = 0, post_adj = 0;
165             int indent_depth;
166              
167             /* Check for leading '}' */
168 166 100         if (*content == '}') {
169 49           pre_adj = -1;
170             }
171              
172 166           indent_depth = ctx->depth + pre_adj;
173 166 50         if (indent_depth < 0) indent_depth = 0;
174              
175             /* Emit indentation + content */
176 166           eshu_emit_indent(out, indent_depth, &ctx->cfg);
177 166           eshu_buf_write_trimmed(out, content, content_len);
178 166 50         if (*eol == '\n') eshu_buf_putc(out, '\n');
179              
180             /* Scan line for full depth changes */
181             {
182 166           int scan_pre = 0, scan_post = 0;
183 166           eshu_css_scan_line(ctx, content, eol, &scan_pre, &scan_post);
184              
185 166           ctx->depth += scan_pre + scan_post;
186 166 50         if (ctx->depth < 0) ctx->depth = 0;
187             }
188             }
189             }
190              
191             /* ══════════════════════════════════════════════════════════════════
192             * Public API
193             * ══════════════════════════════════════════════════════════════════ */
194              
195 31           static char * eshu_indent_css(const char *src, size_t src_len,
196             const eshu_config_t *cfg, size_t *out_len) {
197             eshu_css_ctx_t ctx;
198             eshu_buf_t out;
199 31           const char *p = src;
200 31           const char *end = src + src_len;
201             char *result;
202              
203 31           eshu_css_ctx_init(&ctx, cfg);
204 31           eshu_buf_init(&out, src_len + 256);
205              
206 198 100         while (p < end) {
207 167           const char *eol = eshu_find_eol(p);
208 167           eshu_css_process_line(&ctx, p, eol, &out);
209 167 50         p = (*eol == '\n') ? eol + 1 : eol;
210 167 50         if (p > end) p = end;
211             }
212              
213 31           *out_len = out.len;
214 31           result = out.data;
215 31           return result;
216             }
217              
218             #endif /* ESHU_CSS_H */