diff options
-rw-r--r-- | README.md | 5 | ||||
-rw-r--r-- | buffer.c | 9 | ||||
-rw-r--r-- | keywords.h | 59 | ||||
-rwxr-xr-x | keywords.py | 63 | ||||
-rw-r--r-- | syntax.c | 255 | ||||
-rw-r--r-- | ted.cfg | 2 | ||||
-rw-r--r-- | ted.h | 13 | ||||
-rw-r--r-- | test.java | 13 | ||||
-rw-r--r-- | test.js | 12 |
9 files changed, 421 insertions, 10 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, LaTeX, Markdown, Python, and Rust. +- Syntax highlighting for C, C++, HTML, Java, JavaScript, LaTeX, Markdown, Python, and Rust. - Find and replace (with regular expressions!) - Run build command, go to errors - Run any shell command @@ -145,7 +145,8 @@ Then, open windows\_installer\\ted\\ted.sln, and build. <tr><td>1.0</td> <td>Bugfixes, small additional features, installers</td> <td>2021 Apr 20</td></tr> <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.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> </table> ## License @@ -2183,6 +2183,13 @@ bool buffer_save(TextBuffer *buffer) { bool buffer_save_as(TextBuffer *buffer, char const *new_filename) { char *prev_filename = buffer->filename; if ((buffer->filename = buffer_strdup(buffer, new_filename))) { + buffer->view_only = false; + + // ensure whole file is syntax highlighted when saving with a different + // file extension + buffer->frame_earliest_line_modified = 0; + buffer->frame_latest_line_modified = buffer->nlines - 1; + if (buffer_save(buffer)) { free(prev_filename); return true; @@ -2428,6 +2435,8 @@ void buffer_render(TextBuffer *buffer, Rect r) { if (buffer->frame_latest_line_modified >= buffer->frame_earliest_line_modified && syntax_highlighting) { // update syntax cache + if (buffer->frame_latest_line_modified >= buffer->nlines) + buffer->frame_latest_line_modified = buffer->nlines - 1; Line *earliest = &buffer->lines[buffer->frame_earliest_line_modified]; Line *latest = &buffer->lines[buffer->frame_latest_line_modified]; Line *buffer_last_line = &buffer->lines[buffer->nlines - 1]; @@ -131,6 +131,65 @@ static Keyword const *const syntax_all_keywords_rust[] = { ['A'] = syntax_keywords_rust_A, ['B'] = syntax_keywords_rust_B, ['C'] = syntax_keywords_rust_C, ['D'] = syntax_keywords_rust_D, ['E'] = syntax_keywords_rust_E, ['F'] = syntax_keywords_rust_F, ['I'] = syntax_keywords_rust_I, ['N'] = syntax_keywords_rust_N, ['O'] = syntax_keywords_rust_O, ['P'] = syntax_keywords_rust_P, ['R'] = syntax_keywords_rust_R, ['S'] = syntax_keywords_rust_S, ['T'] = syntax_keywords_rust_T, ['U'] = syntax_keywords_rust_U, ['V'] = syntax_keywords_rust_V, ['a'] = syntax_keywords_rust_a, ['b'] = syntax_keywords_rust_b, ['c'] = syntax_keywords_rust_c, ['d'] = syntax_keywords_rust_d, ['e'] = syntax_keywords_rust_e, ['f'] = syntax_keywords_rust_f, ['g'] = syntax_keywords_rust_g, ['i'] = syntax_keywords_rust_i, ['l'] = syntax_keywords_rust_l, ['m'] = syntax_keywords_rust_m, ['o'] = syntax_keywords_rust_o, ['p'] = syntax_keywords_rust_p, ['r'] = syntax_keywords_rust_r, ['s'] = syntax_keywords_rust_s, ['t'] = syntax_keywords_rust_t, ['u'] = syntax_keywords_rust_u, ['v'] = syntax_keywords_rust_v, ['w'] = syntax_keywords_rust_w, ['y'] = syntax_keywords_rust_y }; +static Keyword const syntax_keywords_javascript_A[8] = {{"AggregateError", SYNTAX_BUILTIN},{"Array", SYNTAX_BUILTIN},{"ArrayBuffer", SYNTAX_BUILTIN},{"AsyncFunction", SYNTAX_BUILTIN},{"AsyncGenerator", SYNTAX_BUILTIN},{"AsyncGeneratorFunction", SYNTAX_BUILTIN},{"Atomics", SYNTAX_BUILTIN}}; +static Keyword const syntax_keywords_javascript_B[5] = {{"BigInt", SYNTAX_BUILTIN},{"BigInt64Array", SYNTAX_BUILTIN},{"BigUint64Array", SYNTAX_BUILTIN},{"Boolean", SYNTAX_BUILTIN}}; +static Keyword const syntax_keywords_javascript_D[3] = {{"DataView", SYNTAX_BUILTIN},{"Date", SYNTAX_BUILTIN}}; +static Keyword const syntax_keywords_javascript_E[3] = {{"Error", SYNTAX_BUILTIN},{"EvalError", SYNTAX_BUILTIN}}; +static Keyword const syntax_keywords_javascript_F[5] = {{"FinalizationRegistry", SYNTAX_BUILTIN},{"Float32Array", SYNTAX_BUILTIN},{"Float64Array", SYNTAX_BUILTIN},{"Function", SYNTAX_BUILTIN}}; +static Keyword const syntax_keywords_javascript_G[3] = {{"Generator", SYNTAX_BUILTIN},{"GeneratorFunction", SYNTAX_BUILTIN}}; +static Keyword const syntax_keywords_javascript_I[7] = {{"Infinity", SYNTAX_BUILTIN},{"Int16Array", SYNTAX_BUILTIN},{"Int32Array", SYNTAX_BUILTIN},{"Int8Array", SYNTAX_BUILTIN},{"InternalError", SYNTAX_BUILTIN},{"Intl", SYNTAX_BUILTIN}}; +static Keyword const syntax_keywords_javascript_J[2] = {{"JSON", SYNTAX_BUILTIN}}; +static Keyword const syntax_keywords_javascript_M[3] = {{"Map", SYNTAX_BUILTIN},{"Math", SYNTAX_BUILTIN}}; +static Keyword const syntax_keywords_javascript_N[3] = {{"NaN", SYNTAX_BUILTIN},{"Number", SYNTAX_BUILTIN}}; +static Keyword const syntax_keywords_javascript_O[2] = {{"Object", SYNTAX_BUILTIN}}; +static Keyword const syntax_keywords_javascript_P[3] = {{"Promise", SYNTAX_BUILTIN},{"Proxy", SYNTAX_BUILTIN}}; +static Keyword const syntax_keywords_javascript_R[5] = {{"RangeError", SYNTAX_BUILTIN},{"ReferenceError", SYNTAX_BUILTIN},{"Reflect", SYNTAX_BUILTIN},{"RegExp", SYNTAX_BUILTIN}}; +static Keyword const syntax_keywords_javascript_S[6] = {{"Set", SYNTAX_BUILTIN},{"SharedArrayBuffer", SYNTAX_BUILTIN},{"String", SYNTAX_BUILTIN},{"Symbol", SYNTAX_BUILTIN},{"SyntaxError", SYNTAX_BUILTIN}}; +static Keyword const syntax_keywords_javascript_T[3] = {{"TypeError", SYNTAX_BUILTIN},{"TypedArray", SYNTAX_BUILTIN}}; +static Keyword const syntax_keywords_javascript_U[6] = {{"URIError", SYNTAX_BUILTIN},{"Uint16Array", SYNTAX_BUILTIN},{"Uint32Array", SYNTAX_BUILTIN},{"Uint8Array", SYNTAX_BUILTIN},{"Uint8ClampedArray", SYNTAX_BUILTIN}}; +static Keyword const syntax_keywords_javascript_W[5] = {{"WeakMap", SYNTAX_BUILTIN},{"WeakRef", SYNTAX_BUILTIN},{"WeakSet", SYNTAX_BUILTIN},{"WebAssembly", SYNTAX_BUILTIN}}; +static Keyword const syntax_keywords_javascript_a[2] = {{"await", SYNTAX_KEYWORD}}; +static Keyword const syntax_keywords_javascript_b[2] = {{"break", SYNTAX_KEYWORD}}; +static Keyword const syntax_keywords_javascript_c[6] = {{"case", SYNTAX_KEYWORD},{"catch", SYNTAX_KEYWORD},{"class", SYNTAX_KEYWORD},{"const", SYNTAX_KEYWORD},{"continue", SYNTAX_KEYWORD}}; +static Keyword const syntax_keywords_javascript_d[7] = {{"debugger", SYNTAX_KEYWORD},{"default", SYNTAX_KEYWORD},{"delete", SYNTAX_KEYWORD},{"do", SYNTAX_KEYWORD},{"decodeURI", SYNTAX_BUILTIN},{"decodeURIComponent", SYNTAX_BUILTIN}}; +static Keyword const syntax_keywords_javascript_e[7] = {{"else", SYNTAX_KEYWORD},{"export", SYNTAX_KEYWORD},{"extends", SYNTAX_KEYWORD},{"encodeURI", SYNTAX_BUILTIN},{"encodeURIComponent", SYNTAX_BUILTIN},{"eval", SYNTAX_BUILTIN}}; +static Keyword const syntax_keywords_javascript_f[5] = {{"finally", SYNTAX_KEYWORD},{"for", SYNTAX_KEYWORD},{"function", SYNTAX_KEYWORD},{"false", SYNTAX_CONSTANT}}; +static Keyword const syntax_keywords_javascript_g[2] = {{"globalThis", SYNTAX_BUILTIN}}; +static Keyword const syntax_keywords_javascript_i[7] = {{"if", SYNTAX_KEYWORD},{"import", SYNTAX_KEYWORD},{"in", SYNTAX_KEYWORD},{"instanceof", SYNTAX_KEYWORD},{"isFinite", SYNTAX_BUILTIN},{"isNaN", SYNTAX_BUILTIN}}; +static Keyword const syntax_keywords_javascript_l[2] = {{"let", SYNTAX_KEYWORD}}; +static Keyword const syntax_keywords_javascript_n[3] = {{"new", SYNTAX_KEYWORD},{"null", SYNTAX_BUILTIN}}; +static Keyword const syntax_keywords_javascript_p[3] = {{"parseFloat", SYNTAX_BUILTIN},{"parseInt", SYNTAX_BUILTIN}}; +static Keyword const syntax_keywords_javascript_r[2] = {{"return", SYNTAX_KEYWORD}}; +static Keyword const syntax_keywords_javascript_s[3] = {{"super", SYNTAX_KEYWORD},{"switch", SYNTAX_KEYWORD}}; +static Keyword const syntax_keywords_javascript_t[6] = {{"this", SYNTAX_KEYWORD},{"throw", SYNTAX_KEYWORD},{"try", SYNTAX_KEYWORD},{"typeof", SYNTAX_KEYWORD},{"true", SYNTAX_CONSTANT}}; +static Keyword const syntax_keywords_javascript_u[2] = {{"undefined", SYNTAX_BUILTIN}}; +static Keyword const syntax_keywords_javascript_v[3] = {{"var", SYNTAX_KEYWORD},{"void", SYNTAX_KEYWORD}}; +static Keyword const syntax_keywords_javascript_w[3] = {{"while", SYNTAX_KEYWORD},{"with", SYNTAX_KEYWORD}}; +static Keyword const syntax_keywords_javascript_y[2] = {{"yield", SYNTAX_KEYWORD}}; +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_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}}; +static Keyword const syntax_keywords_java_d[4] = {{"default", SYNTAX_KEYWORD},{"do", SYNTAX_KEYWORD},{"double", SYNTAX_KEYWORD}}; +static Keyword const syntax_keywords_java_e[4] = {{"else", SYNTAX_KEYWORD},{"enum", SYNTAX_KEYWORD},{"extends", SYNTAX_KEYWORD}}; +static Keyword const syntax_keywords_java_f[6] = {{"final", SYNTAX_KEYWORD},{"finally", SYNTAX_KEYWORD},{"float", SYNTAX_KEYWORD},{"for", SYNTAX_KEYWORD},{"false", SYNTAX_CONSTANT}}; +static Keyword const syntax_keywords_java_g[2] = {{"goto", SYNTAX_KEYWORD}}; +static Keyword const syntax_keywords_java_i[7] = {{"if", SYNTAX_KEYWORD},{"implements", SYNTAX_KEYWORD},{"import", SYNTAX_KEYWORD},{"instanceof", SYNTAX_KEYWORD},{"int", SYNTAX_KEYWORD},{"interface", SYNTAX_KEYWORD}}; +static Keyword const syntax_keywords_java_l[2] = {{"long", SYNTAX_KEYWORD}}; +static Keyword const syntax_keywords_java_n[4] = {{"native", SYNTAX_KEYWORD},{"new", SYNTAX_KEYWORD},{"null", SYNTAX_CONSTANT}}; +static Keyword const syntax_keywords_java_p[5] = {{"package", SYNTAX_KEYWORD},{"private", SYNTAX_KEYWORD},{"protected", SYNTAX_KEYWORD},{"public", SYNTAX_KEYWORD}}; +static Keyword const syntax_keywords_java_r[2] = {{"return", SYNTAX_KEYWORD}}; +static Keyword const syntax_keywords_java_s[7] = {{"short", SYNTAX_KEYWORD},{"static", SYNTAX_KEYWORD},{"strictfp", SYNTAX_KEYWORD},{"super", SYNTAX_KEYWORD},{"switch", SYNTAX_KEYWORD},{"synchronized", SYNTAX_KEYWORD}}; +static Keyword const syntax_keywords_java_t[7] = {{"this", SYNTAX_KEYWORD},{"throw", SYNTAX_KEYWORD},{"throws", SYNTAX_KEYWORD},{"transient", SYNTAX_KEYWORD},{"try", SYNTAX_KEYWORD},{"true", SYNTAX_CONSTANT}}; +static Keyword const syntax_keywords_java_v[4] = {{"var", SYNTAX_KEYWORD},{"void", SYNTAX_KEYWORD},{"volatile", SYNTAX_KEYWORD}}; +static Keyword const syntax_keywords_java_w[2] = {{"while", SYNTAX_KEYWORD}}; +static Keyword const *const syntax_all_keywords_java[] = { + ['a'] = syntax_keywords_java_a, ['b'] = syntax_keywords_java_b, ['c'] = syntax_keywords_java_c, ['d'] = syntax_keywords_java_d, ['e'] = syntax_keywords_java_e, ['f'] = syntax_keywords_java_f, ['g'] = syntax_keywords_java_g, ['i'] = syntax_keywords_java_i, ['l'] = syntax_keywords_java_l, ['n'] = syntax_keywords_java_n, ['p'] = syntax_keywords_java_p, ['r'] = syntax_keywords_java_r, ['s'] = syntax_keywords_java_s, ['t'] = syntax_keywords_java_t, ['v'] = syntax_keywords_java_v, ['w'] = syntax_keywords_java_w +}; + static Keyword const syntax_keywords_python_A[4] = {{"ArithmeticError", SYNTAX_BUILTIN},{"AssertionError", SYNTAX_BUILTIN},{"AttributeError", SYNTAX_BUILTIN}}; static Keyword const syntax_keywords_python_B[6] = {{"BaseException", SYNTAX_BUILTIN},{"BlockingIOError", SYNTAX_BUILTIN},{"BrokenPipeError", SYNTAX_BUILTIN},{"BufferError", SYNTAX_BUILTIN},{"BytesWarning", SYNTAX_BUILTIN}}; static Keyword const syntax_keywords_python_C[6] = {{"ChildProcessError", SYNTAX_BUILTIN},{"ConnectionAbortedError", SYNTAX_BUILTIN},{"ConnectionError", SYNTAX_BUILTIN},{"ConnectionRefusedError", SYNTAX_BUILTIN},{"ConnectionResetError", SYNTAX_BUILTIN}}; diff --git a/keywords.py b/keywords.py index 2413dd9..b7f7432 100755 --- a/keywords.py +++ b/keywords.py @@ -27,6 +27,13 @@ def output_keywords(file, keywords, language): file.write('\t'+', '.join(["['{}'] = syntax_keywords_{}_{}".format(c, language, c) for c in sorted(keywords.keys())]) + '\n') file.write('};\n\n') +def cant_overlap(*args): + for i in range(len(args)): + for j in range(i): + intersection = set(args[i]).intersection(args[j]) + if intersection: + raise ValueError("Argument {} intersects with {}: {}".format(i, j, intersection)) + constants_c = ['CHAR_BIT', 'CHAR_MAX', 'CHAR_MIN', 'DBL_DIG', 'DBL_EPSILON', 'DBL_HAS_SUBNORM', 'DBL_MANT_DIG', 'DBL_MAX', 'DBL_MAX_10_EXP', 'DBL_MAX_EXP', 'DBL_MIN', 'DBL_MIN_EXP', 'DBL_TRUE_MIN', 'DECIMAL_DIG', 'EXIT_FAILURE', 'EXIT_SUCCESS', 'FLT_DECIMAL_DIG', 'FLT_DIG', 'FLT_EVAL_METHOD', 'FLT_HAS_SUBNORM', 'FLT_MANT_DIG', 'FLT_MAX', 'FLT_MAX_10_EXP', 'FLT_MAX_EXP', @@ -151,12 +158,7 @@ keywords_cpp = [ 'xor', 'xor_eq', 'bool', 'wchar_t', ] -def cant_overlap(*args): - for i in range(len(args)): - for j in range(i): - intersection = set(args[i]).intersection(args[j]) - if intersection: - raise ValueError("Argument {} intersects with {}: {}".format(i, j, intersection)) + cant_overlap(keywords_c, keywords_cpp) keywords_rust = [ @@ -253,6 +255,51 @@ builtins_html = [] for attr in attributes_html: builtins_html.append(attr + '=') + +keywords_javascript = [ + 'break', 'case', 'catch', 'class', 'const', + 'continue', 'debugger', 'default', 'delete', + 'do', 'else', 'export', 'extends', 'finally', + 'for', 'function', 'if', 'import', 'in', + 'instanceof', 'new', 'return', 'super', + 'switch', 'this', 'throw', 'try', 'typeof', + 'var', 'void', 'while', 'with', 'yield', + 'let', 'await' +] + +constants_javascript = [ + 'true', 'false' +] + +builtins_javascript = [ + 'AggregateError','Array','ArrayBuffer','AsyncFunction','AsyncGenerator','AsyncGeneratorFunction', + 'Atomics','BigInt','BigInt64Array','BigUint64Array','Boolean','DataView','Date','decodeURI', + 'decodeURIComponent','encodeURI','encodeURIComponent','Error','eval','EvalError','FinalizationRegistry', + 'Float32Array','Float64Array','Function','Generator','GeneratorFunction','globalThis','Infinity', + 'Int16Array','Int32Array','Int8Array','InternalError','Intl','isFinite','isNaN','JSON','Map','Math', + 'NaN','Number','Object','parseFloat','parseInt','Promise','Proxy','RangeError','ReferenceError', + 'Reflect','RegExp','Set','SharedArrayBuffer','String','Symbol','SyntaxError','TypedArray', + 'TypeError','Uint16Array','Uint32Array','Uint8Array','Uint8ClampedArray','undefined', + 'URIError','WeakMap','WeakRef','WeakSet','WebAssembly', 'null' +] + +keywords_java = [ + 'abstract', 'continue', 'for', 'new', 'switch', + 'assert', 'default', 'goto', 'package', 'synchronized', + 'boolean', 'do', 'if', 'private', 'this', + 'break', 'double', 'implements', 'protected', 'throw', + 'byte', 'else', 'import', 'public', 'throws', + 'case', 'enum', 'instanceof', 'return', 'transient', + 'catch', 'extends', 'int', 'short', 'try', + 'char', 'final', 'interface', 'static', 'var', + 'class', 'finally', 'long', 'strictfp', 'void', + 'const', 'float', 'native', 'super', 'volatile', 'while' +] + +constants_java = [ + 'true', 'false', 'null' +] + file = open('keywords.h', 'w') file.write('''// keywords for all languages ted supports // This file was auto-generated by keywords.py @@ -268,6 +315,7 @@ def label(kwds, l): cant_overlap(keywords_c, constants_c, builtins_c) cant_overlap(keywords_rust, builtins_rust, constants_rust) cant_overlap(keywords_python, builtins_python) +cant_overlap(keywords_javascript, builtins_javascript, constants_javascript) c_things = label(keywords_c, SYNTAX_KEYWORD) + label(constants_c, SYNTAX_CONSTANT) + label(builtins_c, SYNTAX_BUILTIN) output_keywords(file, c_things, 'c') cpp_things = c_things + label(keywords_cpp, SYNTAX_KEYWORD) @@ -275,6 +323,9 @@ cpp_things.remove((SYNTAX_BUILTIN, 'bool')) cpp_things.remove((SYNTAX_BUILTIN, 'wchar_t')) 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_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') output_keywords(file, label(constants_config, SYNTAX_CONSTANT), 'config') @@ -20,6 +20,8 @@ char const *language_comment_start(Language l) { return "/* "; case LANG_RUST: case LANG_CPP: + case LANG_JAVASCRIPT: + case LANG_JAVA: return "// "; case LANG_CONFIG: case LANG_PYTHON: @@ -497,12 +499,12 @@ static void syntax_highlight_python(SyntaxState *state, char32_t const *line, u3 case '\'': case '"': { bool dbl_quoted = c == '"'; - bool is_triple = i < line_len - 2 && + bool is_triple = i+2 < line_len && line[i+1] == c && line[i+2] == c; if (in_string) { if (!string_is_multiline || is_triple) { - // end of string if (string_is_dbl_quoted == dbl_quoted && backslashes % 2 == 0) { + // end of string in_string = false; if (char_types) { char_types[i] = SYNTAX_STRING; @@ -1046,6 +1048,249 @@ static void syntax_highlight_config(SyntaxState *state, char32_t const *line, u3 } } +static void syntax_highlight_javascript(SyntaxState *state, char32_t const *line, u32 line_len, SyntaxCharType *char_types) { + (void)state; + bool string_is_template = (*state & SYNTAX_STATE_JAVASCRIPT_TEMPLATE_STRING) != 0; + bool in_multiline_comment = (*state & SYNTAX_STATE_JAVASCRIPT_MULTILINE_COMMENT) != 0; + bool string_is_dbl_quoted = false; + bool in_number = false; + bool in_string = string_is_template; + uint backslashes = 0; + + for (u32 i = 0; i < line_len; ++i) { + char32_t c = line[i]; + bool dealt_with = false; + switch (c) { + case '/': + if (!in_string) { + if (i > 0) { + if (line[i-1] == '*') { + // end of multi line comment + in_multiline_comment = false; + if (char_types) char_types[i] = SYNTAX_COMMENT; + dealt_with = true; + } + } + if (!dealt_with && i+1 < line_len) { + if (line[i+1] == '/') { + // single line comment + if (char_types) memset(&char_types[i], SYNTAX_COMMENT, line_len - i); + i = line_len - 1; + dealt_with = true; + } else if (line[i+1] == '*') { + // multi line comment + in_multiline_comment = true; + if (char_types) char_types[i] = SYNTAX_COMMENT; + dealt_with = true; + } + } + } + break; + case '\'': + case '"': + case '`': { + if (!in_multiline_comment) { + bool dbl_quoted = c == '"'; + bool template = c == '`'; + if (in_string) { + if (string_is_dbl_quoted == dbl_quoted && string_is_template == template && backslashes % 2 == 0) { + // end of string + in_string = false; + if (char_types) char_types[i] = SYNTAX_STRING; + dealt_with = true; + } + } else { + // start of string + string_is_dbl_quoted = dbl_quoted; + string_is_template = template; + in_string = true; + } + } + } break; + case ANY_DIGIT: + if (char_types && !in_string && !in_number && !in_multiline_comment) { + 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; + case '\\': + ++backslashes; + break; + default: + if ((i && is32_ident(line[i - 1])) || !is32_ident(c)) + break; // can't be a keyword on its own. + + if (char_types && !in_string && !in_number && !in_multiline_comment) { + u32 keyword_len = syntax_keyword_len(LANG_JAVASCRIPT, line, i, line_len); + Keyword const *keyword = syntax_keyword_lookup(syntax_all_keywords_javascript, arr_count(syntax_all_keywords_javascript), + &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_number) + type = SYNTAX_CONSTANT; + char_types[i] = type; + } + } + *state = 0; + if (in_string && string_is_template) + *state |= SYNTAX_STATE_JAVASCRIPT_TEMPLATE_STRING; + if (in_multiline_comment) + *state |= SYNTAX_STATE_JAVASCRIPT_MULTILINE_COMMENT; +} + +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_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 && 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; + } + 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_JAVA, line, i, line_len); + Keyword const *keyword = syntax_keyword_lookup(syntax_all_keywords_java, arr_count(syntax_all_keywords_java), + &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_JAVA_MULTILINE_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. @@ -1080,6 +1325,12 @@ void syntax_highlight(SyntaxState *state, Language lang, char32_t const *line, u case LANG_CONFIG: syntax_highlight_config(state, line, line_len, char_types); break; + case LANG_JAVASCRIPT: + syntax_highlight_javascript(state, line, line_len, char_types); + break; + case LANG_JAVA: + syntax_highlight_java(state, line, line_len, char_types); + break; case LANG_COUNT: assert(0); break; } } @@ -227,3 +227,5 @@ Tex = .tex Markdown = .md HTML = .html, .php, .xml, .xhtml Config = .cfg +Javascript = .js +Java = .java @@ -41,6 +41,15 @@ enum { SYNTAX_STATE_HTML_COMMENT = 0x01u }; +enum { + SYNTAX_STATE_JAVASCRIPT_TEMPLATE_STRING = 0x01u, + SYNTAX_STATE_JAVASCRIPT_MULTILINE_COMMENT = 0x02u, +}; + +enum { + SYNTAX_STATE_JAVA_MULTILINE_COMMENT = 0x01u +}; + typedef u8 SyntaxState; // If you are adding new languages, DO NOT change the constant values @@ -55,6 +64,8 @@ ENUM_U16 { LANG_MARKDOWN = 6, LANG_HTML = 7, LANG_CONFIG = 8, // .cfg files, e.g. ted.cfg + LANG_JAVASCRIPT = 9, + LANG_JAVA = 10, LANG_COUNT } ENUM_U16_END(Language); @@ -73,6 +84,8 @@ static LanguageName const language_names[] = { {LANG_MARKDOWN, "Markdown"}, {LANG_HTML, "HTML"}, {LANG_CONFIG, "Config"}, + {LANG_JAVASCRIPT, "Javascript"}, + {LANG_JAVA, "Java"}, }; static_assert_if_possible(arr_count(language_names) == LANG_COUNT) diff --git a/test.java b/test.java new file mode 100644 index 0000000..7183aed --- /dev/null +++ b/test.java @@ -0,0 +1,13 @@ +class Test { + public static void main(String[] args) { + /* hello! + everyone + this + is + a test*/ + String x = "hello, world!\""; + System.out.println(x + + "yes\n\\"+ + x); + } +} @@ -0,0 +1,12 @@ +const c = `template +strings +yay +`; +let y = 34 * parseInt('hello\\\'\\"') + "'\\"; +/* +comm +ent + +*/a//yes +/* +*//no |