diff --git a/src/code.c b/src/code.c index fa31e14..a6436be 100644 --- a/src/code.c +++ b/src/code.c @@ -23,6 +23,8 @@ #include "code.h" #include "history.h" +#include + #define TEXT_CURSOR_DELAY (TIC_FRAMERATE / 2) #define TEXT_CURSOR_BLINK_PERIOD TIC_FRAMERATE #define TEXT_BUFFER_WIDTH STUDIO_TEXT_BUFFER_WIDTH @@ -185,258 +187,258 @@ static void updateEditor(Code* code) } } -static bool isLetter(char symbol) {return (symbol >= 'A' && symbol <= 'Z') || (symbol >= 'a' && symbol <= 'z') || (symbol == '_');} -static bool isNumber(char symbol) {return (symbol >= '0' && symbol <= '9');} -static bool isWord(char symbol) {return isLetter(symbol) || isNumber(symbol);} -static bool isDot(char symbol) {return (symbol == '.');} +static inline bool islineend(char c) {return c == '\n' || c == '\0';} +static inline bool isalpha_(char c) {return isalpha(c) || c == '_';} +static inline bool isalnum_(char c) {return isalnum(c) || c == '_';} -static void highlightStrings(Code* code, const char* text, u8* color, char separator) +typedef struct { - char* start = SDL_strchr(text, separator); + const char* blockCommentStart; + const char* blockCommentEnd; + const char* singleCommentStart; - if(start) + const char* const * keywords; + s32 keywordsCount; +} SyntaxConfig; + +static const char* const LuaKeywords [] = +{ + "and", "break", "do", "else", "elseif", + "end", "false", "for", "function", "goto", "if", + "in", "local", "nil", "not", "or", "repeat", + "return", "then", "true", "until", "while" +}; + +static const SyntaxConfig LuaSyntaxConfig = +{ + .blockCommentStart = "--[[", + .blockCommentEnd = "]]", + .singleCommentStart = "--", + .keywords = LuaKeywords, + .keywordsCount = COUNT_OF(LuaKeywords), +}; + +static const char* const MoonKeywords [] = +{ + "false", "true", "nil", "return", + "break", "continue", "for", "while", + "if", "else", "elseif", "unless", "switch", + "when", "and", "or", "in", "do", + "not", "super", "try", "catch", + "with", "export", "import", "then", + "from", "class", "extends", "new" +}; + +static const SyntaxConfig MoonSyntaxConfig = +{ + .blockCommentStart = "--[[", + .blockCommentEnd = "]]", + .singleCommentStart = "--", + .keywords = MoonKeywords, + .keywordsCount = COUNT_OF(MoonKeywords), +}; + +static const char* const JsKeywords [] = +{ + "break", "do", "instanceof", "typeof", "case", "else", "new", + "var", "catch", "finally", "return", "void", "continue", "for", + "switch", "while", "debugger", "function", "this", "with", + "default", "if", "throw", "delete", "in", "try", "const" +}; + +static const SyntaxConfig JsSyntaxConfig = +{ + .blockCommentStart = "/*", + .blockCommentEnd = "*/", + .singleCommentStart = "//", + .keywords = JsKeywords, + .keywordsCount = COUNT_OF(JsKeywords), +}; + +static void parse(const char* start, u8* color, const SyntaxConfig* config) +{ + const char* ptr = start; + + const char* blockCommentStart = NULL; + const char* blockStringStart = NULL; + const char* singleCommentStart = NULL; + const char* wordStart = NULL; + const char* numberStart = NULL; + + while(true) { - char* end = SDL_strchr(start + 1, separator); + char c = *ptr; - if(end) + if(blockCommentStart) { - end++; - u8* colorPtr = color + (start - text); + const char* end = strstr(ptr, config->blockCommentEnd); - if(*colorPtr != getConfig()->theme.code.comment) - memset(colorPtr, getConfig()->theme.code.string, end - start); - - highlightStrings(code, end, color + (end - text), separator); + ptr = end ? end + strlen(config->blockCommentEnd) : blockCommentStart + strlen(blockCommentStart); + memset(color + (blockCommentStart - start), getConfig()->theme.code.comment, ptr - blockCommentStart); + blockCommentStart = NULL; + continue; } - } -} - -static void highlightNumbers(Code* code, u8* color) -{ - const char* text = code->src; - const char* pointer = text; - - while(*pointer) - { - char symbol = *pointer; - - if(isLetter(symbol)) - while(symbol && (isLetter(symbol) || isNumber(symbol) || isDot(symbol))) - symbol = *++pointer; - - const char* start = pointer; - while(symbol && (isNumber(symbol) || isDot(symbol))) symbol = *++pointer; - - if(!isLetter(symbol)) memset(color + (start - text), getConfig()->theme.code.number, pointer - start); - - pointer++; - } -} - -static void highlightWords(const char* text, u8* color, const char* const strings[], s32 count, u8 wordColor) -{ - const char* pointer = text; - - while(*pointer) - { - char symbol = *pointer; - - const char* start = pointer; - while(symbol && (isLetter(symbol) || isNumber(symbol))) - symbol = *++pointer; - - size_t size = pointer - start; - - if(size) + else if(blockStringStart) { - for(s32 i = 0; i < count; i++) + const char* blockStart = blockStringStart+1; + + while(true) { - const char* keyword = strings[i]; - if(size == strlen(keyword) && memcmp(start, keyword, size) == 0) + const char* pos = strchr(blockStart, *blockStringStart); + + if(pos) { - memset(color + (start - text), wordColor, size); + if(*(pos-1) == '\\' && *(pos-2) != '\\') + { + blockStart = pos + 1; + } + else + { + ptr = pos + 1; + break; + } + } + else + { + ptr = blockStringStart + strlen(blockStringStart); break; } } + + memset(color + (blockStringStart - start), getConfig()->theme.code.string, ptr - blockStringStart); + blockStringStart = NULL; + continue; + } + else if(singleCommentStart) + { + while(!islineend(*ptr))ptr++; + + memset(color + (singleCommentStart - start), getConfig()->theme.code.comment, ptr - singleCommentStart); + singleCommentStart = NULL; + continue; + } + else if(wordStart) + { + while(!islineend(*ptr) && isalnum_(*ptr)) ptr++; + + bool keyword = false; + { + for(s32 i = 0; i < config->keywordsCount; i++) + if(memcmp(wordStart, config->keywords[i], strlen(config->keywords[i])) == 0) + { + memset(color + (wordStart - start), getConfig()->theme.code.keyword, ptr - wordStart); + keyword = true; + break; + } + } + + if(!keyword) + { + static const char* const Api[] = API_KEYWORDS; + + for(s32 i = 0; i < COUNT_OF(Api); i++) + if(memcmp(wordStart, Api[i], strlen(Api[i])) == 0) + { + memset(color + (wordStart - start), getConfig()->theme.code.api, ptr - wordStart); + break; + } + } + + wordStart = NULL; + continue; + } + else if(numberStart) + { + while(!islineend(*ptr)) + { + char c = *ptr; + + if(isdigit(c)) ptr++; + else if(numberStart[0] == '0' + && (numberStart[1] == 'x' || numberStart[1] == 'X') + && isxdigit(numberStart[2])) + { + if((ptr - numberStart < 2) || (ptr - numberStart >= 2 && isxdigit(c))) ptr++; + else break; + } + else if(c == '.' || c == 'e' || c == 'E') + { + if(isdigit(ptr[1])) ptr++; + else break; + } + else break; + } + + memset(color + (numberStart - start), getConfig()->theme.code.number, ptr - numberStart); + numberStart = NULL; + continue; + } + else + { + s32 blockCommentStartSize = strlen(config->blockCommentStart); + if(c == config->blockCommentStart[0] && memcmp(ptr, config->blockCommentStart, blockCommentStartSize) == 0) + { + blockCommentStart = ptr; + ptr += blockCommentStartSize; + continue; + } + else + { + if(c == '"' || c == '\'') + { + blockStringStart = ptr; + ptr++; + continue; + } + else + { + s32 singleCommentStartSize = strlen(config->singleCommentStart); + + if(c == config->singleCommentStart[0] && memcmp(ptr, config->singleCommentStart, singleCommentStartSize) == 0) + { + singleCommentStart = ptr; + ptr += singleCommentStartSize; + continue; + } + else + { + if(isalpha_(c)) + { + wordStart = ptr; + ptr++; + continue; + } + else + { + if(isdigit(c) || (c == '.' && isdigit(ptr[1]))) + { + numberStart = ptr; + ptr++; + continue; + } + else if(ispunct(c)) + color[ptr - start] = getConfig()->theme.code.sign; + } + } + } + } } - pointer++; + if(!c) break; + + ptr++; } - -} - -static void highlightMoonKeywords(Code* code, u8* color) -{ - const char* text = code->src; - - static const char* const MoonKeywords [] = - { - "false", "true", "nil", "return", - "break", "continue", "for", "while", - "if", "else", "elseif", "unless", "switch", - "when", "and", "or", "in", "do", - "not", "super", "try", "catch", - "with", "export", "import", "then", - "from", "class", "extends", "new" - }; - - highlightWords(text, color, MoonKeywords, COUNT_OF(MoonKeywords), getConfig()->theme.code.keyword); -} - -static void highlightLuaKeywords(Code* code, u8* color) -{ - const char* text = code->src; - - static const char* const LuaKeywords [] = - { - "and", "break", "do", "else", "elseif", - "end", "false", "for", "function", "goto", "if", - "in", "local", "nil", "not", "or", "repeat", - "return", "then", "true", "until", "while" - }; - - highlightWords(text, color, LuaKeywords, COUNT_OF(LuaKeywords), getConfig()->theme.code.keyword); -} - -static void highlightJsKeywords(Code* code, u8* color) -{ - const char* text = code->src; - - static const char* const JsKeywords [] = - { - "break", "do", "instanceof", "typeof", "case", "else", "new", - "var", "catch", "finally", "return", "void", "continue", "for", - "switch", "while", "debugger", "function", "this", "with", - "default", "if", "throw", "delete", "in", "try", "const" - }; - - highlightWords(text, color, JsKeywords, COUNT_OF(JsKeywords), getConfig()->theme.code.keyword); -} - -static void highlightApi(Code* code, u8* color) -{ - static const char* const ApiKeywords[] = API_KEYWORDS; - - const char* text = code->src; - highlightWords(text, color, ApiKeywords, COUNT_OF(ApiKeywords), getConfig()->theme.code.api); -} - -static void highlightNonChars(Code* code, u8* color) -{ - const char* text = code->src; - - while(*text) - { - if(*text <= 32) - *color = getConfig()->theme.code.other; - - text++; - color++; - } -} - -static void highlightSigns(Code* code, u8* color) -{ - const char* text = code->src; - - static const char* const LuaSigns [] = - { - "+", "-", "*", "/", "%", "^", "#", - "&", "~", "|", "<<", ">>", "//", - "==", "~=", "<=", ">=", "<", ">", "=", - "(", ")", "{", "}", "[", "]", "::", - ";", ":", ",", ".", "..", "...", - }; - - for(s32 i = 0; i < COUNT_OF(LuaSigns); i++) - { - const char* sign = LuaSigns[i]; - const char* start = text; - - while((start = strstr(start, sign))) - { - size_t size = strlen(sign); - memset(color + (start - text), getConfig()->theme.code.sign, size); - start += size; - } - } -} - -static void highlightCommentsBase(Code* code, u8* color, const char* pattern1, const char* pattern2, s32 extraSize) -{ - const char* text = code->src; - const char* pointer = text; - - while(*pointer) - { - char* start = strstr(pointer, pattern1); - - if(start) - { - char* end = strstr(start + strlen(pattern1), pattern2); - - if(!end) end = start + strlen(start); - - if(end) - { - end += extraSize; - - memset(color + (start - text), getConfig()->theme.code.comment, end - start); - pointer = end; - } - } - - pointer++; - } -} - -static void highlightComments(Code* code, u8* color) -{ - highlightCommentsBase(code, color, "--", "\n", 0); - highlightCommentsBase(code, color, "--[[", "]]", 2); -} - -static void highlightJsComments(Code* code, u8* color) -{ - highlightCommentsBase(code, color, "//", "\n", 0); - highlightCommentsBase(code, color, "/*", "*/", 2); } static void parseSyntaxColor(Code* code) { memset(code->colorBuffer, getConfig()->theme.code.var, sizeof(code->colorBuffer)); - u8* color = code->colorBuffer; - switch(code->tic->api.get_script(code->tic)) { - case tic_script_moon: - highlightNonChars(code, color); - highlightMoonKeywords(code, color); - highlightApi(code, color); - highlightNumbers(code, color); - highlightSigns(code, color); - highlightCommentsBase(code, color, "--", "\n", 0); - highlightStrings(code, code->src, color, '"'); - break; - case tic_script_lua: - highlightNonChars(code, color); - highlightLuaKeywords(code, color); - highlightApi(code, color); - highlightNumbers(code, color); - highlightSigns(code, color); - highlightComments(code, color); - highlightStrings(code, code->src, color, '"'); - break; - case tic_script_js: - highlightNonChars(code, color); - highlightJsKeywords(code, color); - highlightApi(code, color); - highlightNumbers(code, color); - highlightSigns(code, color); - highlightJsComments(code, color); - highlightStrings(code, code->src, color, '"'); - break; + case tic_script_moon: parse(code->src, code->colorBuffer, &MoonSyntaxConfig); break; + case tic_script_lua: parse(code->src, code->colorBuffer, &LuaSyntaxConfig); break; + case tic_script_js: parse(code->src, code->colorBuffer, &JsSyntaxConfig); break; } } @@ -584,8 +586,8 @@ static void leftWord(Code* code) if(pos > start) { - if(isWord(*pos)) while(pos > start && isWord(*(pos-1))) pos--; - else while(pos > start && !isWord(*(pos-1))) pos--; + if(isalnum_(*pos)) while(pos > start && isalnum_(*(pos-1))) pos--; + else while(pos > start && !isalnum_(*(pos-1))) pos--; code->cursor.position = pos; @@ -600,8 +602,8 @@ static void rightWord(Code* code) if(pos < end) { - if(isWord(*pos)) while(pos < end && isWord(*pos)) pos++; - else while(pos < end && !isWord(*pos)) pos++; + if(isalnum_(*pos)) while(pos < end && isalnum_(*pos)) pos++; + else while(pos < end && !isalnum_(*pos)) pos++; code->cursor.position = pos; updateColumn(code); @@ -940,7 +942,7 @@ static char* getFuncName(const char* start, char* buffer) { char sym = *ptr; - if(isLetter(sym) || isNumber(sym) || sym == ':'){} + if(isalpha_(sym) || isdigit(sym) || sym == ':'){} else if(sym == '(') break; else return NULL; @@ -1025,11 +1027,11 @@ static void setMoonscriptOutlineMode(Code* code) char* endPtr = ptr; ptr += sizeof FuncString - 1; - while(endPtr >= code->src && !isLetter(*endPtr) && !isNumber(*endPtr)) endPtr--; + while(endPtr >= code->src && !isalpha_(*endPtr) && !isdigit(*endPtr)) endPtr--; char* start = endPtr; - for (const char* val = start-1; val >= code->src && (isLetter(*val) || isNumber(*val)); val--, start--); + for (const char* val = start-1; val >= code->src && (isalpha_(*val) || isdigit(*val)); val--, start--); if(start >= code->src) {