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:
| M | src/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) {