syntax.patch (36903B)
1 From e0b72ba7c83465d51e24140506426098a9bf78c7 Mon Sep 17 00:00:00 2001 2 From: Страхиња Радић <sr@strahinja.org> 3 Date: Tue Mar 31 14:06:36 2026 UTC 4 Subject: [PATCH] Add syntax highlighting 5 MIME-Version: 1.0 6 Content-Type: text/plain; charset=UTF-8 7 Content-Transfer-Encoding: 8bit 8 9 --- 10 config.def.h | 7+ 0- 11 draw.c | 618+ 31- 12 draw.h | 76+ 6- 13 sled.c | 82+ 15- 14 4 files changed, 783 insertions(+), 52 deletions(-) 15 16 diff --git a/config.def.h b/config.def.h 17 index 68cc281d8..e0b72ba7c 100644 18 --- a/config.def.h 19 +++ b/config.def.h 20 @@ -234,6 +234,13 @@ static const struct Key command_keys[] = { 21 #endif /* SLED_C_GLOBALS */ 22 23 /* clang-format off */ 24 +static const struct Color syntax_colors[] = { 25 + [SE_DEFAULT] = { .fg = TB_DEFAULT, .bg = TB_DEFAULT }, 26 + [SE_COMMENT] = { .fg = TB_GREEN, .bg = TB_DEFAULT }, 27 + [SE_KEYWORD] = { .fg = TB_CYAN | TB_BOLD, .bg = TB_DEFAULT }, 28 + [SE_OPERATOR] = { .fg = TB_YELLOW | TB_BOLD, .bg = TB_DEFAULT }, 29 + [SE_LITERAL] = { .fg = TB_MAGENTA | TB_BOLD, .bg = TB_DEFAULT }, 30 +}; 31 static const struct StatusSegment status_segments[] = { 32 {.format = "%s%s", 33 .callback = format_filename, 34 diff --git a/draw.c b/draw.c 35 index 68cc281d8..e0b72ba7c 100644 36 --- a/draw.c 37 +++ b/draw.c 38 @@ -3,6 +3,7 @@ 39 * See the file LICENSE for exact copyright and license details. */ 40 41 #include <assert.h> 42 +#include <ctype.h> 43 #include <errno.h> 44 #include <stdio.h> 45 #include <stdlib.h> 46 @@ -14,6 +15,25 @@ 47 #include "util.h" 48 #include "version.h" 49 50 +/* Keyword initializer specification macro */ 51 +#define KW(literal) \ 52 + { \ 53 + .text = literal, .size = sizeof(literal) / sizeof(*literal) \ 54 + } 55 +/* Single-line comment specification macro */ 56 +#define SLC(com) \ 57 + { \ 58 + .start_text = com, .start_size = sizeof(com) / sizeof(*com), \ 59 + .end_text = NULL, .end_size = 0 \ 60 + } 61 +/* Area comment specification macro */ 62 +#define AC(com1, com2) \ 63 + { \ 64 + .start_text = com1, \ 65 + .start_size = sizeof(com1) / sizeof(*com1), .end_text = com2, \ 66 + .end_size = sizeof(com2) / sizeof(*com2) \ 67 + } 68 + 69 enum { 70 UNKNOWN_MODE, 71 COMMAND_MODE, 72 @@ -89,6 +109,95 @@ static const struct { 73 { .boundary = 99, .width = 3 }, 74 { .boundary = 9, .width = 2 } 75 }; 76 +extern const struct Color syntax_colors[]; 77 +const struct Keyword c_keywords[] = { 78 + KW("auto"), KW("break"), KW("case"), KW("char"), KW("const"), 79 + KW("continue"), KW("default"), KW("do"), KW("double"), KW("else"), 80 + KW("enum"), KW("extern"), KW("float"), KW("for"), KW("goto"), 81 + KW("if"), KW("inline"), KW("int"), KW("long"), KW("register"), 82 + KW("restrict"), KW("return"), KW("short"), KW("signed"), 83 + KW("sizeof"), KW("static"), KW("struct"), KW("switch"), 84 + KW("typedef"), KW("union"), KW("unsigned"), KW("void"), 85 + KW("volatile"), KW("while"), KW("_Bool"), KW("_Complex"), 86 + KW("_Imaginary"), 87 + { .text = NULL, .size = 0 } 88 +}; 89 +const struct Comment c_comments[] = { 90 + SLC("//"), 91 + AC("/*", "*/"), 92 + { .start_text = NULL, .start_size = 0 } 93 +}; 94 +const struct Operator c_operators[] = { 95 + KW("<<="), KW(">>="), KW("<<"), KW(">>"), 96 + KW("+="), KW("-="), KW("*="), KW("/="), KW("%="), 97 + KW("&="), KW("|="), KW("^="), 98 + KW("=="), KW("!="), KW("<="), KW(">="), 99 + KW("->"), 100 + KW("++"), KW("--"), 101 + KW("&&"), KW("||"), 102 + KW("="), KW("+"), KW("-"), KW("~"), KW("*"), KW("/"), KW("%"), 103 + KW("&"), KW("|"), KW("^"), KW("!"), 104 + KW("<"), KW(">"), 105 + KW(","), KW(";"), KW(":"), KW("?"), KW("."), 106 + KW("("), KW(")"), KW("["), KW("]"), KW("{"), KW("}"), 107 + { .text = NULL, .size = 0 } 108 +}; 109 +const struct Keyword shell_keywords[] = { 110 + KW("!"), KW("{"), KW("}"), 111 + KW("case"), KW("do"), KW("done"), KW("elif"), KW("else"), 112 + KW("esac"), KW("fi"), KW("for"), KW("if"), KW("in"), KW("then"), 113 + KW("until"), KW("while"), KW("[["), KW("]]"), KW("function"), 114 + KW("select"), KW("admin"), KW("alias"), KW("ar"), KW("asa"), 115 + KW("at"), KW("awk"), KW("basename"), KW("batch"), KW("bc"), 116 + KW("bg"), KW("break"), KW("c99"), KW("cal"), KW("cat"), KW("cd"), 117 + KW("cflow"), KW("chgrp"), KW("chmod"), KW("chown"), KW("cksum"), 118 + KW("cmp"), KW("colon"), KW("comm"), KW("command"), KW("compress"), 119 + KW("continue"), KW("cp"), KW("crontab"), KW("csplit"), KW("ctags"), 120 + KW("cut"), KW("cxref"), KW("date"), KW("dd"), KW("delta"), 121 + KW("df"), KW("diff"), KW("dirname"), KW("dot"), KW("du"), 122 + KW("echo"), KW("ed"), KW("env"), KW("eval"), KW("ex"), KW("exec"), 123 + KW("exit"), KW("expand"), KW("export"), KW("expr"), KW("false"), 124 + KW("fc"), KW("fg"), KW("file"), KW("find"), KW("fold"), 125 + KW("fort77"), KW("fuser"), KW("gencat"), KW("get"), KW("getconf"), 126 + KW("getopts"), KW("grep"), KW("hash"), KW("head"), KW("iconv"), 127 + KW("id"), KW("ipcrm"), KW("ipcs"), KW("jobs"), KW("join"), 128 + KW("kill"), KW("lex"), KW("link"), KW("ln"), KW("locale"), 129 + KW("localedef"), KW("logger"), KW("logname"), KW("lp"), KW("ls"), 130 + KW("m4"), KW("mailx"), KW("make"), KW("man"), KW("mesg"), 131 + KW("mkdir"), KW("mkfifo"), KW("more"), KW("mv"), KW("newgrp"), 132 + KW("nice"), KW("nl"), KW("nm"), KW("nohup"), KW("od"), 133 + KW("paste"), KW("patch"), KW("pathchk"), KW("pax"), KW("pr"), 134 + KW("printf"), KW("prs"), KW("ps"), KW("pwd"), KW("qalter"), 135 + KW("qdel"), KW("qhold"), KW("qmove"), KW("qmsg"), KW("qrerun"), 136 + KW("qrls"), KW("qselect"), KW("qsig"), KW("qstat"), KW("qsub"), 137 + KW("read"), KW("readonly"), KW("renice"), KW("return"), KW("rm"), 138 + KW("rmdel"), KW("rmdir"), KW("sact"), KW("sccs"), KW("sed"), 139 + KW("set"), KW("sh"), KW("shift"), KW("sleep"), KW("sort"), 140 + KW("split"), KW("strings"), KW("strip"), KW("stty"), KW("tabs"), 141 + KW("tail"), KW("talk"), KW("tee"), KW("test"), KW("time"), 142 + KW("times"), KW("touch"), KW("tput"), KW("tr"), KW("trap"), 143 + KW("true"), KW("tsort"), KW("tty"), KW("type"), KW("ulimit"), 144 + KW("umask"), KW("unalias"), KW("uname"), KW("uncompress"), 145 + KW("unexpand"), KW("unget"), KW("uniq"), KW("unlink"), KW("unset"), 146 + KW("uucp"), KW("uudecode"), KW("uuencode"), KW("uustat"), 147 + KW("uux"), KW("val"), KW("vi"), KW("wait"), KW("wc"), KW("what"), 148 + KW("who"), KW("write"), KW("xargs"), KW("yacc"), KW("zcat"), 149 + KW("break"), KW(":"), KW("."), KW("continue"), KW("eval"), 150 + KW("exec"), KW("exit"), KW("export"), KW("readonly"), KW("return"), 151 + KW("set"), KW("shift"), KW("times"), KW("trap"), KW("unset"), 152 + { .text = NULL, .size = 0 } 153 +}; 154 +const struct Comment shell_comments[] = { 155 + SLC("#"), 156 + { .start_text = NULL, .start_size = 0 } 157 +}; 158 +const struct Operator shell_operators[] = { 159 + KW("<<"), KW(">>"), 160 + KW("&&"), KW("||"), KW("$"), KW("`"), KW("="), 161 + KW("<"), KW(">"), 162 + KW(";"), KW("("), KW(")"), KW("["), KW("]"), KW("{"), KW("}"), 163 + { .text = NULL, .size = 0 } 164 +}; 165 const char* go_to_dialog_title = "Go to line"; 166 const char* prompt_dirty = "File changed, save [y/n/ESC]?"; 167 const char* prompt_overwrite = "Not empty, overwrite? [y/n/ESC]?"; 168 @@ -97,6 +206,46 @@ const char* replace_dialog_title = "Replace with"; 169 const char* search_dialog_title = "Search text"; 170 /* clang-format on */ 171 172 +/* Set current fg/bg taking into account syntax highlighting. */ 173 +static void 174 +synsetcolor(uint16_t* fg, uint16_t* bg, const struct Color* defcolor, 175 + const struct Color* selection, const int sel_on, 176 + const struct Color* highlight, const int hi_on, 177 + const struct SyntaxRegion* syntax, const ssize_t syntax_length, 178 + const int x) 179 +{ 180 + ssize_t i; 181 + assert((fg != NULL) && (bg != NULL) && (defcolor != NULL) 182 + && (selection != NULL) && (highlight != NULL)); 183 + if (hi_on) 184 + { 185 + *fg = highlight->fg; 186 + *bg = highlight->bg; 187 + return; 188 + } 189 + 190 + if (sel_on) 191 + { 192 + *fg = selection->fg; 193 + *bg = selection->bg; 194 + return; 195 + } 196 + 197 + for (i = 0; i < syntax_length; i++) 198 + { 199 + if ((syntax[i].start <= x) 200 + && (x < syntax[i].start + syntax[i].length)) 201 + { 202 + *fg = syntax[i].color.fg; 203 + *bg = syntax[i].color.bg; 204 + return; 205 + } 206 + } 207 + 208 + *fg = defcolor->fg; 209 + *bg = defcolor->bg; 210 +} 211 + 212 void get_selection(int* ssr, int* ssc, int* ser, int* sec, 213 const struct DrawState* state); 214 215 @@ -270,14 +419,14 @@ draw_help(const struct DrawState* state) 216 217 draw_box(startx, endx, starty, endy, colors[C_DLG].fg, colors[C_DLG].bg); 218 if (draw_string(startx, starty, colors[C_TITLE].fg, colors[C_TITLE].bg, 219 - help_title, strlen(help_title) + 2, w, 1, 1, CENTER, 0, 0, 220 - NULL, 0, 0, 0, 0, 0, 0)) 221 + help_title, NULL, 0, strlen(help_title) + 2, w, 1, 1, 222 + CENTER, 0, 0, NULL, 0, 0, 0, 0, 0, 0)) 223 return 1; 224 while ((size_t)row < LEN(help)) 225 { 226 if (draw_string(startx + 1, starty + 1 + row, colors[C_DLG].fg, 227 - colors[C_DLG].bg, help[row], w - 2, w - 2, 1, 1, 228 - LEFT, 0, 1, NULL, 0, 0, 0, 0, 0, 0)) 229 + colors[C_DLG].bg, help[row], NULL, 0, w - 2, w - 2, 230 + 1, 1, LEFT, 0, 1, NULL, 0, 0, 0, 0, 0, 0)) 231 return 1; 232 row++; 233 } 234 @@ -313,16 +462,16 @@ draw_input_dialog(const struct DrawState* state) 235 colors[C_INPUT_EDIT].fg, colors[C_INPUT_EDIT].bg); 236 if (state->input_title) 237 if (draw_string(startx, starty, colors[C_TITLE].fg, 238 - colors[C_TITLE].bg, state->input_title, 239 + colors[C_TITLE].bg, state->input_title, NULL, 0, 240 strlen(state->input_title) + 2, w, 1, 1, CENTER, 0, 241 0, NULL, 0, 0, 0, 0, 0, 0)) 242 return 1; 243 dlen = display_length(state->input, len); 244 if (dlen > state->input_first_shown_column) 245 u32_draw_string(startx + 1, starty + 1, colors[C_INPUT_EDIT].fg, 246 - colors[C_INPUT_EDIT].bg, state->input, w - 2, w - 2, 1, 247 - 1, LEFT, state->input_first_shown_column, 1, NULL, 0, 0, 248 - 0, 0, 0, 0); 249 + colors[C_INPUT_EDIT].bg, state->input, NULL, 0, w - 2, 250 + w - 2, 1, 1, LEFT, state->input_first_shown_column, 1, 251 + NULL, 0, 0, 0, 0, 0, 0); 252 tb_set_cursor(startx + 2 + state->input_display_column 253 - state->input_first_shown_column, 254 starty + 1); 255 @@ -344,7 +493,7 @@ draw_line_number(const struct DrawState* state, const 256 ? numbuf_end - state->line_numbers_width 257 : numbuf; 258 draw_string(0, cy, colors[C_LINENO].fg, colors[C_LINENO].bg, 259 - pnumbuf, numbuf_end - numbuf + 1, 260 + pnumbuf, NULL, 0, numbuf_end - numbuf + 1, 261 state->line_numbers_width, 0, 0, RIGHT, 0, 1, NULL, 0, 262 0, 0, 0, 0, 0); 263 } 264 @@ -425,8 +574,9 @@ draw_screen(const struct DrawState* state) 265 ? state->line_numbers_width + 2 266 : 0, 267 cy, colors[C_TEXT].fg, colors[C_TEXT].bg, 268 - current->text, maxx, maxx, 0, 0, LEFT, fsc, 0, 269 - state->search, colors[C_SEARCH].fg, 270 + current->text, current->syntax, 271 + current->syntax_size, maxx, maxx, 0, 0, LEFT, 272 + fsc, 0, state->search, colors[C_SEARCH].fg, 273 colors[C_SEARCH].bg, ssi, sei, colors[C_SEL].fg, 274 colors[C_SEL].bg); 275 i++; 276 @@ -458,7 +608,7 @@ draw_status(const struct DrawState* state) 277 if (IN(state->mode, M_PROMPT) && *state->prompt) 278 { 279 if (draw_string(0, state->maxy - 1, colors[C_PROMPT].fg, 280 - colors[C_PROMPT].bg, state->prompt, 281 + colors[C_PROMPT].bg, state->prompt, NULL, 0, 282 strlen(state->prompt) + 2, maxx, 1, 1, CENTER, 0, 1, 283 NULL, 0, 0, 0, 0, 0, 0)) 284 return 1; 285 @@ -466,15 +616,15 @@ draw_status(const struct DrawState* state) 286 else if (state->action) 287 { 288 if (draw_string(0, state->maxy - 1, colors[C_ACTION].fg, 289 - colors[C_ACTION].bg, actions[state->action], 290 - strlen(actions[state->action]) + 2, maxx, 1, 1, 291 + colors[C_ACTION].bg, actions[state->action], NULL, 292 + 0, strlen(actions[state->action]) + 2, maxx, 1, 1, 293 CENTER, 0, 1, NULL, 0, 0, 0, 0, 0, 0)) 294 return 1; 295 } 296 else if (state->error) 297 { 298 if (draw_string(0, state->maxy - 1, colors[C_ERROR].fg, 299 - colors[C_ERROR].bg, errors[state->error], 300 + colors[C_ERROR].bg, errors[state->error], NULL, 0, 301 strlen(errors[state->error]) + 2, maxx, 1, 1, 302 CENTER, 0, 1, NULL, 0, 0, 0, 0, 0, 0)) 303 return 1; 304 @@ -500,7 +650,7 @@ draw_status(const struct DrawState* state) 305 seg_size = maxx - 1 - current_start; 306 if (draw_string(current_start, state->maxy - 1, 307 colors[pseg->color_index].fg, 308 - colors[pseg->color_index].bg, buf, 309 + colors[pseg->color_index].bg, buf, NULL, 0, 310 MIN(seg_size, (ssize_t)(strlen(buf) + 2)), 311 last ? seg_size + 1 : seg_size, 1, 1, 312 pseg->alignment, 0, 1, NULL, 0, 0, 0, 0, 0, 313 @@ -535,8 +685,9 @@ draw_status(const struct DrawState* state) 314 * */ 315 int 316 draw_string(const int x, const int y, const uint16_t fg, const uint16_t bg, 317 - const char* s, const int mc, const int fc, const int p_l, const int p_r, 318 - const Alignment align, const int ds, const int fill, 319 + const char* s, const struct SyntaxRegion* syntax, 320 + const ssize_t syntax_length, const int mc, const int fc, const int p_l, 321 + const int p_r, const Alignment align, const int ds, const int fill, 322 const uint32_t* srch, const uint16_t sfg, const uint16_t sbg, 323 const int ssi, const int sei, const uint16_t slfg, const uint16_t slbg) 324 { 325 @@ -546,8 +697,8 @@ draw_string(const int x, const int y, const uint16_t f 326 if (!us) 327 return 1; 328 u8_string_to_unicode(us, s, mc); 329 - u32_draw_string(x, y, fg, bg, us, mc, fc, p_l, p_r, align, ds, fill, 330 - srch, sfg, sbg, ssi, sei, slfg, slbg); 331 + u32_draw_string(x, y, fg, bg, us, syntax, syntax_length, mc, fc, p_l, 332 + p_r, align, ds, fill, srch, sfg, sbg, ssi, sei, slfg, slbg); 333 free(us); 334 return 0; 335 } 336 @@ -653,12 +804,15 @@ free_bufferline(struct BufferLine* bl) 337 bl->length = 0; 338 bl->fg = 0; 339 bl->bg = 0; 340 + free(bl->syntax); 341 + bl->syntax = NULL; 342 } 343 344 void 345 free_drawstate(struct DrawState* state) 346 { 347 ssize_t row = 0; 348 + 349 assert(state != NULL); 350 for (row = 0; row < state->buffer_size; row++) 351 free_bufferline(state->buffer + row); 352 @@ -672,11 +826,13 @@ init_bufferline(struct BufferLine* bl) 353 { 354 if (!bl) 355 return; 356 - bl->text = NULL; 357 - bl->length = 0; 358 - bl->size = 0; 359 - bl->fg = 0; 360 - bl->bg = 0; 361 + bl->text = NULL; 362 + bl->length = 0; 363 + bl->size = 0; 364 + bl->fg = 0; 365 + bl->bg = 0; 366 + bl->syntax = NULL; 367 + bl->syntax_size = 0; 368 } 369 370 void 371 @@ -701,6 +857,14 @@ init_drawstate(struct DrawState* state, char* filename 372 state->orig_real_filename = NULL; 373 state->backup_filename = NULL; 374 state->rfn_len = 0; 375 + state->recalculate_syntax = 1; 376 + state->syntax_enabled = 0; 377 + state->keywords = NULL; 378 + state->comments = NULL; 379 + state->operators = NULL; 380 + state->ukeywords = NULL; 381 + state->ucomments = NULL; 382 + state->uoperators = NULL; 383 } 384 state->input = NULL; 385 state->search = NULL; 386 @@ -759,7 +923,407 @@ recalculate_size(struct DrawState* state) 387 recalculate_line_numbers(state); 388 } 389 390 +int 391 +syntax_add(struct BufferLine* bl, SyntaxElement type, const ssize_t start, 392 + const ssize_t length) 393 +{ 394 + struct SyntaxRegion* temps = NULL; 395 + struct SyntaxRegion* newreg; 396 + 397 + assert(bl != NULL); 398 + temps = reallocarray(bl->syntax, bl->syntax_size + 1, 399 + sizeof(struct SyntaxRegion)); 400 + if (!temps) 401 + return -1; 402 + bl->syntax = temps; 403 + bl->syntax_size++; 404 + newreg = &bl->syntax[bl->syntax_size - 1]; 405 + newreg->start = start; 406 + newreg->length = length; 407 + newreg->color.fg = syntax_colors[type].fg; 408 + newreg->color.bg = syntax_colors[type].bg; 409 + return 0; 410 +} 411 + 412 +/* 413 + * Regenerate syntax regions from the document text. 414 + * This is more complicated than it should be due to several factors: 415 + * - We are using 32-bit Unicode strings internally (uint32_t*) 416 + * - For non-ASCII, C99 supports only wchar_t string literals L"string" (if we 417 + * don't consider using UTF-8 everywhere, which would complicate everything 418 + * dealing with text and introduce numerous potential bugs) 419 + * - There are some systems out there (Windows) which use 16-bit wchar_t since 420 + * the standard is disappointingly vague, so we can't rely on seamless 421 + * conversion from wchar_t* to uint32_t*. Using code as-is leads to infinite 422 + * loop hangs. 423 + * Thus, we can't use L"literals" to define the list of keywords, operators and 424 + * comment delimiters, so we can either always convert them to uint32_t*, which 425 + * is severely suboptimal performance-wise, or do what seems to be the most 426 + * optimal solution, applied here: convert everything once and keep separate 427 + * arrays with keywords, operators and comment delimiters explicitly converted 428 + * to uint32_t*. This comes at the expense of more code bloat. 429 + */ 430 void 431 +recalculate_syntax(struct DrawState* state) 432 +{ 433 + struct BufferLine* curline; 434 + struct Keyword32* keyword; 435 + struct Comment32* comment; 436 + struct Operator32* operator; 437 + uint32_t* temp32; 438 + ssize_t i, li, ti, comment_start, quote_start, literal_start; 439 + uint32_t quote; 440 + int comment_index, seen_end_comment; 441 + 442 + assert((state != NULL) && (state->buffer != NULL) 443 + && (state->keywords != NULL) && (state->comments != NULL) 444 + && (state->operators != NULL) 445 + && (state->buffer[state->first_shown.row].text != NULL)); 446 + 447 + if (!state->ukeywords) 448 + { 449 + struct Keyword* kw = state->keywords; 450 + struct Keyword32* pklist; 451 + size_t keyword_count = 0; 452 + while (kw->text != NULL) 453 + { 454 + keyword_count++; 455 + kw++; 456 + } 457 + assert(keyword_count > 0); 458 + state->ukeywords 459 + = calloc(keyword_count + 1, sizeof(struct Keyword32)); 460 + if (!state->ukeywords) 461 + { 462 + state->running = 0; 463 + return; 464 + } 465 + pklist = state->ukeywords; 466 + kw = state->keywords; 467 + while (kw->text != NULL) 468 + { 469 + uint32_t ukw[kw->size]; 470 + u8_string_to_unicode(ukw, kw->text, kw->size - 1); 471 + pklist->text = calloc(kw->size, sizeof(*ukw)); 472 + if (!pklist->text) 473 + { 474 + state->running = 0; 475 + return; 476 + } 477 + U32_MEMCCPY(pklist->text, ukw, kw->size, temp32); 478 + pklist->size = kw->size; 479 + kw++; 480 + pklist++; 481 + } 482 + pklist->text = NULL; 483 + pklist->size = -1; 484 + } 485 + if (!state->ucomments) 486 + { 487 + struct Comment* cm = state->comments; 488 + struct Comment32* pclist; 489 + size_t comment_count = 0; 490 + while (cm->start_text != NULL) 491 + { 492 + comment_count++; 493 + cm++; 494 + } 495 + assert(comment_count > 0); 496 + state->ucomments 497 + = calloc(comment_count + 1, sizeof(struct Comment32)); 498 + if (!state->ucomments) 499 + { 500 + state->running = 0; 501 + return; 502 + } 503 + pclist = state->ucomments; 504 + cm = state->comments; 505 + while (cm->start_text != NULL) 506 + { 507 + uint32_t ucm[MAX(cm->start_size, cm->end_size)]; 508 + u8_string_to_unicode(ucm, cm->start_text, 509 + cm->start_size - 1); 510 + pclist->start_text 511 + = calloc(cm->start_size, sizeof(*ucm)); 512 + if (!pclist->start_text) 513 + { 514 + state->running = 0; 515 + return; 516 + } 517 + U32_MEMCCPY(pclist->start_text, ucm, cm->start_size, 518 + temp32); 519 + pclist->start_size = cm->start_size; 520 + if (!cm->end_text) 521 + { 522 + pclist->end_text = NULL; 523 + pclist->end_size = -1; 524 + goto pclist_skip_copy; 525 + } 526 + u8_string_to_unicode(ucm, cm->end_text, 527 + cm->end_size - 1); 528 + pclist->end_text = calloc(cm->end_size, sizeof(*ucm)); 529 + if (!pclist->end_text) 530 + { 531 + state->running = 0; 532 + return; 533 + } 534 + U32_MEMCCPY(pclist->end_text, ucm, cm->end_size, temp32); 535 + pclist->end_size = cm->end_size; 536 + pclist_skip_copy: 537 + cm++; 538 + pclist++; 539 + } 540 + pclist->start_text = NULL; 541 + pclist->start_size = -1; 542 + } 543 + if (!state->uoperators) 544 + { 545 + struct Operator* op = state->operators; 546 + struct Operator32* polist; 547 + size_t operator_count = 0; 548 + while (op->text != NULL) 549 + { 550 + operator_count++; 551 + op++; 552 + } 553 + assert(operator_count > 0); 554 + state->uoperators 555 + = calloc(operator_count + 1, sizeof(struct Operator32)); 556 + if (!state->uoperators) 557 + { 558 + state->running = 0; 559 + return; 560 + } 561 + polist = state->uoperators; 562 + op = state->operators; 563 + while (op->text != NULL) 564 + { 565 + uint32_t uop[op->size]; 566 + u8_string_to_unicode(uop, op->text, op->size - 1); 567 + polist->text = calloc(op->size, sizeof(*uop)); 568 + if (!polist->text) 569 + { 570 + state->running = 0; 571 + return; 572 + } 573 + U32_MEMCCPY(polist->text, uop, op->size, temp32); 574 + polist->size = op->size; 575 + op++; 576 + polist++; 577 + } 578 + polist->text = NULL; 579 + polist->size = -1; 580 + } 581 + li = 0; 582 + quote = 0; 583 + comment_index = -1; 584 +recalc_syn_next_line: 585 + curline = state->buffer + li; 586 + free(curline->syntax); 587 + curline->syntax = NULL; 588 + curline->syntax_size = 0; 589 + if (li == state->rows_count) 590 + return; 591 + 592 + ti = 0; 593 + quote_start = 0; 594 + comment_start = 0; 595 +recalc_syn_next_token: 596 + if ((ti >= curline->length) || (curline->text[ti] == 0L)) 597 + { 598 + li++; 599 + goto recalc_syn_next_line; 600 + } 601 + 602 + if (quote) 603 + goto recalc_syn_skip_comments; 604 + 605 + /* Check for comments */ 606 + if (comment_index >= 0) 607 + { 608 + recalc_syn_comment_rest_of_line: 609 + seen_end_comment = 0; 610 + while (ti < curline->length) 611 + { 612 + seen_end_comment = u32_starts_with(&curline->text[ti], 613 + state->ucomments[comment_index].end_text); 614 + if (seen_end_comment) 615 + break; 616 + ti++; 617 + } 618 + assert(state->comments[comment_index].end_size > 1); 619 + ti += state->comments[comment_index].end_size - 1; 620 + if (seen_end_comment) 621 + comment_index = -1; 622 + if (syntax_add(curline, SE_COMMENT, comment_start, 623 + ti - comment_start) 624 + < 0) 625 + { 626 + state->running = 0; 627 + return; 628 + } 629 + ti++; 630 + comment_start = 0; 631 + if (comment_index >= 0) 632 + { 633 + li++; 634 + goto recalc_syn_next_line; 635 + } 636 + goto recalc_syn_next_token; 637 + } 638 + 639 + i = 0; 640 + comment = state->ucomments; 641 + while (comment->start_text != NULL) 642 + { 643 + if ((curline->length >= ti + comment->start_size - 1) 644 + && (u32_starts_with(&curline->text[ti], 645 + comment->start_text))) 646 + { 647 + assert(comment->start_size > 1); 648 + if (comment->end_text != NULL) 649 + goto recalc_syn_ml_comment; 650 + 651 + if (syntax_add(curline, SE_COMMENT, ti, 652 + curline->length - ti) 653 + < 0) 654 + { 655 + state->running = 0; 656 + return; 657 + } 658 + li++; 659 + goto recalc_syn_next_line; 660 + 661 + recalc_syn_ml_comment: 662 + comment_start = ti; 663 + comment_index = i; 664 + ti += comment->start_size; 665 + goto recalc_syn_comment_rest_of_line; 666 + } 667 + i++; 668 + comment = state->ucomments + i; 669 + } 670 + 671 + /* Check for string literals */ 672 +recalc_syn_skip_comments: 673 + if (quote) 674 + { 675 + while ((ti < curline->length) 676 + && (((ti == 1 && curline->text[ti - 1] == '\\') 677 + || (ti > 1 && curline->text[ti - 2] != '\\' 678 + && curline->text[ti - 1] == '\\')) 679 + || curline->text[ti] != quote)) 680 + ti++; 681 + if (curline->text[ti] == quote) 682 + { 683 + quote = 0; 684 + ti++; 685 + } 686 + assert(ti != quote_start); 687 + if (syntax_add(curline, SE_LITERAL, quote_start, 688 + ti - quote_start) 689 + < 0) 690 + { 691 + state->running = 0; 692 + return; 693 + } 694 + quote_start = 0; 695 + goto recalc_syn_next_token; 696 + } 697 + 698 + assert(quote == 0); 699 + if ((ti == 0 || (ti == 1 && curline->text[ti - 1] != '\\') 700 + || (ti > 1 701 + && (curline->text[ti - 2] == '\\' 702 + || curline->text[ti - 1] != '\\'))) 703 + && ((curline->text[ti] == '"') || (curline->text[ti] == '\''))) 704 + { 705 + quote = curline->text[ti]; 706 + quote_start = ti; 707 + ti++; 708 + goto recalc_syn_next_token; 709 + } 710 + 711 + /* Check for numeric literals */ 712 + if (isdigit(curline->text[ti]) 713 + && ((ti == 0) || ((ti > 0) && !isalnum(curline->text[ti - 1])))) 714 + { 715 + literal_start = ti; 716 + while ((ti < curline->length) && (isdigit(curline->text[ti]))) 717 + ti++; 718 + assert(ti != literal_start); 719 + if (syntax_add(curline, SE_LITERAL, literal_start, 720 + ti - literal_start) 721 + < 0) 722 + { 723 + state->running = 0; 724 + return; 725 + } 726 + goto recalc_syn_next_token; 727 + } 728 + 729 + /* Check for keywords */ 730 + i = 0; 731 + keyword = state->ukeywords; 732 + while (keyword->text != NULL) 733 + { 734 + if ((curline->length >= ti + keyword->size - 1) 735 + && ((ti == 0) 736 + || ((ti > 0) && !isalnum(curline->text[ti - 1]) 737 + && curline->text[ti - 1] != L'_')) 738 + && (!isalnum(curline->text[ti + keyword->size - 1])) 739 + && (curline->text[ti + keyword->size - 1] != L'_') 740 + && (u32_starts_with(&curline->text[ti], keyword->text))) 741 + { 742 + /* Add new syntax region */ 743 + assert(keyword->size > 1); 744 + if (syntax_add(curline, SE_KEYWORD, ti, 745 + keyword->size - 1) 746 + < 0) 747 + { 748 + state->running = 0; 749 + return; 750 + } 751 + ti += keyword->size - 1; 752 + goto recalc_syn_next_token; 753 + } 754 + i++; 755 + keyword = state->ukeywords + i; 756 + } 757 + 758 + /* Check for operators */ 759 + i = 0; 760 + /* clang-format off */ 761 + operator = state->uoperators; 762 + /* clang-format on */ 763 + while (operator->text != NULL) 764 + { 765 + if ((curline->length >= ti + operator->size - 1) 766 + && (u32_starts_with(&curline->text[ti], operator->text))) 767 + { 768 + /* Add new syntax region */ 769 + assert(operator->size> 1); 770 + if (syntax_add(curline, SE_OPERATOR, 771 + ti, operator->size - 1) 772 + < 0) 773 + { 774 + state->running = 0; 775 + return; 776 + } 777 + ti += operator->size - 1; 778 + goto recalc_syn_next_token; 779 + } 780 + i++; 781 + /* clang-format off */ 782 + operator = state->uoperators + i; 783 + /* clang-format on */ 784 + } 785 + 786 + ti++; 787 + goto recalc_syn_next_token; 788 +} 789 + 790 +void 791 redraw_status(struct DrawState* state) 792 { 793 assert(state != NULL); 794 @@ -790,7 +1354,8 @@ redraw_status(struct DrawState* state) 795 * */ 796 void 797 u32_draw_string(const int x, const int y, const uint16_t fg, const uint16_t bg, 798 - const uint32_t* s, const int mc, const int fc, const int p_l, 799 + const uint32_t* s, const struct SyntaxRegion* syntax, 800 + const ssize_t syntax_length, const int mc, const int fc, const int p_l, 801 const int p_r, const Alignment align, const int ds, const int fill, 802 const uint32_t* srch, const uint16_t sfg, const uint16_t sbg, 803 const int ssi, const int sei, const uint16_t slfg, const uint16_t slbg) 804 @@ -828,6 +1393,8 @@ u32_draw_string(const int x, const int y, const uint16 805 806 int w = 0; /* actual on-screen rune width */ 807 808 + int sel_on = 0; 809 + 810 const int ts = TAB_SIZE; 811 812 const uint32_t* ps = s; /* pointer to current character in 813 @@ -836,11 +1403,23 @@ u32_draw_string(const int x, const int y, const uint16 814 uint16_t cfg = fg; /* current fg */ 815 uint16_t cbg = bg; /* current bg */ 816 817 + struct Color defcolor, selection, highlight; 818 + 819 int hctl = 0; /* highlight "characters to live", when this 820 * reaches zero the highlighting is turned 821 * off */ 822 823 assert(s != NULL); 824 + 825 + defcolor.fg = fg; 826 + defcolor.bg = bg; 827 + 828 + selection.fg = slfg; 829 + selection.bg = slbg; 830 + 831 + highlight.fg = sfg; 832 + highlight.bg = sbg; 833 + 834 c = mc - p_l - p_r; 835 836 if (align == LEFT) 837 @@ -872,7 +1451,12 @@ u32_draw_string(const int x, const int y, const uint16 838 */ 839 while (*ps && sc < c) 840 { 841 - if ((hctl == 0) && ((ps - s) >= ssi) && ((ps - s) < sei)) 842 + cfg = fg; 843 + cbg = bg; 844 + 845 + sel_on = ((ps - s) >= ssi) && ((ps - s) < sei); 846 + 847 + if ((hctl == 0) && sel_on) 848 { 849 cfg = slfg; 850 cbg = slbg; 851 @@ -889,6 +1473,9 @@ u32_draw_string(const int x, const int y, const uint16 852 } 853 } 854 855 + synsetcolor(&cfg, &cbg, &defcolor, &selection, sel_on, 856 + &highlight, hctl > 0, syntax, syntax_length, ps - s); 857 + 858 if (*ps == '\t') 859 { 860 if (ssc < ds) 861 @@ -1004,11 +1591,11 @@ u32_draw_string(const int x, const int y, const uint16 862 } 863 ssc += w; 864 } 865 + sel_on = ((ps - s) >= ssi) && ((ps - s) < sei); 866 + 867 if (hctl == 0) 868 - { 869 - cfg = fg; 870 - cbg = bg; 871 - } 872 + synsetcolor(&cfg, &cbg, &defcolor, &selection, sel_on, 873 + &highlight, 0, syntax, syntax_length, ps - s); 874 ps++; 875 } 876 877 diff --git a/draw.h b/draw.h 878 index 68cc281d8..e0b72ba7c 100644 879 --- a/draw.h 880 +++ b/draw.h 881 @@ -63,17 +63,69 @@ struct Color { 882 uint16_t bg; 883 }; 884 885 +typedef enum { FT_GENERIC, FT_C, FT_SHELL } FileType; 886 + 887 +typedef enum { 888 + SE_DEFAULT, 889 + SE_COMMENT, 890 + SE_KEYWORD, 891 + SE_OPERATOR, 892 + SE_LITERAL 893 +} SyntaxElement; 894 + 895 +struct Keyword { 896 + char* text; 897 + int size; 898 +}; 899 + 900 +struct Keyword32 { 901 + uint32_t* text; 902 + int size; 903 +}; 904 + 905 +struct Comment { 906 + char* start_text; 907 + char* end_text; 908 + int start_size; 909 + int end_size; 910 +}; 911 + 912 +struct Comment32 { 913 + uint32_t* start_text; 914 + uint32_t* end_text; 915 + int start_size; 916 + int end_size; 917 +}; 918 + 919 +struct Operator { 920 + char* text; 921 + int size; 922 +}; 923 + 924 +struct Operator32 { 925 + uint32_t* text; 926 + int size; 927 +}; 928 + 929 struct Coord { 930 int col; 931 int row; 932 }; 933 934 +struct SyntaxRegion { 935 + ssize_t start; 936 + ssize_t length; 937 + struct Color color; 938 +}; 939 + 940 struct BufferLine { 941 uint32_t* text; 942 ssize_t length; /* both in uint32_t units */ 943 ssize_t size; 944 uint16_t fg; 945 uint16_t bg; 946 + struct SyntaxRegion* syntax; 947 + ssize_t syntax_size; 948 }; 949 950 struct DrawState { 951 @@ -113,12 +165,20 @@ struct DrawState { 952 int dirty; 953 int new_file; 954 int recalculate_size; 955 + int syntax_enabled; 956 + int recalculate_syntax; 957 int mode; 958 int maxx; 959 int maxy; 960 struct Coord sel_start; /* buffer coordinate */ 961 struct Coord sel_end; /* buffer coordinate */ 962 int backup_possible; /* is saving backup possible? */ 963 + struct Keyword* keywords; 964 + struct Comment* comments; 965 + struct Operator* operators; 966 + struct Keyword32* ukeywords; /* Converted to 32-bit Unicode */ 967 + struct Comment32* ucomments; 968 + struct Operator32* uoperators; 969 }; 970 971 struct Key { 972 @@ -134,6 +194,13 @@ struct StatusSegment { 973 Alignment alignment; 974 }; 975 976 +extern const struct Keyword c_keywords[]; 977 +extern const struct Comment c_comments[]; 978 +extern const struct Operator c_operators[]; 979 +extern const struct Keyword shell_keywords[]; 980 +extern const struct Comment shell_comments[]; 981 +extern const struct Operator shell_operators[]; 982 + 983 void assign_coord(struct Coord* dest, const struct Coord src); 984 ssize_t buffer_length(uint32_t* buffer, const ssize_t max_index); 985 int calculate_help_width(const struct DrawState* state); 986 @@ -150,7 +217,8 @@ void draw_rect(const int startx, const int endx, const 987 int draw_screen(const struct DrawState* state); 988 int draw_status(const struct DrawState* state); 989 int draw_string(const int x, const int y, const uint16_t fg, const uint16_t bg, 990 - const char* s, const int max_cols, const int fill_cols, 991 + const char* s, const struct SyntaxRegion* syntax, 992 + const ssize_t syntax_length, const int max_cols, const int fill_cols, 993 const int padding_left, const int padding_right, const Alignment align, 994 const int ds, const int fill, const uint32_t* srch, const uint16_t sfg, 995 const uint16_t sbg, const int ssi, const int sei, const uint16_t slfg, 996 @@ -171,10 +239,12 @@ void init_drawstate(struct DrawState* state, char* fil 997 const int skip_paste_and_mode_init, const int skip_filenames_init); 998 void recalculate_line_numbers(struct DrawState* state); 999 void recalculate_size(struct DrawState* state); 1000 +void recalculate_syntax(struct DrawState* state); 1001 void redraw_status(struct DrawState* state); 1002 void u32_draw_string(const int x, const int y, const uint16_t fg, 1003 - const uint16_t bg, const uint32_t* s, const int max_cols, 1004 - const int fill_cols, const int padding_left, const int padding_right, 1005 - const Alignment align, const int ds, const int fill, 1006 - const uint32_t* srch, const uint16_t sfg, const uint16_t sbg, 1007 - const int ssi, const int sei, const uint16_t slfg, const uint16_t slbg); 1008 + const uint16_t bg, const uint32_t* s, const struct SyntaxRegion* syntax, 1009 + const ssize_t syntax_length, const int max_cols, const int fill_cols, 1010 + const int padding_left, const int padding_right, const Alignment align, 1011 + const int ds, const int fill, const uint32_t* srch, const uint16_t sfg, 1012 + const uint16_t sbg, const int ssi, const int sei, const uint16_t slfg, 1013 + const uint16_t slbg); 1014 diff --git a/sled.c b/sled.c 1015 index 68cc281d8..e0b72ba7c 100644 1016 --- a/sled.c 1017 +++ b/sled.c 1018 @@ -50,6 +50,18 @@ extern const char* prompt_reread_dirty; 1019 extern const char* replace_dialog_title; 1020 extern const char* search_dialog_title; 1021 1022 +struct SyntaxEnding { 1023 + char* text; 1024 + FileType type; 1025 +}; 1026 +/* clang-format off */ 1027 +static const struct SyntaxEnding syntax_endings[] = { 1028 + { .text = ".c", .type = FT_C }, 1029 + { .text = ".h", .type = FT_C }, 1030 + { .text = ".sh", .type = FT_SHELL } 1031 +}; 1032 +/* clang-format on */ 1033 + 1034 static int 1035 cancel_close(struct DrawState* state) 1036 { 1037 @@ -178,8 +190,9 @@ create_file(struct DrawState* state) 1038 state->buffer_size = ALLOC_DELTA; 1039 state->rows_count = 1; 1040 recalculate_line_numbers(state); 1041 - state->coord.row = 0; 1042 - state->dirty = 1; 1043 + state->coord.row = 0; 1044 + state->dirty = 1; 1045 + state->recalculate_syntax = 1; 1046 1047 return 0; 1048 } 1049 @@ -310,6 +323,7 @@ do_search_replace(struct DrawState* state) 1050 state->buffer[row].length = buflen; 1051 } 1052 free(buf); 1053 + state->recalculate_syntax = 1; 1054 return 0; 1055 1056 do_search_replace_alloc_error: 1057 @@ -360,7 +374,8 @@ erase_backwards(struct DrawState* state) 1058 buffer_line->text[i - 1] = 0; 1059 buffer_line->length--; 1060 position_to(state, state->coord.row, state->coord.col - 1); 1061 - state->dirty = 1; 1062 + state->dirty = 1; 1063 + state->recalculate_syntax = 1; 1064 } 1065 else if (state->coord.row > 0) 1066 { 1067 @@ -397,7 +412,8 @@ erase_forward(struct DrawState* state) 1068 } 1069 buffer_line->text[i] = 0; 1070 buffer_line->length--; 1071 - state->dirty = 1; 1072 + state->dirty = 1; 1073 + state->recalculate_syntax = 1; 1074 } 1075 else if ((state->coord.row + 1) < state->rows_count) 1076 simple_join_lines(state); 1077 @@ -431,7 +447,8 @@ erase_line(struct DrawState* state) 1078 state->coord.row--; 1079 position_to(state, state->coord.row, 0); 1080 state->rows_count--; 1081 - state->dirty = 1; 1082 + state->dirty = 1; 1083 + state->recalculate_syntax = 1; 1084 recalculate_line_numbers(state); 1085 return 0; 1086 1087 @@ -474,6 +491,7 @@ erase_prev_word(struct DrawState* state) 1088 if (state->display_column < state->first_shown.col) 1089 state->first_shown.col = state->display_column; 1090 state->dirty = 1; 1091 + state->recalculate_syntax = 1; 1092 return 0; 1093 } 1094 1095 @@ -529,7 +547,8 @@ erase_selection(struct DrawState* state) 1096 recalculate_line_numbers(state); 1097 } 1098 free(buf); 1099 - state->dirty = 1; 1100 + state->dirty = 1; 1101 + state->recalculate_syntax = 1; 1102 } 1103 return 0; 1104 } 1105 @@ -554,7 +573,8 @@ erase_to_end(struct DrawState* state) 1106 buffer_line->text[state->coord.col] = 0; 1107 buffer_line->length = state->coord.col; 1108 move_end(state); 1109 - state->dirty = 1; 1110 + state->dirty = 1; 1111 + state->recalculate_syntax = 1; 1112 return 0; 1113 } 1114 1115 @@ -588,7 +608,8 @@ erase_to_start(struct DrawState* state) 1116 buffer_line->text[len - state->coord.col] = 0; 1117 buffer_line->length = len - state->coord.col; 1118 move_start(state); 1119 - state->dirty = 1; 1120 + state->dirty = 1; 1121 + state->recalculate_syntax = 1; 1122 return 0; 1123 } 1124 1125 @@ -1148,8 +1169,8 @@ insert_char(struct DrawState* state, uint32_t ch) 1126 } 1127 1128 insert_skip_wrap: 1129 - state->dirty = 1; 1130 - 1131 + state->dirty = 1; 1132 + state->recalculate_syntax = 1; 1133 return 0; 1134 1135 insert_char_error: 1136 @@ -1200,7 +1221,8 @@ insert_skip_wrap: 1137 simple_insert_line(state); 1138 state->saved_column = -1; 1139 position_to(state, state->coord.row + 1, 0); 1140 - state->dirty = 1; 1141 + state->dirty = 1; 1142 + state->recalculate_syntax = 1; 1143 return 0; 1144 } 1145 1146 @@ -1735,7 +1757,8 @@ paste_from_paste(struct DrawState* state) 1147 } 1148 1149 position_to(state, newpos.row, newpos.col); 1150 - state->dirty = 1; 1151 + state->dirty = 1; 1152 + state->recalculate_syntax = 1; 1153 1154 paste_from_paste_cleanup: 1155 free(rest); 1156 @@ -2001,6 +2024,7 @@ reread_file(struct DrawState* state) 1157 } 1158 1159 recalculate_line_numbers(state); 1160 + state->recalculate_syntax = 1; 1161 return 0; 1162 } 1163 1164 @@ -2398,6 +2422,7 @@ simple_join_lines(struct DrawState* state) 1165 state->rows_count--; 1166 state->dirty = 1; 1167 recalculate_line_numbers(state); 1168 + state->recalculate_syntax = 1; 1169 return; 1170 1171 simple_join_lines_error: 1172 @@ -2875,6 +2900,8 @@ main(int argc, char** argv) 1173 int saved_errno; 1174 int tbires; 1175 int optind = 1; 1176 + int found_ending = 0; 1177 + int i; 1178 1179 *prompt = 0; 1180 1181 @@ -2981,6 +3008,40 @@ main_options_loop: 1182 1183 init_drawstate(&state, filename, prompt, 0, 0); 1184 1185 + for (i = 0; (size_t)i < LEN(syntax_endings); i++) 1186 + { 1187 + int filename_len = strlen(state.filename); 1188 + int ending_len = strlen(syntax_endings[i].text); 1189 + 1190 + found_ending = 0; 1191 + if ((filename_len >= ending_len) 1192 + && !strcmp(syntax_endings[i].text, 1193 + state.filename + filename_len - ending_len)) 1194 + { 1195 + found_ending = 1; 1196 + break; 1197 + } 1198 + } 1199 + if (found_ending) 1200 + { 1201 + state.syntax_enabled = 1; 1202 + switch (syntax_endings[i].type) 1203 + { 1204 + case FT_C: 1205 + state.keywords = (struct Keyword*)c_keywords; 1206 + state.comments = (struct Comment*)c_comments; 1207 + state.operators = (struct Operator*)c_operators; 1208 + break; 1209 + case FT_SHELL: 1210 + state.keywords = (struct Keyword*)shell_keywords; 1211 + state.comments = (struct Comment*)shell_comments; 1212 + state.operators = (struct Operator*)shell_operators; 1213 + break; 1214 + default: 1215 + /* Shouldn't happen */ 1216 + assert(0); 1217 + } 1218 + } 1219 #ifdef __OpenBSD__ 1220 if (unveil(state.filename, "rwc") < 0) 1221 { 1222 @@ -3200,9 +3261,10 @@ init_termbox: 1223 goto main_done; 1224 } 1225 1226 - errno = 0; 1227 - state.maxx = 0; 1228 - state.recalculate_size = 1; 1229 + errno = 0; 1230 + state.maxx = 0; 1231 + state.recalculate_size = 1; 1232 + state.recalculate_syntax = 1; 1233 1234 while (state.running) 1235 { 1236 @@ -3214,6 +3276,11 @@ init_termbox: 1237 recalculate_size(&state); 1238 state.recalculate_size = 0; 1239 } 1240 + if (state.syntax_enabled && state.recalculate_syntax) 1241 + { 1242 + recalculate_syntax(&state); 1243 + state.recalculate_syntax = 0; 1244 + } 1245 if (draw(&state)) 1246 { 1247 state.running = 0; 1248 -- 1249 2.44.1 1250