Browse Source

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.
rexim 2 years ago
parent
commit
a1f24e4c19
1 changed files with 67 additions and 4 deletions
  1. 67 4
      src/editor.c

+ 67 - 4
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) {