sled

Једноставни уређивач текста
git clone https://git.sr.ht/~strahinja/sled
Дневник | Датотеке | Референце | ПРОЧИТАЈМЕ | ЛИЦЕНЦА

чување a55be18ef394913a9be698c37137db0419288e2a
родитељ 39ca2835b2e586289806e92744832513d93243f8
Аутор: Страхиња Радић <sr@strahinja.org>
Датум:   Wed,  7 Aug 2024 21:13:48 +0200

patch/syntax.patch: New patch (syntax highlighting)

Diffstat:
Apatch/syntax.patch | 1104+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
измењених датотека: 1, додавања: 1104(+), брисања: 0(-)

diff --git a/patch/syntax.patch b/patch/syntax.patch @@ -0,0 +1,1104 @@ +From fb80cff14430251ddd845727af64307397fed61a Mon Sep 17 00:00:00 2001 +From: Страхиња Радић <sr@strahinja.org> +Date: Wed Aug 7 19:09:36 2024 UTC +Subject: [PATCH] Add syntax highlighting +MIME-Version: 1.0 +Content-Type: text/plain; charset=UTF-8 +Content-Transfer-Encoding: 8bit + +Add syntax highlighting. This is a rudimentary, flawed version, +but gets the job done. TODO: Optimization. +--- + config.def.h | 7+ 0- + draw.c | 485+ 29- + draw.h | 62+ 6- + sled.c | 91+ 7- + 4 files changed, 645 insertions(+), 42 deletions(-) + +diff --git a/config.def.h b/config.def.h +index 39ca2835b..fb80cff14 100644 +--- a/config.def.h ++++ b/config.def.h +@@ -239,6 +239,13 @@ static const struct Key command_keys[] = { + #endif /* SLED_C_GLOBALS */ + + /* clang-format off */ ++static const struct Color syntax_colors[] = { ++ [SE_DEFAULT] = { .fg = TEXT_FG, .bg = TEXT_BG }, ++ [SE_COMMENT] = { .fg = TB_GREEN, .bg = TEXT_BG }, ++ [SE_KEYWORD] = { .fg = TB_CYAN | TB_BOLD, .bg = TEXT_BG }, ++ [SE_OPERATOR] = { .fg = TB_YELLOW | TB_BOLD, .bg = TEXT_BG }, ++ [SE_LITERAL] = { .fg = TB_MAGENTA | TB_BOLD, .bg = TEXT_BG }, ++}; + static const struct StatusSegment status_segments[] = { + {.format = "%s%s", + .callback = format_filename, +diff --git a/draw.c b/draw.c +index 39ca2835b..fb80cff14 100644 +--- a/draw.c ++++ b/draw.c +@@ -2,6 +2,7 @@ + * any later version. Copyright (C) 2022-2024 Страхиња Радић. + * See the file LICENSE for exact copyright and license details. */ + #include <assert.h> ++#include <ctype.h> + #include <errno.h> + #include <stdio.h> + #include <stdlib.h> +@@ -13,6 +14,28 @@ + #include "util.h" + #include "version.h" + ++/* Keyword initializer specification macro */ ++#define KW(literal) \ ++ { \ ++ .text = (uint32_t*)literal, \ ++ .size = sizeof(literal) / sizeof(uint32_t) \ ++ } ++/* Single-line comment specification macro */ ++#define SLC(com) \ ++ { \ ++ .start_text = (uint32_t*)com, \ ++ .start_size = sizeof(com) / sizeof(uint32_t), \ ++ .end_text = NULL, .end_size = -1 \ ++ } ++/* Area comment specification macro */ ++#define AC(com1, com2) \ ++ { \ ++ .start_text = (uint32_t*)com1, \ ++ .start_size = sizeof(com1) / sizeof(uint32_t), \ ++ .end_text = (uint32_t*)com2, \ ++ .end_size = sizeof(com2) / sizeof(uint32_t) \ ++ } ++ + enum { + UNKNOWN_MODE, + COMMAND_MODE, +@@ -87,6 +110,95 @@ static const struct { + { .boundary = 99, .width = 3 }, + { .boundary = 9, .width = 2 } + }; ++extern const struct Color syntax_colors[]; ++const struct Keyword c_keywords[] = { ++ KW(L"auto"), KW(L"break"), KW(L"case"), KW(L"char"), KW(L"const"), ++ KW(L"continue"), KW(L"default"), KW(L"do"), KW(L"double"), KW(L"else"), ++ KW(L"enum"), KW(L"extern"), KW(L"float"), KW(L"for"), KW(L"goto"), ++ KW(L"if"), KW(L"inline"), KW(L"int"), KW(L"long"), KW(L"register"), ++ KW(L"restrict"), KW(L"return"), KW(L"short"), KW(L"signed"), ++ KW(L"sizeof"), KW(L"static"), KW(L"struct"), KW(L"switch"), ++ KW(L"typedef"), KW(L"union"), KW(L"unsigned"), KW(L"void"), ++ KW(L"volatile"), KW(L"while"), KW(L"_Bool"), KW(L"_Complex"), ++ KW(L"_Imaginary"), ++ { .text = NULL } ++}; ++const struct Comment c_comments[] = { ++ SLC(L"//"), ++ AC(L"/*", L"*/"), ++ { .start_text = NULL } ++}; ++const struct Operator c_operators[] = { ++ KW(L"<<="), KW(L">>="), KW(L"<<"), KW(L">>"), ++ KW(L"+="), KW(L"-="), KW(L"*="), KW(L"/="), KW(L"%="), ++ KW(L"&="), KW(L"|="), KW(L"^="), ++ KW(L"=="), KW(L"!="), KW(L"<="), KW(L">="), ++ KW(L"->"), ++ KW(L"++"), KW(L"--"), ++ KW(L"&&"), KW(L"||"), ++ KW(L"="), KW(L"+"), KW(L"-"), KW(L"~"), KW(L"*"), KW(L"/"), KW(L"%"), ++ KW(L"&"), KW(L"|"), KW(L"^"), KW(L"!"), ++ KW(L"<"), KW(L">"), ++ KW(L","), KW(L";"), KW(L":"), KW(L"?"), KW(L"."), ++ KW(L"("), KW(L")"), KW(L"["), KW(L"]"), KW(L"{"), KW(L"}"), ++ { .text = NULL } ++}; ++const struct Keyword shell_keywords[] = { ++ KW(L"!"), KW(L"{"), KW(L"}"), ++ KW(L"case"), KW(L"do"), KW(L"done"), KW(L"elif"), KW(L"else"), ++ KW(L"esac"), KW(L"fi"), KW(L"for"), KW(L"if"), KW(L"in"), KW(L"then"), ++ KW(L"until"), KW(L"while"), KW(L"[["), KW(L"]]"), KW(L"function"), ++ KW(L"select"), KW(L"admin"), KW(L"alias"), KW(L"ar"), KW(L"asa"), ++ KW(L"at"), KW(L"awk"), KW(L"basename"), KW(L"batch"), KW(L"bc"), ++ KW(L"bg"), KW(L"break"), KW(L"c99"), KW(L"cal"), KW(L"cat"), KW(L"cd"), ++ KW(L"cflow"), KW(L"chgrp"), KW(L"chmod"), KW(L"chown"), KW(L"cksum"), ++ KW(L"cmp"), KW(L"colon"), KW(L"comm"), KW(L"command"), KW(L"compress"), ++ KW(L"continue"), KW(L"cp"), KW(L"crontab"), KW(L"csplit"), KW(L"ctags"), ++ KW(L"cut"), KW(L"cxref"), KW(L"date"), KW(L"dd"), KW(L"delta"), ++ KW(L"df"), KW(L"diff"), KW(L"dirname"), KW(L"dot"), KW(L"du"), ++ KW(L"echo"), KW(L"ed"), KW(L"env"), KW(L"eval"), KW(L"ex"), KW(L"exec"), ++ KW(L"exit"), KW(L"expand"), KW(L"export"), KW(L"expr"), KW(L"false"), ++ KW(L"fc"), KW(L"fg"), KW(L"file"), KW(L"find"), KW(L"fold"), ++ KW(L"fort77"), KW(L"fuser"), KW(L"gencat"), KW(L"get"), KW(L"getconf"), ++ KW(L"getopts"), KW(L"grep"), KW(L"hash"), KW(L"head"), KW(L"iconv"), ++ KW(L"id"), KW(L"ipcrm"), KW(L"ipcs"), KW(L"jobs"), KW(L"join"), ++ KW(L"kill"), KW(L"lex"), KW(L"link"), KW(L"ln"), KW(L"locale"), ++ KW(L"localedef"), KW(L"logger"), KW(L"logname"), KW(L"lp"), KW(L"ls"), ++ KW(L"m4"), KW(L"mailx"), KW(L"make"), KW(L"man"), KW(L"mesg"), ++ KW(L"mkdir"), KW(L"mkfifo"), KW(L"more"), KW(L"mv"), KW(L"newgrp"), ++ KW(L"nice"), KW(L"nl"), KW(L"nm"), KW(L"nohup"), KW(L"od"), ++ KW(L"paste"), KW(L"patch"), KW(L"pathchk"), KW(L"pax"), KW(L"pr"), ++ KW(L"printf"), KW(L"prs"), KW(L"ps"), KW(L"pwd"), KW(L"qalter"), ++ KW(L"qdel"), KW(L"qhold"), KW(L"qmove"), KW(L"qmsg"), KW(L"qrerun"), ++ KW(L"qrls"), KW(L"qselect"), KW(L"qsig"), KW(L"qstat"), KW(L"qsub"), ++ KW(L"read"), KW(L"readonly"), KW(L"renice"), KW(L"return"), KW(L"rm"), ++ KW(L"rmdel"), KW(L"rmdir"), KW(L"sact"), KW(L"sccs"), KW(L"sed"), ++ KW(L"set"), KW(L"sh"), KW(L"shift"), KW(L"sleep"), KW(L"sort"), ++ KW(L"split"), KW(L"strings"), KW(L"strip"), KW(L"stty"), KW(L"tabs"), ++ KW(L"tail"), KW(L"talk"), KW(L"tee"), KW(L"test"), KW(L"time"), ++ KW(L"times"), KW(L"touch"), KW(L"tput"), KW(L"tr"), KW(L"trap"), ++ KW(L"true"), KW(L"tsort"), KW(L"tty"), KW(L"type"), KW(L"ulimit"), ++ KW(L"umask"), KW(L"unalias"), KW(L"uname"), KW(L"uncompress"), ++ KW(L"unexpand"), KW(L"unget"), KW(L"uniq"), KW(L"unlink"), KW(L"unset"), ++ KW(L"uucp"), KW(L"uudecode"), KW(L"uuencode"), KW(L"uustat"), ++ KW(L"uux"), KW(L"val"), KW(L"vi"), KW(L"wait"), KW(L"wc"), KW(L"what"), ++ KW(L"who"), KW(L"write"), KW(L"xargs"), KW(L"yacc"), KW(L"zcat"), ++ KW(L"break"), KW(L":"), KW(L"."), KW(L"continue"), KW(L"eval"), ++ KW(L"exec"), KW(L"exit"), KW(L"export"), KW(L"readonly"), KW(L"return"), ++ KW(L"set"), KW(L"shift"), KW(L"times"), KW(L"trap"), KW(L"unset"), ++ {.text = NULL} ++}; ++const struct Comment shell_comments[] = { ++ SLC(L"#"), ++ {.start_text = NULL} ++}; ++const struct Operator shell_operators[] = { ++ KW(L"<<"), KW(L">>"), ++ KW(L"&&"), KW(L"||"), KW(L"$"), KW(L"`"), KW(L"="), ++ KW(L"<"), KW(L">"), ++ KW(L";"), KW(L"("), KW(L")"), KW(L"["), KW(L"]"), KW(L"{"), KW(L"}"), ++ {.text = NULL} ++}; + const char* go_to_dialog_title = "Go to line"; + const char* prompt_dirty = "File changed, save [y/n/ESC]?"; + const char* prompt_overwrite = "Not empty, overwrite? [y/n/ESC]?"; +@@ -95,6 +207,46 @@ const char* replace_dialog_title = "Replace with"; + const char* search_dialog_title = "Search text"; + /* clang-format on */ + ++/* Set current fg/bg taking into account syntax highlighting. */ ++static void ++synsetcolor(uint16_t* fg, uint16_t* bg, const struct Color* defcolor, ++ const struct Color* selection, const int sel_on, ++ const struct Color* highlight, const int hi_on, ++ const struct SyntaxRegion* syntax, const ssize_t syntax_length, ++ const int x) ++{ ++ ssize_t i; ++ assert((fg != NULL) && (bg != NULL) && (defcolor != NULL) ++ && (selection != NULL) && (highlight != NULL)); ++ if (hi_on) ++ { ++ *fg = highlight->fg; ++ *bg = highlight->bg; ++ return; ++ } ++ ++ if (sel_on) ++ { ++ *fg = selection->fg; ++ *bg = selection->bg; ++ return; ++ } ++ ++ for (i = 0; i < syntax_length; i++) ++ { ++ if ((syntax[i].start <= x) ++ && (x < syntax[i].start + syntax[i].length)) ++ { ++ *fg = syntax[i].color.fg; ++ *bg = syntax[i].color.bg; ++ return; ++ } ++ } ++ ++ *fg = defcolor->fg; ++ *bg = defcolor->bg; ++} ++ + void get_selection(int* ssr, int* ssc, int* ser, int* sec, + const struct DrawState* state); + +@@ -264,15 +416,15 @@ draw_help(const struct DrawState* state) + endy = h > maxy - 1 ? maxy - 2 : starty + h; + + draw_box(startx, endx, starty, endy, DLG_FG, DLG_BG); +- if (draw_string(startx, starty, TITLE_FG, TITLE_BG, help_title, ++ if (draw_string(startx, starty, TITLE_FG, TITLE_BG, help_title, NULL, 0, + strlen(help_title) + 2, w, 1, 1, CENTER, 0, 0, NULL, 0, 0, + 0, 0, 0, 0)) + return 1; + while ((size_t)row < LEN(help)) + { + if (draw_string(startx + 1, starty + 1 + row, DLG_FG, DLG_BG, +- help[row], w - 2, w - 2, 1, 1, LEFT, 0, 1, NULL, 0, +- 0, 0, 0, 0, 0)) ++ help[row], NULL, 0, w - 2, w - 2, 1, 1, LEFT, 0, 1, ++ NULL, 0, 0, 0, 0, 0, 0)) + return 1; + row++; + } +@@ -307,15 +459,16 @@ draw_input_dialog(const struct DrawState* state) + INPUT_EDIT_BG); + if (state->input_title) + if (draw_string(startx, starty, TITLE_FG, TITLE_BG, +- state->input_title, strlen(state->input_title) + 2, +- w, 1, 1, CENTER, 0, 0, NULL, 0, 0, 0, 0, 0, 0)) ++ state->input_title, NULL, 0, ++ strlen(state->input_title) + 2, w, 1, 1, CENTER, 0, ++ 0, NULL, 0, 0, 0, 0, 0, 0)) + return 1; + dlen = display_length(state->input, len); + if (dlen > state->input_first_shown_column) + u32_draw_string(startx + 1, starty + 1, INPUT_EDIT_FG, +- INPUT_EDIT_BG, state->input, w - 2, w - 2, 1, 1, LEFT, +- state->input_first_shown_column, 1, NULL, 0, 0, 0, 0, 0, +- 0); ++ INPUT_EDIT_BG, state->input, NULL, 0, w - 2, w - 2, 1, ++ 1, LEFT, state->input_first_shown_column, 1, NULL, 0, 0, ++ 0, 0, 0, 0); + tb_set_cursor(startx + 2 + state->input_display_column + - state->input_first_shown_column, + starty + 1); +@@ -336,7 +489,7 @@ draw_line_number(const struct DrawState* state, const + pnumbuf = numbuf_end - numbuf > state->line_numbers_width + ? numbuf_end - state->line_numbers_width + : numbuf; +- draw_string(0, cy, LINENO_FG, LINENO_BG, pnumbuf, ++ draw_string(0, cy, LINENO_FG, LINENO_BG, pnumbuf, NULL, 0, + numbuf_end - numbuf + 1, state->line_numbers_width, 0, + 0, RIGHT, 0, 1, NULL, 0, 0, 0, 0, 0, 0); + } +@@ -416,9 +569,10 @@ draw_screen(const struct DrawState* state) + u32_draw_string(state->line_numbers + ? state->line_numbers_width + 2 + : 0, +- cy, TEXT_FG, TEXT_BG, current->text, maxx, maxx, +- 0, 0, LEFT, fsc, 0, state->search, SEARCH_FG, +- SEARCH_BG, ssi, sei, SEL_FG, SEL_BG); ++ cy, TEXT_FG, TEXT_BG, current->text, ++ current->syntax, current->syntax_length, maxx, ++ maxx, 0, 0, LEFT, fsc, 0, state->search, ++ SEARCH_FG, SEARCH_BG, ssi, sei, SEL_FG, SEL_BG); + i++; + cy++; + } +@@ -448,22 +602,22 @@ draw_status(const struct DrawState* state) + if (IN(state->mode, M_PROMPT) && *state->prompt) + { + if (draw_string(0, state->maxy - 1, PROMPT_FG, PROMPT_BG, +- state->prompt, strlen(state->prompt) + 2, maxx, 1, +- 1, CENTER, 0, 1, NULL, 0, 0, 0, 0, 0, 0)) ++ state->prompt, NULL, 0, strlen(state->prompt) + 2, ++ maxx, 1, 1, CENTER, 0, 1, NULL, 0, 0, 0, 0, 0, 0)) + return 1; + } + else if (state->action && *state->action) + { + if (draw_string(0, state->maxy - 1, ACTION_FG, ACTION_BG, +- state->action, strlen(state->action) + 2, maxx, 1, +- 1, CENTER, 0, 1, NULL, 0, 0, 0, 0, 0, 0)) ++ state->action, NULL, 0, strlen(state->action) + 2, ++ maxx, 1, 1, CENTER, 0, 1, NULL, 0, 0, 0, 0, 0, 0)) + return 1; + } + else if (state->error && *state->error) + { + if (draw_string(0, state->maxy - 1, ERROR_FG, ERROR_BG, +- state->error, strlen(state->error) + 2, maxx, 1, 1, +- CENTER, 0, 1, NULL, 0, 0, 0, 0, 0, 0)) ++ state->error, NULL, 0, strlen(state->error) + 2, ++ maxx, 1, 1, CENTER, 0, 1, NULL, 0, 0, 0, 0, 0, 0)) + return 1; + } + else +@@ -486,7 +640,7 @@ draw_status(const struct DrawState* state) + if (pseg == status_segments + LEN(status_segments) - 1) + seg_size = maxx - 1 - current_start; + if (draw_string(current_start, state->maxy - 1, +- pseg->fg, pseg->bg, buf, ++ pseg->fg, pseg->bg, buf, NULL, 0, + MIN(seg_size, (ssize_t)(strlen(buf) + 2)), + last ? seg_size + 1 : seg_size, 1, 1, + pseg->alignment, 0, 1, NULL, 0, 0, 0, 0, 0, +@@ -521,8 +675,9 @@ draw_status(const struct DrawState* state) + * */ + int + draw_string(const int x, const int y, const uint16_t fg, const uint16_t bg, +- const char* s, const int mc, const int fc, const int p_l, const int p_r, +- const Alignment align, const int ds, const int fill, ++ const char* s, const struct SyntaxRegion* syntax, ++ const ssize_t syntax_length, const int mc, const int fc, const int p_l, ++ const int p_r, const Alignment align, const int ds, const int fill, + const uint32_t* srch, const uint16_t sfg, const uint16_t sbg, + const int ssi, const int sei, const uint16_t slfg, const uint16_t slbg) + { +@@ -532,8 +687,8 @@ draw_string(const int x, const int y, const uint16_t f + if (!us) + return 1; + u8_string_to_unicode(us, s, mc); +- u32_draw_string(x, y, fg, bg, us, mc, fc, p_l, p_r, align, ds, fill, +- srch, sfg, sbg, ssi, sei, slfg, slbg); ++ u32_draw_string(x, y, fg, bg, us, syntax, syntax_length, mc, fc, p_l, ++ p_r, align, ds, fill, srch, sfg, sbg, ssi, sei, slfg, slbg); + free(us); + return 0; + } +@@ -638,6 +793,8 @@ free_bufferline(struct BufferLine* bl) + bl->length = 0; + bl->fg = 0; + bl->bg = 0; ++ free(bl->syntax); ++ bl->syntax = NULL; + } + + void +@@ -676,6 +833,7 @@ init_bufferline(struct BufferLine* bl) + bl->size = 0; + bl->fg = 0; + bl->bg = 0; ++ bl->syntax = NULL; + } + + void +@@ -701,6 +859,11 @@ init_drawstate(struct DrawState* state, char* error, c + state->orig_real_filename = NULL; + state->backup_filename = NULL; + state->rfn_len = 0; ++ state->recalculate_syntax = 1; ++ state->syntax_enabled = 0; ++ state->keywords = NULL; ++ state->comments = NULL; ++ state->operators = NULL; + } + state->input = NULL; + state->search = NULL; +@@ -759,7 +922,272 @@ recalculate_size(struct DrawState* state) + recalculate_line_numbers(state); + } + ++int ++syntax_add(struct BufferLine* bl, SyntaxElement type, const ssize_t start, ++ const ssize_t length) ++{ ++ struct SyntaxRegion* temps = NULL; ++ struct SyntaxRegion* newreg; ++ ++ assert(bl != NULL); ++ if (bl->syntax_length + 1 >= bl->syntax_size) ++ { ++ temps = reallocarray(bl->syntax, bl->syntax_size + 1, ++ sizeof(struct SyntaxRegion)); ++ if (!temps) ++ return -1; ++ bl->syntax = temps; ++ bl->syntax_size++; ++ } ++ newreg = &bl->syntax[bl->syntax_length]; ++ newreg->start = start; ++ newreg->length = length; ++ newreg->color.fg = syntax_colors[type].fg; ++ newreg->color.bg = syntax_colors[type].bg; ++ bl->syntax_length++; ++ return 0; ++} ++ + void ++recalculate_syntax(struct DrawState* state) ++{ ++ struct BufferLine* curline; ++ struct Keyword* keyword; ++ struct Comment* comment; ++ struct Operator* operator; ++ struct Keyword* klist; ++ struct Comment* clist; ++ struct Operator* olist; ++ char* temp; ++ ssize_t i, li, ti, comment_start, quote_start, literal_start; ++ uint32_t quote; ++ int comment_index, seen_end_comment; ++ ++ assert((state != NULL) && (state->buffer != NULL) ++ && (state->keywords != NULL) && (state->comments != NULL) ++ && (state->operators != NULL) ++ && (state->buffer[state->first_shown.row].text != NULL)); ++ ++ klist = state->keywords; ++ clist = state->comments; ++ olist = state->operators; ++ li = state->first_shown.row; ++ quote = 0; ++ comment_index = -1; ++recalc_syn_next_line: ++ curline = state->buffer + li; ++ free(curline->syntax); ++ curline->syntax = NULL; ++ curline->syntax_length = 0; ++ curline->syntax_size = 0; ++ if (li == state->first_shown.row + state->maxy - 1) ++ return; ++ ++ ti = 0; ++ quote_start = 0; ++ comment_start = 0; ++recalc_syn_next_token: ++ if ((ti >= curline->length) || (curline->text[ti] == 0L)) ++ { ++ li++; ++ goto recalc_syn_next_line; ++ } ++ ++ if (quote) ++ goto recalc_syn_skip_comments; ++ ++ /* Check for comments */ ++ if (comment_index >= 0) ++ { ++ seen_end_comment = 0; ++ while (ti < curline->length) ++ { ++ seen_end_comment = u32_starts_with(&curline->text[ti], ++ clist[comment_index].end_text); ++ if (seen_end_comment) ++ break; ++ ti++; ++ } ++ assert(clist[comment_index].end_size > 1); ++ ti += clist[comment_index].end_size - 1; ++ if (seen_end_comment) ++ comment_index = -1; ++ if (syntax_add(curline, SE_COMMENT, comment_start, ++ ti - comment_start) ++ < 0) ++ { ++ MEMCCPY(state->error, errors[ERR_CANT_ALLOC], ++ LINE_DEFAULT, temp); ++ state->running = 0; ++ return; ++ } ++ ti++; ++ comment_start = 0; ++ if (comment_index >= 0) ++ { ++ li++; ++ goto recalc_syn_next_line; ++ } ++ goto recalc_syn_next_token; ++ } ++ ++ i = 0; ++ comment = &clist[0]; ++ while (comment->start_text != NULL) ++ { ++ if ((curline->length >= ti + comment->start_size - 1) ++ && (u32_starts_with(&curline->text[ti], ++ comment->start_text))) ++ { ++ assert(comment->start_size > 1); ++ if (comment->end_text != NULL) ++ goto recalc_syn_ml_comment; ++ ++ if (syntax_add(curline, SE_COMMENT, ti, ++ curline->length - ti) ++ < 0) ++ { ++ MEMCCPY(state->error, errors[ERR_CANT_ALLOC], ++ LINE_DEFAULT, temp); ++ state->running = 0; ++ return; ++ } ++ li++; ++ goto recalc_syn_next_line; ++ ++ recalc_syn_ml_comment: ++ comment_start = ti; ++ comment_index = i; ++ ti += comment->start_size; ++ goto recalc_syn_next_token; ++ } ++ i++; ++ comment = &clist[i]; ++ } ++ ++ /* Check for string literals */ ++recalc_syn_skip_comments: ++ if (quote) ++ { ++ while ((ti < curline->length) ++ && (((ti > 0) && (curline->text[ti - 1] == L'\\')) ++ || (curline->text[ti] != quote))) ++ ti++; ++ if (curline->text[ti] == quote) ++ { ++ quote = 0; ++ ti++; ++ } ++ assert(ti != quote_start); ++ if (syntax_add(curline, SE_LITERAL, quote_start, ++ ti - quote_start) ++ < 0) ++ { ++ MEMCCPY(state->error, errors[ERR_CANT_ALLOC], ++ LINE_DEFAULT, temp); ++ state->running = 0; ++ return; ++ } ++ quote_start = 0; ++ goto recalc_syn_next_token; ++ } ++ ++ assert(quote == 0); ++ if (((ti == 0) || ((ti > 0) && (curline->text[ti - 1] != L'\\'))) ++ && ((curline->text[ti] == '"') || (curline->text[ti] == '\''))) ++ { ++ quote = curline->text[ti]; ++ quote_start = ti; ++ ti++; ++ goto recalc_syn_next_token; ++ } ++ ++ /* Check for numeric literals */ ++ if (isdigit((const char)curline->text[ti]) ++ && ((ti == 0) || ((ti > 0) && !isalnum(curline->text[ti - 1])))) ++ { ++ literal_start = ti; ++ while ((ti < curline->length) && (isdigit(curline->text[ti]))) ++ ti++; ++ assert(ti != literal_start); ++ if (syntax_add(curline, SE_LITERAL, literal_start, ++ ti - literal_start) ++ < 0) ++ { ++ MEMCCPY(state->error, errors[ERR_CANT_ALLOC], ++ LINE_DEFAULT, temp); ++ state->running = 0; ++ return; ++ } ++ goto recalc_syn_next_token; ++ } ++ ++ /* Check for keywords */ ++ i = 0; ++ keyword = klist; ++ while (keyword->text != NULL) ++ { ++ if ((curline->length >= ti + keyword->size - 1) ++ && ((ti == 0) ++ || ((ti > 0) && !isalnum(curline->text[ti - 1]) ++ && curline->text[ti - 1] != L'_')) ++ && (!isalnum(curline->text[ti + keyword->size - 1])) ++ && (curline->text[ti + keyword->size - 1] != L'_') ++ && (u32_starts_with(&curline->text[ti], keyword->text))) ++ { ++ /* Add new syntax region */ ++ assert(keyword->size > 1); ++ if (syntax_add(curline, SE_KEYWORD, ti, ++ keyword->size - 1) ++ < 0) ++ { ++ MEMCCPY(state->error, errors[ERR_CANT_ALLOC], ++ LINE_DEFAULT, temp); ++ state->running = 0; ++ return; ++ } ++ ti += keyword->size - 1; ++ goto recalc_syn_next_token; ++ } ++ i++; ++ keyword = &klist[i]; ++ } ++ ++ /* Check for operators */ ++ i = 0; ++ /* clang-format off */ ++ operator = olist; ++ /* clang-format on */ ++ while (operator->text != NULL) ++ { ++ if ((curline->length >= ti + operator->size - 1) ++ && (u32_starts_with(&curline->text[ti], operator->text))) ++ { ++ /* Add new syntax region */ ++ assert(operator->size> 1); ++ if (syntax_add(curline, SE_OPERATOR, ++ ti, operator->size - 1) ++ < 0) ++ { ++ MEMCCPY(state->error, errors[ERR_CANT_ALLOC], ++ LINE_DEFAULT, temp); ++ state->running = 0; ++ return; ++ } ++ ti += operator->size - 1; ++ goto recalc_syn_next_token; ++ } ++ i++; ++ /* clang-format off */ ++ operator = &olist[i]; ++ /* clang-format on */ ++ } ++ ++ ti++; ++ goto recalc_syn_next_token; ++} ++ ++void + redraw_status(struct DrawState* state) + { + assert(state != NULL); +@@ -768,6 +1196,11 @@ redraw_status(struct DrawState* state) + recalculate_size(state); + state->recalculate_size = 0; + } ++ if (state->syntax_enabled && state->recalculate_syntax) ++ { ++ recalculate_syntax(state); ++ state->recalculate_syntax = 0; ++ } + (void)draw_status(state); + tb_present(); + } +@@ -790,7 +1223,8 @@ redraw_status(struct DrawState* state) + * */ + void + u32_draw_string(const int x, const int y, const uint16_t fg, const uint16_t bg, +- const uint32_t* s, const int mc, const int fc, const int p_l, ++ const uint32_t* s, const struct SyntaxRegion* syntax, ++ const ssize_t syntax_length, const int mc, const int fc, const int p_l, + const int p_r, const Alignment align, const int ds, const int fill, + const uint32_t* srch, const uint16_t sfg, const uint16_t sbg, + const int ssi, const int sei, const uint16_t slfg, const uint16_t slbg) +@@ -828,6 +1262,8 @@ u32_draw_string(const int x, const int y, const uint16 + + int w = 0; /* actual on-screen rune width */ + ++ int sel_on = 0; ++ + const int ts = TAB_SIZE; + + const uint32_t* ps = s; /* pointer to current character in +@@ -836,11 +1272,23 @@ u32_draw_string(const int x, const int y, const uint16 + uint16_t cfg = fg; /* current fg */ + uint16_t cbg = bg; /* current bg */ + ++ struct Color defcolor, selection, highlight; ++ + int hctl = 0; /* highlight "characters to live", when this + * reaches zero the highlighting is turned + * off */ + + assert(s != NULL); ++ ++ defcolor.fg = fg; ++ defcolor.bg = bg; ++ ++ selection.fg = slfg; ++ selection.bg = slbg; ++ ++ highlight.fg = sfg; ++ highlight.bg = sbg; ++ + c = mc - p_l - p_r; + + if (align == LEFT) +@@ -872,7 +1320,12 @@ u32_draw_string(const int x, const int y, const uint16 + */ + while (*ps && sc < c) + { +- if ((hctl == 0) && ((ps - s) >= ssi) && ((ps - s) < sei)) ++ cfg = fg; ++ cbg = bg; ++ ++ sel_on = ((ps - s) >= ssi) && ((ps - s) < sei); ++ ++ if ((hctl == 0) && sel_on) + { + cfg = slfg; + cbg = slbg; +@@ -889,6 +1342,9 @@ u32_draw_string(const int x, const int y, const uint16 + } + } + ++ synsetcolor(&cfg, &cbg, &defcolor, &selection, sel_on, ++ &highlight, hctl > 0, syntax, syntax_length, ps - s); ++ + if (*ps == '\t') + { + if (ssc < ds) +@@ -1004,11 +1460,11 @@ u32_draw_string(const int x, const int y, const uint16 + } + ssc += w; + } ++ sel_on = ((ps - s) >= ssi) && ((ps - s) < sei); ++ + if (hctl == 0) +- { +- cfg = fg; +- cbg = bg; +- } ++ synsetcolor(&cfg, &cbg, &defcolor, &selection, sel_on, ++ &highlight, 0, syntax, syntax_length, ps - s); + ps++; + } + +diff --git a/draw.h b/draw.h +index 39ca2835b..fb80cff14 100644 +--- a/draw.h ++++ b/draw.h +@@ -37,17 +37,58 @@ enum { + + typedef enum { LEFT, CENTER, RIGHT } Alignment; + ++typedef enum { FT_GENERIC, FT_C, FT_SHELL } FileType; ++ ++typedef enum { ++ SE_DEFAULT, ++ SE_COMMENT, ++ SE_KEYWORD, ++ SE_OPERATOR, ++ SE_LITERAL ++} SyntaxElement; ++ ++struct Keyword { ++ uint32_t* text; ++ int size; ++}; ++ ++struct Comment { ++ uint32_t* start_text; ++ uint32_t* end_text; ++ int start_size; ++ int end_size; ++}; ++ ++struct Operator { ++ uint32_t* text; ++ int size; ++}; ++ + struct Coord { + int col; + int row; + }; + ++struct Color { ++ uint16_t fg; ++ uint16_t bg; ++}; ++ ++struct SyntaxRegion { ++ ssize_t start; ++ ssize_t length; ++ struct Color color; ++}; ++ + struct BufferLine { + uint32_t* text; + ssize_t length; /* both in uint32_t units */ + ssize_t size; + uint16_t fg; + uint16_t bg; ++ struct SyntaxRegion* syntax; ++ ssize_t syntax_length; ++ ssize_t syntax_size; + }; + + struct DrawState { +@@ -87,12 +128,17 @@ struct DrawState { + int dirty; + int new_file; + int recalculate_size; ++ int syntax_enabled; ++ int recalculate_syntax; + int mode; + int maxx; + int maxy; + struct Coord sel_start; /* buffer coordinate */ + struct Coord sel_end; /* buffer coordinate */ + int backup_possible; /* is saving backup possible? */ ++ struct Keyword* keywords; ++ struct Comment* comments; ++ struct Operator* operators; + }; + + struct Key { +@@ -109,6 +155,13 @@ struct StatusSegment { + Alignment alignment; + }; + ++extern const struct Keyword c_keywords[]; ++extern const struct Comment c_comments[]; ++extern const struct Operator c_operators[]; ++extern const struct Keyword shell_keywords[]; ++extern const struct Comment shell_comments[]; ++extern const struct Operator shell_operators[]; ++ + void assign_coord(struct Coord* dest, const struct Coord src); + ssize_t buffer_length(uint32_t* buffer, const ssize_t max_index); + int calculate_help_width(const struct DrawState* state); +@@ -125,7 +178,8 @@ void draw_rect(const int startx, const int endx, const + int draw_screen(const struct DrawState* state); + int draw_status(const struct DrawState* state); + int draw_string(const int x, const int y, const uint16_t fg, const uint16_t bg, +- const char* s, const int max_cols, const int fill_cols, ++ const char* s, const struct SyntaxRegion* syntax, ++ const ssize_t syntax_length, const int max_cols, const int fill_cols, + const int padding_left, const int padding_right, const Alignment align, + const int ds, const int fill, const uint32_t* srch, const uint16_t sfg, + const uint16_t sbg, const int ssi, const int sei, const uint16_t slfg, +@@ -148,10 +202,12 @@ void init_drawstate(struct DrawState* state, char* err + const int skip_filenames_init); + void recalculate_line_numbers(struct DrawState* state); + void recalculate_size(struct DrawState* state); ++void recalculate_syntax(struct DrawState* state); + void redraw_status(struct DrawState* state); + void u32_draw_string(const int x, const int y, const uint16_t fg, +- const uint16_t bg, const uint32_t* s, const int max_cols, +- const int fill_cols, const int padding_left, const int padding_right, +- const Alignment align, const int ds, const int fill, +- const uint32_t* srch, const uint16_t sfg, const uint16_t sbg, +- const int ssi, const int sei, const uint16_t slfg, const uint16_t slbg); ++ const uint16_t bg, const uint32_t* s, const struct SyntaxRegion* syntax, ++ const ssize_t syntax_length, const int max_cols, const int fill_cols, ++ const int padding_left, const int padding_right, const Alignment align, ++ const int ds, const int fill, const uint32_t* srch, const uint16_t sfg, ++ const uint16_t sbg, const int ssi, const int sei, const uint16_t slfg, ++ const uint16_t slbg); +diff --git a/sled.c b/sled.c +index 39ca2835b..fb80cff14 100644 +--- a/sled.c ++++ b/sled.c +@@ -67,6 +67,18 @@ extern const char* prompt_reread_dirty; + extern const char* replace_dialog_title; + extern const char* search_dialog_title; + ++struct SyntaxEnding { ++ char* text; ++ FileType type; ++}; ++/* clang-format off */ ++static const struct SyntaxEnding syntax_endings[] = { ++ { .text = ".c", .type = FT_C }, ++ { .text = ".h", .type = FT_C }, ++ { .text = ".sh", .type = FT_SHELL } ++}; ++/* clang-format on */ ++ + static int + cancel_close(struct DrawState* state) + { +@@ -195,6 +207,8 @@ create_file(struct DrawState* state) + recalculate_line_numbers(state); + state->coord.row = 0; + state->dirty = 1; ++ if (state->syntax_enabled) ++ recalculate_syntax(state); + + return LOAD_ERR_NONE; + } +@@ -325,6 +339,8 @@ do_search_replace(struct DrawState* state) + state->buffer[row].length = buflen; + } + free(buf); ++ if (state->syntax_enabled) ++ recalculate_syntax(state); + return 0; + + do_search_replace_alloc_error: +@@ -378,6 +394,8 @@ erase_backwards(struct DrawState* state) + buffer_line->length--; + position_to(state, state->coord.row, state->coord.col - 1); + state->dirty = 1; ++ if (state->syntax_enabled) ++ recalculate_syntax(state); + } + else if (state->coord.row > 0) + { +@@ -415,6 +433,8 @@ erase_forward(struct DrawState* state) + buffer_line->text[i] = 0; + buffer_line->length--; + state->dirty = 1; ++ if (state->syntax_enabled) ++ recalculate_syntax(state); + } + else if ((state->coord.row + 1) < state->rows_count) + simple_join_lines(state); +@@ -450,6 +470,8 @@ erase_line(struct DrawState* state) + position_to(state, state->coord.row, 0); + state->rows_count--; + state->dirty = 1; ++ if (state->syntax_enabled) ++ recalculate_syntax(state); + recalculate_line_numbers(state); + return 0; + +@@ -489,6 +511,8 @@ erase_prev_word(struct DrawState* state) + state->coord.col = i; + state->display_column = display_length(current->text, state->coord.col); + state->dirty = 1; ++ if (state->syntax_enabled) ++ recalculate_syntax(state); + return 0; + } + +@@ -545,6 +569,8 @@ erase_selection(struct DrawState* state) + } + free(buf); + state->dirty = 1; ++ if (state->syntax_enabled) ++ recalculate_syntax(state); + } + return 0; + } +@@ -570,6 +596,8 @@ erase_to_end(struct DrawState* state) + buffer_line->length = state->coord.col; + move_end(state); + state->dirty = 1; ++ if (state->syntax_enabled) ++ recalculate_syntax(state); + return 0; + } + +@@ -604,6 +632,8 @@ erase_to_start(struct DrawState* state) + buffer_line->length = len - state->coord.col; + move_start(state); + state->dirty = 1; ++ if (state->syntax_enabled) ++ recalculate_syntax(state); + return 0; + } + +@@ -1160,6 +1190,8 @@ insert_char(struct DrawState* state, uint32_t ch) + + insert_skip_wrap: + state->dirty = 1; ++ if (state->syntax_enabled) ++ recalculate_syntax(state); + + return 0; + +@@ -1865,7 +1897,11 @@ position_to(struct DrawState* state, const int row, co + state->first_shown.col = fsc; + } + if (old_first_shown_row != state->first_shown.row) ++ { ++ if (state->syntax_enabled) ++ recalculate_syntax(state); + recalculate_line_numbers(state); ++ } + + if (IN(state->mode, M_SELECTION)) + assign_coord(&state->sel_end, state->coord); +@@ -2025,6 +2061,8 @@ reread_file(struct DrawState* state) + break; + default: + recalculate_line_numbers(state); ++ if (state->syntax_enabled) ++ recalculate_syntax(state); + } + return result; + } +@@ -2905,6 +2943,8 @@ main(int argc, char** argv) + int result = 0; + int dir_access; + int dir_access_errno; ++ int found_ending = 0; ++ int i; + + *error = 0; + *action = 0; +@@ -2988,6 +3028,40 @@ main(int argc, char** argv) + + init_drawstate(&state, error, action, filename, prompt, 0, 0); + ++ for (i = 0; (size_t)i < LEN(syntax_endings); i++) ++ { ++ int filename_len = strlen(state.filename); ++ int ending_len = strlen(syntax_endings[i].text); ++ ++ found_ending = 0; ++ if ((filename_len >= ending_len) ++ && !strcmp(syntax_endings[i].text, ++ state.filename + filename_len - ending_len)) ++ { ++ found_ending = 1; ++ break; ++ } ++ } ++ if (found_ending) ++ { ++ state.syntax_enabled = 1; ++ switch (syntax_endings[i].type) ++ { ++ case FT_C: ++ state.keywords = (struct Keyword*)c_keywords; ++ state.comments = (struct Comment*)c_comments; ++ state.operators = (struct Operator*)c_operators; ++ break; ++ case FT_SHELL: ++ state.keywords = (struct Keyword*)shell_keywords; ++ state.comments = (struct Comment*)shell_comments; ++ state.operators = (struct Operator*)shell_operators; ++ break; ++ default: ++ /* Shouldn't happen */ ++ assert(0); ++ } ++ } + #ifdef __OpenBSD__ + if (unveil(state.filename, "rwc") < 0) + { +@@ -3054,6 +3128,8 @@ main(int argc, char** argv) + } + else /* rfn is relative */ + { ++ size_t rfn_dir_len; ++ + state.orig_real_filename + = strndup(rfn, state.rfn_len); + if (!state.orig_real_filename) +@@ -3069,11 +3145,13 @@ main(int argc, char** argv) + print_error(1, "Pathname too long"); + goto main_cleanup; + } +- *(temp - 1) = '/'; ++ *(temp - 1) = '/'; /* overwrite NUL */ ++ rfn_dir_len = temp - state.real_filename - 1; + MEMCCPY_EXT(temp, state.real_filename, rfn, +- limits[L_PATH_MAX] - state.rfn_len - 1, +- temp); +- state.rfn_len = temp - state.real_filename - 1; ++ limits[L_PATH_MAX] - rfn_dir_len, temp); ++ if (temp) ++ state.rfn_len = temp ++ - state.real_filename - 1; + } + free(rfn); + rfn = NULL; +@@ -3206,9 +3284,10 @@ main_skip_backups: + + init_termbox: + tb_init(); +- *error = 0; +- state.maxx = 0; +- state.recalculate_size = 1; ++ *error = 0; ++ state.maxx = 0; ++ state.recalculate_size = 1; ++ state.recalculate_syntax = 1; + + while (state.running) + { +@@ -3220,6 +3299,11 @@ init_termbox: + recalculate_size(&state); + state.recalculate_size = 0; + } ++ if (state.syntax_enabled && state.recalculate_syntax) ++ { ++ recalculate_syntax(&state); ++ state.recalculate_syntax = 0; ++ } + if (draw(&state)) + { + state.running = 0; +-- +2.44.1 +