diff options
author | pommicket <pommicket@gmail.com> | 2022-07-22 14:54:07 -0400 |
---|---|---|
committer | pommicket <pommicket@gmail.com> | 2022-07-22 14:54:07 -0400 |
commit | 35b1b0129e8791a9412173acf3756606d0cc397d (patch) | |
tree | 6e631f6382cabf4ad2522bf4386828aaabae3347 | |
parent | 47a65f608f485af32d2df12be489146ad67839e1 (diff) |
go syntax highlighting
also :goto-line now clamps numbers < 1 and > nlines instead of rejecting them
-rw-r--r-- | README.md | 4 | ||||
-rw-r--r-- | keywords.h | 21 | ||||
-rwxr-xr-x | keywords.py | 24 | ||||
-rw-r--r-- | menu.c | 4 | ||||
-rw-r--r-- | syntax.c | 147 | ||||
-rw-r--r-- | ted.cfg | 1 | ||||
-rw-r--r-- | ted.h | 7 | ||||
-rw-r--r-- | test.go | 18 |
8 files changed, 222 insertions, 4 deletions
@@ -30,7 +30,7 @@ in other editors. - Multiple tabs, each with a different file - Split screen - Auto-indent -- Syntax highlighting for C, C++, HTML, Java, JavaScript, LaTeX, Markdown, Python, and Rust. +- Syntax highlighting for C, C++, Go, HTML, Java, JavaScript, LaTeX, Markdown, Python, and Rust. - Find and replace (with regular expressions!) - Run build command, go to errors - Run any shell command @@ -146,7 +146,7 @@ Then, open windows\_installer\\ted\\ted.sln, and build. <tr><td>1.0r1</td> <td>Windows-specific bugfixes, update to new version of PCRE2</td> <td>2022 Jan 1</td></tr> <tr><td>1.0r2</td> <td>Various bugfixes involving closing tabs and windows</td> <td>2022 Mar 26</td></tr> <tr><td>1.0r3</td> <td>Better TeX syntax highlighting, move to cursor on backspace/delete</td> <td>2022 Jul 7</td></tr> -<tr><td>1.1</td> <td>Minor fixes, syntax highlighting for JavaScript and Java</td> <td>2022 Jul 22</td></tr> +<tr><td>1.1</td> <td>Minor fixes, syntax highlighting for JavaScript, Java, and Go</td> <td>2022 Jul 22</td></tr> </table> ## License @@ -170,6 +170,27 @@ static Keyword const *const syntax_all_keywords_javascript[] = { ['A'] = syntax_keywords_javascript_A, ['B'] = syntax_keywords_javascript_B, ['D'] = syntax_keywords_javascript_D, ['E'] = syntax_keywords_javascript_E, ['F'] = syntax_keywords_javascript_F, ['G'] = syntax_keywords_javascript_G, ['I'] = syntax_keywords_javascript_I, ['J'] = syntax_keywords_javascript_J, ['M'] = syntax_keywords_javascript_M, ['N'] = syntax_keywords_javascript_N, ['O'] = syntax_keywords_javascript_O, ['P'] = syntax_keywords_javascript_P, ['R'] = syntax_keywords_javascript_R, ['S'] = syntax_keywords_javascript_S, ['T'] = syntax_keywords_javascript_T, ['U'] = syntax_keywords_javascript_U, ['W'] = syntax_keywords_javascript_W, ['a'] = syntax_keywords_javascript_a, ['b'] = syntax_keywords_javascript_b, ['c'] = syntax_keywords_javascript_c, ['d'] = syntax_keywords_javascript_d, ['e'] = syntax_keywords_javascript_e, ['f'] = syntax_keywords_javascript_f, ['g'] = syntax_keywords_javascript_g, ['i'] = syntax_keywords_javascript_i, ['l'] = syntax_keywords_javascript_l, ['n'] = syntax_keywords_javascript_n, ['p'] = syntax_keywords_javascript_p, ['r'] = syntax_keywords_javascript_r, ['s'] = syntax_keywords_javascript_s, ['t'] = syntax_keywords_javascript_t, ['u'] = syntax_keywords_javascript_u, ['v'] = syntax_keywords_javascript_v, ['w'] = syntax_keywords_javascript_w, ['y'] = syntax_keywords_javascript_y }; +static Keyword const syntax_keywords_go_a[2] = {{"append", SYNTAX_BUILTIN}}; +static Keyword const syntax_keywords_go_b[4] = {{"break", SYNTAX_KEYWORD},{"bool", SYNTAX_BUILTIN},{"byte", SYNTAX_BUILTIN}}; +static Keyword const syntax_keywords_go_c[12] = {{"case", SYNTAX_KEYWORD},{"chan", SYNTAX_KEYWORD},{"const", SYNTAX_KEYWORD},{"continue", SYNTAX_KEYWORD},{"cap", SYNTAX_BUILTIN},{"close", SYNTAX_BUILTIN},{"comparable", SYNTAX_BUILTIN},{"complex", SYNTAX_BUILTIN},{"complex128", SYNTAX_BUILTIN},{"complex64", SYNTAX_BUILTIN},{"copy", SYNTAX_BUILTIN}}; +static Keyword const syntax_keywords_go_d[4] = {{"default", SYNTAX_KEYWORD},{"defer", SYNTAX_KEYWORD},{"delete", SYNTAX_BUILTIN}}; +static Keyword const syntax_keywords_go_e[3] = {{"else", SYNTAX_KEYWORD},{"error", SYNTAX_BUILTIN}}; +static Keyword const syntax_keywords_go_f[7] = {{"fallthrough", SYNTAX_KEYWORD},{"for", SYNTAX_KEYWORD},{"func", SYNTAX_KEYWORD},{"false", SYNTAX_CONSTANT},{"float32", SYNTAX_BUILTIN},{"float64", SYNTAX_BUILTIN}}; +static Keyword const syntax_keywords_go_g[3] = {{"go", SYNTAX_KEYWORD},{"goto", SYNTAX_KEYWORD}}; +static Keyword const syntax_keywords_go_i[11] = {{"if", SYNTAX_KEYWORD},{"import", SYNTAX_KEYWORD},{"interface", SYNTAX_KEYWORD},{"iota", SYNTAX_CONSTANT},{"imag", SYNTAX_BUILTIN},{"int", SYNTAX_BUILTIN},{"int16", SYNTAX_BUILTIN},{"int32", SYNTAX_BUILTIN},{"int64", SYNTAX_BUILTIN},{"int8", SYNTAX_BUILTIN}}; +static Keyword const syntax_keywords_go_l[2] = {{"len", SYNTAX_BUILTIN}}; +static Keyword const syntax_keywords_go_m[3] = {{"map", SYNTAX_KEYWORD},{"make", SYNTAX_BUILTIN}}; +static Keyword const syntax_keywords_go_n[3] = {{"nil", SYNTAX_CONSTANT},{"new", SYNTAX_BUILTIN}}; +static Keyword const syntax_keywords_go_p[5] = {{"package", SYNTAX_KEYWORD},{"panic", SYNTAX_BUILTIN},{"print", SYNTAX_BUILTIN},{"println", SYNTAX_BUILTIN}}; +static Keyword const syntax_keywords_go_r[6] = {{"range", SYNTAX_KEYWORD},{"return", SYNTAX_KEYWORD},{"real", SYNTAX_BUILTIN},{"recover", SYNTAX_BUILTIN},{"rune", SYNTAX_BUILTIN}}; +static Keyword const syntax_keywords_go_s[5] = {{"select", SYNTAX_KEYWORD},{"struct", SYNTAX_KEYWORD},{"switch", SYNTAX_KEYWORD},{"string", SYNTAX_BUILTIN}}; +static Keyword const syntax_keywords_go_t[3] = {{"type", SYNTAX_KEYWORD},{"true", SYNTAX_CONSTANT}}; +static Keyword const syntax_keywords_go_u[7] = {{"uint", SYNTAX_BUILTIN},{"uint16", SYNTAX_BUILTIN},{"uint32", SYNTAX_BUILTIN},{"uint64", SYNTAX_BUILTIN},{"uint8", SYNTAX_BUILTIN},{"uintptr", SYNTAX_BUILTIN}}; +static Keyword const syntax_keywords_go_v[2] = {{"var", SYNTAX_KEYWORD}}; +static Keyword const *const syntax_all_keywords_go[] = { + ['a'] = syntax_keywords_go_a, ['b'] = syntax_keywords_go_b, ['c'] = syntax_keywords_go_c, ['d'] = syntax_keywords_go_d, ['e'] = syntax_keywords_go_e, ['f'] = syntax_keywords_go_f, ['g'] = syntax_keywords_go_g, ['i'] = syntax_keywords_go_i, ['l'] = syntax_keywords_go_l, ['m'] = syntax_keywords_go_m, ['n'] = syntax_keywords_go_n, ['p'] = syntax_keywords_go_p, ['r'] = syntax_keywords_go_r, ['s'] = syntax_keywords_go_s, ['t'] = syntax_keywords_go_t, ['u'] = syntax_keywords_go_u, ['v'] = syntax_keywords_go_v +}; + static Keyword const syntax_keywords_java_a[3] = {{"abstract", SYNTAX_KEYWORD},{"assert", SYNTAX_KEYWORD}}; static Keyword const syntax_keywords_java_b[4] = {{"boolean", SYNTAX_KEYWORD},{"break", SYNTAX_KEYWORD},{"byte", SYNTAX_KEYWORD}}; static Keyword const syntax_keywords_java_c[7] = {{"case", SYNTAX_KEYWORD},{"catch", SYNTAX_KEYWORD},{"char", SYNTAX_KEYWORD},{"class", SYNTAX_KEYWORD},{"const", SYNTAX_KEYWORD},{"continue", SYNTAX_KEYWORD}}; diff --git a/keywords.py b/keywords.py index b7f7432..1e345d9 100755 --- a/keywords.py +++ b/keywords.py @@ -300,6 +300,28 @@ constants_java = [ 'true', 'false', 'null' ] +keywords_go = [ + 'break', 'default', 'func', 'interface', 'select', + 'case', 'defer', 'go', 'map', 'struct', + 'chan', 'else', 'goto', 'package', 'switch', + 'const', 'fallthrough', 'if', 'range', 'type', + 'continue', 'for', 'import', 'return', 'var' +] + +constants_go = [ + 'true', 'false', 'iota', 'nil' +] + +builtins_go = [ + 'new', 'make', 'cap', 'len', 'close', 'append', 'copy', 'delete', + 'complex', 'real', 'imag', 'panic', 'recover', 'print', 'println', + 'bool', 'uint8', 'uint16', 'uint32', 'uint64', + 'int8', 'int16', 'int32', 'int64', + 'float32', 'float64', 'complex64', 'complex128', + 'byte', 'rune', 'uint', 'int', 'uintptr', 'string', 'error', + 'comparable' +] + file = open('keywords.h', 'w') file.write('''// keywords for all languages ted supports // This file was auto-generated by keywords.py @@ -325,6 +347,8 @@ output_keywords(file, cpp_things, 'cpp') output_keywords(file, label(keywords_rust, SYNTAX_KEYWORD) + label(builtins_rust, SYNTAX_BUILTIN) + label(constants_rust, SYNTAX_CONSTANT), 'rust') output_keywords(file, label(keywords_javascript, SYNTAX_KEYWORD) + label(builtins_javascript, SYNTAX_BUILTIN) + label(constants_javascript, SYNTAX_CONSTANT), 'javascript') +output_keywords(file, label(keywords_go, SYNTAX_KEYWORD) + label(builtins_go, SYNTAX_BUILTIN) + + label(constants_go, SYNTAX_CONSTANT), 'go') output_keywords(file, label(keywords_java, SYNTAX_KEYWORD) + label(constants_java, SYNTAX_CONSTANT), 'java') output_keywords(file, label(keywords_python, SYNTAX_KEYWORD) + label(builtins_python, SYNTAX_BUILTIN), 'python') output_keywords(file, label(builtins_html, SYNTAX_BUILTIN), 'html') @@ -244,7 +244,9 @@ static void menu_update(Ted *ted) { char *end; long line_number = strtol(contents, &end, 0); TextBuffer *buffer = ted->prev_active_buffer; - if (line_number > 0 && *end == '\0' && line_number <= (long)buffer->nlines) { + if (*contents != '\0' && *end == '\0') { + if (line_number < 1) line_number = 1; + if (line_number > buffer->nlines) line_number = (long)buffer->nlines; BufferPos pos = {(u32)line_number - 1, 0}; if (line_buffer->line_buffer_submitted) { @@ -1169,7 +1169,7 @@ static void syntax_highlight_javascript(SyntaxState *state, char32_t const *line static void syntax_highlight_java(SyntaxState *state_ptr, char32_t const *line, u32 line_len, SyntaxCharType *char_types) { SyntaxState state = *state_ptr; bool in_string = false; - bool in_multiline_comment = (state & SYNTAX_STATE_CPP_MULTI_LINE_COMMENT) != 0; + bool in_multiline_comment = (state & SYNTAX_STATE_JAVA_MULTILINE_COMMENT) != 0; bool in_char = false; bool in_number = false; @@ -1291,6 +1291,148 @@ static void syntax_highlight_java(SyntaxState *state_ptr, char32_t const *line, ); } + +static void syntax_highlight_go(SyntaxState *state_ptr, char32_t const *line, u32 line_len, SyntaxCharType *char_types) { + SyntaxState state = *state_ptr; + bool string_is_raw = (state & SYNTAX_STATE_GO_RAW_STRING) != 0; + bool in_string = string_is_raw; + bool in_multiline_comment = (state & SYNTAX_STATE_GO_MULTILINE_COMMENT) != 0; + bool in_char = false; + bool in_number = false; + + int backslashes = 0; + for (u32 i = 0; i < line_len; ++i) { + + // are there 1/2 characters left in the line? + bool has_1_char = i + 1 < line_len; + + bool dealt_with = false; + + char32_t c = line[i]; + + switch (c) { + case '\\': + ++backslashes; + break; + case '/': + if (!in_multiline_comment && !in_string && !in_char && has_1_char) { + if (line[i + 1] == '/') { + if (char_types) memset(&char_types[i], SYNTAX_COMMENT, line_len - i); + i = line_len - 1; + dealt_with = true; + } else if (line[i + 1] == '*') { + in_multiline_comment = true; // /* + } + } else if (in_multiline_comment) { + if (i > 0 && line[i - 1] == '*') { + // */ + in_multiline_comment = false; + if (char_types) { + dealt_with = true; + char_types[i] = SYNTAX_COMMENT; + } + } + } + break; + case '"': + if (in_string && !string_is_raw && backslashes % 2 == 0) { + in_string = false; + if (char_types) { + dealt_with = true; + char_types[i] = SYNTAX_STRING; + } + } else if (!in_multiline_comment && !in_char) { + in_string = true; + string_is_raw = false; + } + break; + case '`': + if (in_string && string_is_raw) { + // end of raw string + in_string = false; + string_is_raw = false; + if (char_types) char_types[i] = SYNTAX_STRING; + dealt_with = true; + } else if (!in_string && !in_multiline_comment && !in_char) { + // start of raw string + in_string = true; + string_is_raw = true; + } + break; + case '\'': + if (in_char && backslashes % 2 == 0) { + in_char = false; + if (char_types) { + dealt_with = true; + char_types[i] = SYNTAX_CHARACTER; + } + } else if (!in_multiline_comment && !in_string) { + in_char = true; + } + break; + case ANY_DIGIT: + // a number! + if (char_types && !in_multiline_comment && !in_string && !in_number && !in_char) { + in_number = true; + if (i) { + if (line[i - 1] == '.') { + // support .6, for example + char_types[i - 1] = SYNTAX_CONSTANT; + } else if (is32_ident(line[i - 1])) { + // actually, this isn't a number. it's something like a*6* or u3*2*. + in_number = false; + } + } + } + break; + default: { + if ((i && is32_ident(line[i - 1])) || !is32_ident(c)) + break; // can't be a keyword on its own. + + // keywords don't matter for advancing the state + if (char_types && !in_multiline_comment && !in_number && !in_string && !in_char) { + u32 keyword_len = syntax_keyword_len(LANG_GO, line, i, line_len); + Keyword const *keyword = syntax_keyword_lookup(syntax_all_keywords_go, arr_count(syntax_all_keywords_go), + &line[i], keyword_len); + + + if (keyword) { + SyntaxCharType type = keyword->type; + for (size_t j = 0; j < keyword_len; ++j) { + char_types[i++] = type; + } + --i; // we'll increment i from the for loop + dealt_with = true; + break; + } + } + } break; + } + if (c != '\\') backslashes = 0; + if (in_number && !syntax_number_continues(line, line_len, i)) { + in_number = false; + } + + if (char_types && !dealt_with) { + SyntaxCharType type = SYNTAX_NORMAL; + if (in_multiline_comment) + type = SYNTAX_COMMENT; + else if (in_string) + type = SYNTAX_STRING; + else if (in_char) + type = SYNTAX_CHARACTER; + else if (in_number) + type = SYNTAX_CONSTANT; + + char_types[i] = type; + } + } + *state_ptr = (SyntaxState)( + (in_multiline_comment * SYNTAX_STATE_GO_MULTILINE_COMMENT) + | ((in_string && string_is_raw) * SYNTAX_STATE_GO_RAW_STRING) + ); +} + // 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. @@ -1331,6 +1473,9 @@ void syntax_highlight(SyntaxState *state, Language lang, char32_t const *line, u case LANG_JAVA: syntax_highlight_java(state, line, line_len, char_types); break; + case LANG_GO: + syntax_highlight_go(state, line, line_len, char_types); + break; case LANG_COUNT: assert(0); break; } } @@ -229,3 +229,4 @@ HTML = .html, .php, .xml, .xhtml Config = .cfg Javascript = .js Java = .java +Go = .go @@ -50,6 +50,11 @@ enum { SYNTAX_STATE_JAVA_MULTILINE_COMMENT = 0x01u }; +enum { + SYNTAX_STATE_GO_RAW_STRING = 0x01u, // backtick-enclosed string + SYNTAX_STATE_GO_MULTILINE_COMMENT = 0x02u +}; + typedef u8 SyntaxState; // If you are adding new languages, DO NOT change the constant values @@ -66,6 +71,7 @@ ENUM_U16 { LANG_CONFIG = 8, // .cfg files, e.g. ted.cfg LANG_JAVASCRIPT = 9, LANG_JAVA = 10, + LANG_GO = 11, LANG_COUNT } ENUM_U16_END(Language); @@ -86,6 +92,7 @@ static LanguageName const language_names[] = { {LANG_CONFIG, "Config"}, {LANG_JAVASCRIPT, "Javascript"}, {LANG_JAVA, "Java"}, + {LANG_GO, "Go"}, }; static_assert_if_possible(arr_count(language_names) == LANG_COUNT) @@ -0,0 +1,18 @@ +package main + +import "fmt" + +/* +what +a +wonderful +day +*///yes + +func main() { + var x []int = make([]int, 10) + fmt.Println(x != nil) + println(`hello + world\`) + println("yes\"\\") +} |