diff options
-rw-r--r-- | base.h | 8 | ||||
-rw-r--r-- | buffer.c | 19 | ||||
-rw-r--r-- | colors.h | 14 | ||||
-rw-r--r-- | main.c | 1 | ||||
-rw-r--r-- | string32.c | 13 | ||||
-rw-r--r-- | syntax.c | 121 | ||||
-rw-r--r-- | ted.cfg | 6 |
7 files changed, 179 insertions, 3 deletions
@@ -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 @@ -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); @@ -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; } + @@ -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" @@ -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; + } +} @@ -125,3 +125,9 @@ error-text = #fdd yes = #afa no = #faa cancel = #ffa + +preprocessor = #77f +string = #f77 +character = #f7f +comment = #777 +number = #aff |