ded

Dramatic EDitor
Index Commits Files Refs README LICENSE
commit a1f24e4c19544fb6bc0b908905747d23bc7ad331
parent f813754dd2e9c32b55adc4837dbf6b5ff1c2684d
Author: rexim <reximkut@gmail.com>
Date:   Sun, 15 Jan 2023 01:33:23 +0700

Implement simple syntax highlighting based on matching substrings

It demonstrates really well that we have to properly tokenize the
source code instead. But we will do that later.

Diffstat:
Msrc/editor.c | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
1 file changed, 67 insertions(+), 4 deletions(-)
diff --git a/src/editor.c b/src/editor.c
@@ -151,6 +151,53 @@ void editor_recompute_lines(Editor *e)
     da_append(&e->lines, line);
 }
 
+bool editor_line_starts_with(Editor *e, size_t row, size_t col, const char *prefix)
+{
+    size_t prefix_len = strlen(prefix);
+    if (prefix_len == 0) {
+        return true;
+    }
+    Line line = e->lines.items[row];
+    if (col + prefix_len - 1 >= line.end) {
+        return false;
+    }
+    for (size_t i = 0; i < prefix_len; ++i) {
+        if (prefix[i] != e->data.items[line.begin + col + i]) {
+            return false;
+        }
+    }
+    return true;
+}
+
+const char *editor_line_starts_with_one_of(Editor *e, size_t row, size_t col, const char **prefixes, size_t prefixes_count)
+{
+    for (size_t i = 0; i < prefixes_count; ++i) {
+        if (editor_line_starts_with(e, row, col, prefixes[i])) {
+            return prefixes[i];
+        }
+    }
+    return NULL;
+}
+
+
+
+const char *keywords[] = {
+    "auto", "break", "case", "char", "const", "continue", "default", "do", "double",
+    "else", "enum", "extern", "float", "for", "goto", "if", "int", "long", "register",
+    "return", "short", "signed", "sizeof", "static", "struct", "switch", "typedef",
+    "union", "unsigned", "void", "volatile", "while", "alignas", "alignof", "and",
+    "and_eq", "asm", "atomic_cancel", "atomic_commit", "atomic_noexcept", "bitand",
+    "bitor", "bool", "catch", "char16_t", "char32_t", "char8_t", "class", "co_await",
+    "co_return", "co_yield", "compl", "concept", "const_cast", "consteval", "constexpr",
+    "constinit", "decltype", "delete", "dynamic_cast", "explicit", "export", "false",
+    "friend", "inline", "mutable", "namespace", "new", "noexcept", "not", "not_eq",
+    "nullptr", "operator", "or", "or_eq", "private", "protected", "public", "reflexpr",
+    "reinterpret_cast", "requires", "static_assert", "static_cast", "synchronized",
+    "template", "this", "thread_local", "throw", "true", "try", "typeid", "typename",
+    "using", "virtual", "wchar_t", "xor", "xor_eq",
+};
+#define keywords_count (sizeof(keywords)/sizeof(keywords[0]))
+
 void editor_render(SDL_Window *window, Free_Glyph_Atlas *atlas, Simple_Renderer *sr, Editor *editor)
 {
     int w, h;
@@ -206,10 +253,26 @@ void editor_render(SDL_Window *window, Free_Glyph_Atlas *atlas, Simple_Renderer 
 
             const Vec2f begin = vec2f(0, -(float)row * FREE_GLYPH_FONT_SIZE);
             Vec2f end = begin;
-            free_glyph_atlas_render_line_sized(
-                atlas, sr, editor->data.items + line.begin, line.end - line.begin,
-                &end,
-                vec4fs(1));
+
+            size_t col = 0;
+            while (line.begin + col < line.end) {
+                const char *keyword = editor_line_starts_with_one_of(editor, row, col, keywords, keywords_count);
+                if (keyword != NULL) {
+                    size_t keyword_len = strlen(keyword);
+                    free_glyph_atlas_render_line_sized(
+                        atlas, sr, editor->data.items + line.begin + col, keyword_len,
+                        &end,
+                        vec4f(1, 1, 0, 1));
+                    col += keyword_len;
+                } else {
+                    free_glyph_atlas_render_line_sized(
+                        atlas, sr, editor->data.items + line.begin + col, 1,
+                        &end,
+                        vec4fs(1));
+                    col += 1;
+                }
+            }
+
             // TODO: the max_line_len should be calculated based on what's visible on the screen right now
             float line_len = fabsf(end.x - begin.x);
             if (line_len > max_line_len) {