[READ-ONLY] Mirror of https://github.com/bombshell-dev/tty. Platform independent 2D layout engine for terminal applications based on Clay
5

Configure Feed

Select the types of activity you want to include in your feed.

feat(render): implement REP CSI Ps b

Nate Moore (Jun 26, 2026, 11:55 PM EDT) a24cd2bf 7f87619a

+88 -22
+88 -22
src/clayterm.c
··· 167 167 buf_put(&ct->out, "H", 1); 168 168 } 169 169 170 + /* decimal digits in n (n >= 0) */ 171 + static int num_digits(int n) { 172 + int d = 1; 173 + while (n >= 10) { 174 + n /= 10; 175 + d++; 176 + } 177 + return d; 178 + } 179 + 180 + /* glyph actually written for a cell: non-printable -> U+FFFD (see emit_ch) */ 181 + static uint32_t glyph_cp(uint32_t ch) { return iswprint(ch) ? ch : 0xfffd; } 182 + 183 + /* UTF-8 byte length of a codepoint, matching buf_char's encoding */ 184 + static int cp_utf8_len(uint32_t ch) { 185 + if (ch < 0x80) 186 + return 1; 187 + if (ch < 0x800) 188 + return 2; 189 + if (ch < 0x10000) 190 + return 3; 191 + return 4; 192 + } 193 + 170 194 static void emit_ch(struct Clayterm *ct, int x, int y, int row, uint32_t ch) { 171 195 if (ct->lastx != x - 1 || ct->lasty != y) { 172 196 emit_cursor(ct, x, y, row); ··· 174 198 ct->lastx = x; 175 199 ct->lasty = y; 176 200 177 - if (!iswprint(ch)) 178 - ch = 0xfffd; 179 - buf_char(&ct->out, ch); 201 + buf_char(&ct->out, glyph_cp(ch)); 180 202 } 181 203 182 204 /** ··· 198 220 if (w < 1) 199 221 w = 1; 200 222 201 - if (cell_cmp(back, front)) { 202 - /* copy to front */ 203 - *front = *back; 223 + if (!cell_cmp(back, front)) { 224 + x += w; 225 + continue; 226 + } 204 227 205 - emit_attr(ct, back->fg, back->bg); 228 + emit_attr(ct, back->fg, back->bg); 206 229 207 - if (w > 1 && x >= ct->w - (w - 1)) { 208 - /* wide char doesn't fit, send spaces */ 209 - for (int i = x; i < ct->w; i++) 210 - emit_ch(ct, i, y, row, ' '); 211 - } else { 212 - emit_ch(ct, x, y, row, back->ch); 213 - /* mark trailing cells of wide char as invalid in front 214 - * so they'll diff when overwritten by narrow chars */ 215 - for (int i = 1; i < w; i++) { 216 - Cell *fw = cell_at(ct, ct->front, x + i, y); 217 - fw->ch = 0xffffffff; 218 - fw->fg = 0xffffffff; 219 - fw->bg = 0xffffffff; 220 - } 230 + if (w > 1 && x >= ct->w - (w - 1)) { 231 + /* wide char doesn't fit, send spaces */ 232 + *front = *back; 233 + for (int i = x; i < ct->w; i++) 234 + emit_ch(ct, i, y, row, ' '); 235 + x += w; 236 + continue; 237 + } 238 + 239 + if (w > 1) { 240 + *front = *back; 241 + emit_ch(ct, x, y, row, back->ch); 242 + /* mark trailing cells of wide char as invalid in front 243 + * so they'll diff when overwritten by narrow chars */ 244 + for (int i = 1; i < w; i++) { 245 + Cell *fw = cell_at(ct, ct->front, x + i, y); 246 + fw->ch = 0xffffffff; 247 + fw->fg = 0xffffffff; 248 + fw->bg = 0xffffffff; 221 249 } 250 + x += w; 251 + continue; 222 252 } 223 - x += w; 253 + 254 + /* width-1 cell: extend a run of identical changed cells so an 255 + * identical horizontal span collapses to one glyph + REP (CSI b). */ 256 + int run = 1; 257 + for (int nx = x + 1; nx < ct->w; nx++) { 258 + Cell *nb = cell_at(ct, ct->back, nx, y); 259 + Cell *nf = cell_at(ct, ct->front, nx, y); 260 + int nw = wcwidth(nb->ch); 261 + if (nw > 1) 262 + break; 263 + if (!cell_cmp(nb, nf)) 264 + break; 265 + if (cell_cmp(nb, back)) 266 + break; 267 + run++; 268 + } 269 + 270 + /* copy the whole run to the front buffer */ 271 + for (int i = 0; i < run; i++) 272 + *cell_at(ct, ct->front, x + i, y) = *back; 273 + 274 + int repeats = run - 1; 275 + int cb = cp_utf8_len(glyph_cp(back->ch)); 276 + /* inline = run*cb bytes; REP = cb + len("\x1b[") + digits + len("b"). 277 + * Only collapse when REP is strictly smaller. */ 278 + if (repeats >= 1 && run * cb > cb + 3 + num_digits(repeats)) { 279 + emit_ch(ct, x, y, row, back->ch); 280 + buf_str(&ct->out, "\x1b["); 281 + buf_num(&ct->out, repeats); 282 + buf_put(&ct->out, "b", 1); 283 + ct->lastx = x + run - 1; 284 + ct->lasty = y; 285 + } else { 286 + for (int i = 0; i < run; i++) 287 + emit_ch(ct, x + i, y, row, back->ch); 288 + } 289 + x += run; 224 290 } 225 291 } 226 292 }