summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--base.h8
-rw-r--r--buffer.c19
-rw-r--r--colors.h14
-rw-r--r--main.c1
-rw-r--r--string32.c13
-rw-r--r--syntax.c121
-rw-r--r--ted.cfg6
7 files changed, 179 insertions, 3 deletions
diff --git a/base.h b/base.h
index afd320a..293d95e 100644
--- a/base.h
+++ b/base.h
@@ -65,6 +65,14 @@ typedef long long llong;
typedef unsigned long long ullong;
#if __clang__
+#define ENUM_U8 typedef enum : u8
+#define ENUM_U8_END(name) name
+#else
+#define ENUM_U8 enum
+#define ENUM_U8_END(name) ; typedef u8 name
+#endif
+
+#if __clang__
#define ENUM_U16 typedef enum : u16
#define ENUM_U16_END(name) name
#else
diff --git a/buffer.c b/buffer.c
index 41c5d09..6f1b607 100644
--- a/buffer.c
+++ b/buffer.c
@@ -132,6 +132,8 @@ static bool buffer_pos_valid(TextBuffer *buffer, BufferPos p) {
// are there any unsaved changes?
bool buffer_unsaved_changes(TextBuffer *buffer) {
+ if (streq(buffer->filename, TED_UNTITLED) && buffer_empty(buffer))
+ return false; // don't worry about empty untitled buffers
return buffer->modified;
}
@@ -1969,13 +1971,21 @@ void buffer_render(TextBuffer *buffer, Rect r) {
// scroll <= sel - scrolloff
text_state.y -= (float)(buffer->scroll_y - start_line) * char_height;
- gl_color_rgba(colors[COLOR_TEXT]);
+ SyntaxState syntax_state = {0};
+ // dynamic array of character types, to be filled by syntax_highlight
+ SyntaxCharType *char_types = NULL;
for (u32 line_idx = start_line; line_idx < nlines; ++line_idx) {
Line *line = &lines[line_idx];
- for (char32_t *p = line->str, *end = p + line->len; p != end; ++p) {
- char32_t c = *p;
+ if (arr_len(char_types) < line->len) {
+ arr_set_len(char_types, line->len);
+ }
+ syntax_highlight(&syntax_state, LANG_C, line->str, line->len, char_types);
+ for (u32 i = 0; i < line->len; ++i) {
+ char32_t c = line->str[i];
+ SyntaxCharType type = char_types[i];
+ gl_color_rgba(colors[syntax_char_type_to_color(type)]);
switch (c) {
case '\n': assert(0); break;
case '\r': break; // for CRLF line endings
@@ -2003,6 +2013,9 @@ void buffer_render(TextBuffer *buffer, Rect r) {
column = 0;
}
+ arr_free(char_types);
+
+
text_chars_end(font);
diff --git a/colors.h b/colors.h
index 7945beb..64d7ca9 100644
--- a/colors.h
+++ b/colors.h
@@ -23,6 +23,13 @@ ENUM_U16 {
COLOR_NO,
COLOR_CANCEL,
+ COLOR_KEYWORD,
+ COLOR_COMMENT,
+ COLOR_PREPROCESSOR,
+ COLOR_STRING,
+ COLOR_CHARACTER,
+ COLOR_NUMBER,
+
COLOR_COUNT
} ENUM_U16_END(ColorSetting);
@@ -50,6 +57,12 @@ static ColorName const color_names[COLOR_COUNT] = {
{COLOR_ERROR_BG, "error-bg"},
{COLOR_ERROR_BORDER, "error-border"},
{COLOR_ACTIVE_TAB_HL, "active-tab-hl"},
+ {COLOR_KEYWORD, "keyword"},
+ {COLOR_COMMENT, "comment"},
+ {COLOR_PREPROCESSOR, "preprocessor"},
+ {COLOR_STRING, "string"},
+ {COLOR_CHARACTER, "character"},
+ {COLOR_NUMBER, "number"},
{COLOR_YES, "yes"},
{COLOR_NO, "no"},
{COLOR_CANCEL, "cancel"}
@@ -106,3 +119,4 @@ static Status color_from_str(char const *str, u32 *color) {
*color = (u32)r << 24 | (u32)g << 16 | (u32)b << 8 | (u32)a;
return true;
}
+
diff --git a/main.c b/main.c
index 8b68662..0932609 100644
--- a/main.c
+++ b/main.c
@@ -39,6 +39,7 @@ no_warn_end
#include "time.c"
#include "string32.c"
#include "arr.c"
+#include "syntax.c"
#include "buffer.c"
#include "ted.c"
#include "ui.c"
diff --git a/string32.c b/string32.c
index fc6117b..381bf15 100644
--- a/string32.c
+++ b/string32.c
@@ -110,3 +110,16 @@ size_t str32_remove_all_instances_of_char(String32 *s, char32_t c) {
s->len = out;
return ndeleted;
}
+
+bool is32_space(char32_t c) {
+ return c <= WINT_MAX && iswspace((wint_t)c);
+}
+
+bool is32_alpha(char32_t c) {
+ return c <= WINT_MAX && iswalpha((wint_t)c);
+}
+
+bool is32_alnum(char32_t c) {
+ return c <= WINT_MAX && iswalnum((wint_t)c);
+}
+
diff --git a/syntax.c b/syntax.c
new file mode 100644
index 0000000..50e87d9
--- /dev/null
+++ b/syntax.c
@@ -0,0 +1,121 @@
+typedef struct {
+ bool multi_line_comment:1; // are we in a multi-line comment? (delineated by /* */)
+ bool continued_single_line_comment:1; // if you add a \ to the end of a single-line comment, it is continued to the next line.
+ bool continued_preprocessor:1; // similar to above
+ bool continued_string:1;
+} SyntaxStateC;
+
+typedef union {
+ SyntaxStateC c;
+} SyntaxState;
+
+ENUM_U16 {
+ LANG_C
+} ENUM_U16_END(Language);
+
+ENUM_U8 {
+ SYNTAX_NORMAL,
+ SYNTAX_KEYWORD,
+ SYNTAX_COMMENT,
+ SYNTAX_PREPROCESSOR,
+ SYNTAX_STRING,
+ SYNTAX_CHARACTER,
+ SYNTAX_NUMBER
+} ENUM_U8_END(SyntaxCharType);
+
+// NOTE: returns the color setting, not the color
+ColorSetting syntax_char_type_to_color(SyntaxCharType t) {
+ switch (t) {
+ case SYNTAX_NORMAL: return COLOR_TEXT;
+ case SYNTAX_KEYWORD: return COLOR_KEYWORD;
+ case SYNTAX_COMMENT: return COLOR_COMMENT;
+ case SYNTAX_PREPROCESSOR: return COLOR_PREPROCESSOR;
+ case SYNTAX_STRING: return COLOR_STRING;
+ case SYNTAX_CHARACTER: return COLOR_CHARACTER;
+ case SYNTAX_NUMBER: return COLOR_NUMBER;
+ }
+}
+
+static void syntax_highlight_c(SyntaxStateC *state, char32_t *line, u32 line_len, SyntaxCharType *char_types) {
+ (void)state;
+ bool in_preprocessor = state->continued_preprocessor;
+ bool in_string = state->continued_string;
+ bool in_single_line_comment = state->continued_single_line_comment; // this kind of comment :)
+ bool in_multi_line_comment = state->multi_line_comment;
+ int backslashes = 0;
+ for (u32 i = 0; i < line_len; ++i) {
+ SyntaxCharType type = SYNTAX_NORMAL;
+ // necessary for the final " of a string to be highlighted
+ bool in_string_now = in_string;
+ bool in_multi_line_comment_now = in_multi_line_comment;
+
+ // are there 1/2 characters left in the line?
+ bool has_1_char = i + 1 < line_len;
+
+ switch (line[i]) {
+ case '#':
+ if (!in_single_line_comment && !in_multi_line_comment)
+ in_preprocessor = true;
+ break;
+ case '\\':
+ ++backslashes;
+ break;
+ case '/':
+ if (!in_multi_line_comment && !in_single_line_comment && !in_string && has_1_char) {
+ if (line[i + 1] == '/')
+ in_single_line_comment = true; // //
+ else if (line[i + 1] == '*')
+ in_multi_line_comment = in_multi_line_comment_now = true; // /*
+ } else if (in_multi_line_comment) {
+ if (i && line[i - 1] == '*') {
+ // */
+ in_multi_line_comment = false;
+ }
+ }
+ break;
+ case '"':
+ if (in_string && backslashes % 2 == 0)
+ in_string = false;
+ else if (!in_multi_line_comment && !in_single_line_comment)
+ in_string = in_string_now = true;
+ break;
+ case '<': // preprocessor string, e.g. <stdio.h>
+ if (in_preprocessor)
+ in_string = in_string_now = true;
+ break;
+ case '>':
+ if (in_preprocessor && in_string)
+ in_string = false;
+ break;
+ }
+ if (line[i] != '\\') backslashes = 0;
+
+ if (in_single_line_comment || in_multi_line_comment_now)
+ type = SYNTAX_COMMENT;
+ else if (in_string_now)
+ type = SYNTAX_STRING;
+ else if (in_preprocessor)
+ type = SYNTAX_PREPROCESSOR;
+
+ if (char_types) {
+ char_types[i] = type;
+ }
+ }
+ state->continued_single_line_comment = backslashes && in_single_line_comment;
+ state->continued_preprocessor = backslashes && in_preprocessor;
+ state->continued_string = backslashes && in_string;
+
+ state->multi_line_comment = in_multi_line_comment;
+}
+
+// This is the main syntax highlighting function. It will determine which colors to use for each character.
+// Rather than returning colors, it returns a character type (e.g. comment) which can be converted to a color.
+// To highlight multiple lines, start out with a zeroed SyntaxState, and pass a pointer to it each time.
+// You can set char_types to NULL if you just want to advance the state, and don't care about the character types.
+void syntax_highlight(SyntaxState *state, Language lang, char32_t *line, u32 line_len, SyntaxCharType *char_types) {
+ switch (lang) {
+ case LANG_C:
+ syntax_highlight_c(&state->c, line, line_len, char_types);
+ break;
+ }
+}
diff --git a/ted.cfg b/ted.cfg
index a639917..79c9e16 100644
--- a/ted.cfg
+++ b/ted.cfg
@@ -125,3 +125,9 @@ error-text = #fdd
yes = #afa
no = #faa
cancel = #ffa
+
+preprocessor = #77f
+string = #f77
+character = #f7f
+comment = #777
+number = #aff