diff options
| -rw-r--r-- | lexers/LexLPeg.cxx | 1395 |
1 files changed, 701 insertions, 694 deletions
diff --git a/lexers/LexLPeg.cxx b/lexers/LexLPeg.cxx index e79a7f83d..5f4b95b01 100644 --- a/lexers/LexLPeg.cxx +++ b/lexers/LexLPeg.cxx @@ -36,12 +36,12 @@ using namespace Scintilla; #if LUA_VERSION_NUM < 502 #define luaL_requiref(l, s, f, _) \ - (lua_pushcfunction(l, f), lua_pushstring(l, s), lua_call(l, 1, 1)) + (lua_pushcfunction(l, f), lua_pushstring(l, s), lua_call(l, 1, 1)) #define lua_rawlen lua_objlen #define LUA_OK 0 #define lua_rawgetp(l, i, p) (lua_pushlightuserdata(l, p), lua_rawget(l, i)) #define lua_rawsetp(l, i, p) \ - (lua_pushlightuserdata(l, p), lua_insert(l, -2), lua_rawset(l, i)) + (lua_pushlightuserdata(l, p), lua_insert(l, -2), lua_rawset(l, i)) #endif #if LUA_VERSION_NUM < 503 #define lua_getfield(l, i, k) (lua_getfield(l, i, k), lua_type(l, -1)) @@ -50,109 +50,107 @@ using namespace Scintilla; /** lexer.property[key] metamethod. */ static int lexer_property_index(lua_State *L) { - const char *property = lua_tostring(L, lua_upvalueindex(1)); - lua_getfield(L, LUA_REGISTRYINDEX, "sci_lexer_lpeg"); - lua_rawgetp(L, LUA_REGISTRYINDEX, lua_touserdata(L, -1)); - lua_getfield(L, -1, "_PROPS"); - auto props = static_cast<PropSetSimple *>(lua_touserdata(L, -1)); - lua_getfield(L, -2, "_BUFFER"); - auto buffer = static_cast<IDocument *>(lua_touserdata(L, -1)); - if (strcmp(property, "fold_level") == 0) { - luaL_argcheck(L, buffer, 1, "must be lexing or folding"); - lua_pushinteger(L, buffer->GetLevel(luaL_checkinteger(L, 2))); - } else if (strcmp(property, "indent_amount") == 0) { - luaL_argcheck(L, buffer, 1, "must be lexing or folding"); - lua_pushinteger(L, buffer->GetLineIndentation(luaL_checkinteger(L, 2))); - } else if (strcmp(property, "property") == 0) { - lua_pushstring(L, props->Get(luaL_checkstring(L, 2))); - } else if (strcmp(property, "property_int") == 0) { - lua_pushstring(L, props->Get(luaL_checkstring(L, 2))); - lua_pushinteger(L, lua_tointeger(L, -1)); - } else if (strcmp(property, "style_at") == 0) { - luaL_argcheck(L, buffer, 1, "must be lexing or folding"); - int style = buffer->StyleAt(luaL_checkinteger(L, 2) - 1); - lua_getfield(L, 4, "_TOKENSTYLES"); - lua_pushnil(L); - while (lua_next(L, -2)) { - if (luaL_checkinteger(L, -1) == style) break; - lua_pop(L, 1); // value - } - lua_pop(L, 1); // style_num, leaving name on top - } else if (strcmp(property, "line_state") == 0) { - luaL_argcheck(L, buffer, 1, "must be lexing or folding"); - lua_pushinteger(L, buffer->GetLineState(luaL_checkinteger(L, 2))); - } - return 1; + const char *property = lua_tostring(L, lua_upvalueindex(1)); + lua_getfield(L, LUA_REGISTRYINDEX, "sci_lexer_lpeg"); + lua_rawgetp(L, LUA_REGISTRYINDEX, lua_touserdata(L, -1)); + lua_getfield(L, -1, "_PROPS"); + auto props = static_cast<PropSetSimple *>(lua_touserdata(L, -1)); + lua_getfield(L, -2, "_BUFFER"); + auto buffer = static_cast<IDocument *>(lua_touserdata(L, -1)); + if (strcmp(property, "fold_level") == 0) { + luaL_argcheck(L, buffer, 1, "must be lexing or folding"); + lua_pushinteger(L, buffer->GetLevel(luaL_checkinteger(L, 2))); + } else if (strcmp(property, "indent_amount") == 0) { + luaL_argcheck(L, buffer, 1, "must be lexing or folding"); + lua_pushinteger(L, buffer->GetLineIndentation(luaL_checkinteger(L, 2))); + } else if (strcmp(property, "property") == 0) { + lua_pushstring(L, props->Get(luaL_checkstring(L, 2))); + } else if (strcmp(property, "property_int") == 0) { + lua_pushstring(L, props->Get(luaL_checkstring(L, 2))); + lua_pushinteger(L, lua_tointeger(L, -1)); + } else if (strcmp(property, "style_at") == 0) { + luaL_argcheck(L, buffer, 1, "must be lexing or folding"); + int style = buffer->StyleAt(luaL_checkinteger(L, 2) - 1); + lua_getfield(L, 4, "_TOKENSTYLES"); + lua_pushnil(L); + while (lua_next(L, -2)) { + if (luaL_checkinteger(L, -1) == style) break; + lua_pop(L, 1); // value + } + lua_pop(L, 1); // style_num, leaving name on top + } else if (strcmp(property, "line_state") == 0) { + luaL_argcheck(L, buffer, 1, "must be lexing or folding"); + lua_pushinteger(L, buffer->GetLineState(luaL_checkinteger(L, 2))); + } + return 1; } /** lexer.property[key] = value metamethod. */ static int lexer_property_newindex(lua_State *L) { - const char *property = lua_tostring(L, lua_upvalueindex(1)); - luaL_argcheck(L, strcmp(property, "fold_level") != 0 && - strcmp(property, "indent_amount") != 0 && - strcmp(property, "property_int") != 0 && - strcmp(property, "style_at") != 0 && - strcmp(property, "line_from_position") != 0, 3, - "read-only property"); - lua_getfield(L, LUA_REGISTRYINDEX, "sci_lexer_lpeg"); - lua_rawgetp(L, LUA_REGISTRYINDEX, lua_touserdata(L, -1)); - if (strcmp(property, "property") == 0) { - lua_getfield(L, -1, "_PROPS"); - auto props = static_cast<PropSetSimple *>(lua_touserdata(L, -1)); - props->Set(luaL_checkstring(L, 2), luaL_checkstring(L, 3), lua_rawlen(L, 2), - lua_rawlen(L, 3)); - } else if (strcmp(property, "line_state") == 0) { - luaL_argcheck(L, lua_getfield(L, -1, "_BUFFER"), 1, - "must be lexing or folding"); - auto buffer = static_cast<IDocument *>(lua_touserdata(L, -1)); - buffer->SetLineState(luaL_checkinteger(L, 2), luaL_checkinteger(L, 3)); - } - return 0; + const char *property = lua_tostring(L, lua_upvalueindex(1)); + luaL_argcheck( + L, strcmp(property, "fold_level") != 0 && + strcmp(property, "indent_amount") != 0 && + strcmp(property, "property_int") != 0 && + strcmp(property, "style_at") != 0 && + strcmp(property, "line_from_position") != 0, 3, "read-only property"); + lua_getfield(L, LUA_REGISTRYINDEX, "sci_lexer_lpeg"); + lua_rawgetp(L, LUA_REGISTRYINDEX, lua_touserdata(L, -1)); + if (strcmp(property, "property") == 0) { + lua_getfield(L, -1, "_PROPS"); + auto props = static_cast<PropSetSimple *>(lua_touserdata(L, -1)); + props->Set( + luaL_checkstring(L, 2), luaL_checkstring(L, 3), lua_rawlen(L, 2), + lua_rawlen(L, 3)); + } else if (strcmp(property, "line_state") == 0) { + luaL_argcheck( + L, lua_getfield(L, -1, "_BUFFER"), 1, "must be lexing or folding"); + auto buffer = static_cast<IDocument *>(lua_touserdata(L, -1)); + buffer->SetLineState(luaL_checkinteger(L, 2), luaL_checkinteger(L, 3)); + } + return 0; } /** The lexer's `line_from_position` Lua function. */ static int line_from_position(lua_State *L) { - auto buffer = static_cast<IDocument *>(lua_touserdata(L, - lua_upvalueindex(1))); - lua_pushinteger(L, buffer->LineFromPosition(luaL_checkinteger(L, 1) - 1)); - return 1; + auto buffer = static_cast<IDocument *>( + lua_touserdata(L, lua_upvalueindex(1))); + lua_pushinteger(L, buffer->LineFromPosition(luaL_checkinteger(L, 1) - 1)); + return 1; } /** lexer.property metamethod. */ static int lexer_index(lua_State *L) { - const char *key = lua_tostring(L, 2); - if (strcmp(key, "fold_level") == 0 || strcmp(key, "indent_amount") == 0 || - strcmp(key, "property") == 0 || strcmp(key, "property_int") == 0 || - strcmp(key, "style_at") == 0 || strcmp(key, "line_state") == 0) { - lua_createtable(L, 0, 0); - lua_createtable(L, 0, 2); - lua_pushvalue(L, 2), lua_pushcclosure(L, lexer_property_index, 1); - lua_setfield(L, -2, "__index"); - lua_pushvalue(L, 2), lua_pushcclosure(L, lexer_property_newindex, 1); - lua_setfield(L, -2, "__newindex"); - lua_setmetatable(L, -2); - } else if (strcmp(key, "line_from_position") == 0) { - lua_getfield(L, LUA_REGISTRYINDEX, "sci_lexer_lpeg"); - lua_rawgetp(L, LUA_REGISTRYINDEX, lua_touserdata(L, -1)); - luaL_argcheck(L, lua_getfield(L, -1, "_BUFFER"), 2, - "must be lexing or folding"); - lua_pushcclosure(L, line_from_position, 1); - } else lua_rawget(L, 1); - return 1; + const char *key = lua_tostring(L, 2); + if (strcmp(key, "fold_level") == 0 || strcmp(key, "indent_amount") == 0 || + strcmp(key, "property") == 0 || strcmp(key, "property_int") == 0 || + strcmp(key, "style_at") == 0 || strcmp(key, "line_state") == 0) { + lua_createtable(L, 0, 0); + lua_createtable(L, 0, 2); + lua_pushvalue(L, 2), lua_pushcclosure(L, lexer_property_index, 1); + lua_setfield(L, -2, "__index"); + lua_pushvalue(L, 2), lua_pushcclosure(L, lexer_property_newindex, 1); + lua_setfield(L, -2, "__newindex"); + lua_setmetatable(L, -2); + } else if (strcmp(key, "line_from_position") == 0) { + lua_getfield(L, LUA_REGISTRYINDEX, "sci_lexer_lpeg"); + lua_rawgetp(L, LUA_REGISTRYINDEX, lua_touserdata(L, -1)); + luaL_argcheck( + L, lua_getfield(L, -1, "_BUFFER"), 2, "must be lexing or folding"); + lua_pushcclosure(L, line_from_position, 1); + } else lua_rawget(L, 1); + return 1; } /** lexer.property = value metamethod. */ static int lexer_newindex(lua_State *L) { - const char *key = lua_tostring(L, 2); - luaL_argcheck(L, strcmp(key, "fold_level") != 0 && - strcmp(key, "indent_amount") != 0 && - strcmp(key, "property") != 0 && - strcmp(key, "property_int") != 0 && - strcmp(key, "style_at") != 0 && - strcmp(key, "line_state") != 0 && - strcmp(key, "line_from_position") != 0, 3, - "read-only property"); - return (lua_rawset(L, 1), 0); + const char *key = lua_tostring(L, 2); + luaL_argcheck( + L, strcmp(key, "fold_level") != 0 && strcmp(key, "indent_amount") != 0 && + strcmp(key, "property") != 0 && strcmp(key, "property_int") != 0 && + strcmp(key, "style_at") != 0 && strcmp(key, "line_state") != 0 && + strcmp(key, "line_from_position") != 0, 3, "read-only property"); + return (lua_rawset(L, 1), 0); } /** @@ -162,635 +160,644 @@ static int lexer_newindex(lua_State *L) { * @param L The Lua State. */ static void expand_property(lua_State *L) { - //int orig_stack_top = lua_gettop(L); - lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"), lua_getfield(L, -1, "lexer"); - lua_getfield(L, -1, "property_expanded"); - lua_pushvalue(L, -4), lua_gettable(L, -2), lua_replace(L, -5); - lua_pop(L, 3); // property_expanded, lexer, _LOADED - //assert(lua_gettop(L) == orig_stack_top); + //int orig_stack_top = lua_gettop(L); + lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"), lua_getfield(L, -1, "lexer"); + lua_getfield(L, -1, "property_expanded"); + lua_pushvalue(L, -4), lua_gettable(L, -2), lua_replace(L, -5); + lua_pop(L, 3); // property_expanded, lexer, _LOADED + //assert(lua_gettop(L) == orig_stack_top); } /** The LPeg Scintilla lexer. */ class LexerLPeg : public ILexer { - /** - * The lexer's Lua state. - * It is cleared each time the lexer language changes unless `own_lua` is - * `true`. - */ - lua_State *L; - /** - * The flag indicating whether or not the Lua State is owned by the lexer. - */ - bool own_lua = true; - /** - * The set of properties for the lexer. - * The `lexer.name`, `lexer.lpeg.home`, and `lexer.lpeg.color.theme` - * properties must be defined before running the lexer. - */ - PropSetSimple props; - /** The function to send Scintilla messages with. */ - SciFnDirect SS = nullptr; - /** The Scintilla object the lexer belongs to. */ - sptr_t sci = 0; - /** - * The flag indicating whether or not the lexer needs to be re-initialized. - * Re-initialization is required after the lexer language changes. - */ - bool reinit = true; - /** - * The flag indicating whether or not the lexer language has embedded lexers. - */ - bool multilang = false; - /** - * The list of style numbers considered to be whitespace styles. - * This is used in multi-language lexers when backtracking to whitespace to - * determine which lexer grammar to use. - */ - bool ws[STYLE_MAX + 1]; - - /** - * Logs the given error message or a Lua error message, prints it, and clears - * the stack. - * Error messages are logged to the "lexer.lpeg.error" property. - * @param L The Lua State. - * @param str The error message to log and print. If `nullptr`, logs and - * prints the Lua error message at the top of the stack. - */ - void log_error(lua_State *L, const char *str = nullptr) { - const char *key = "lexer.lpeg.error"; - const char *value = str ? str : lua_tostring(L, -1); - props.Set(key, value, strlen(key), strlen(value)); - fprintf(stderr, "Lua Error: %s.\n", value); - lua_settop(L, 0); - } - - /** - * Parses the given style string to set the properties for the given style - * number. - * @param num The style number to set properties for. - * @param style The style string containing properties to set. - */ - void SetStyle(int num, const char *style) { - auto style_copy = static_cast<char *>(malloc(strlen(style) + 1)); - char *key = strcpy(style_copy, style), *next = nullptr, *val = nullptr; - while (key) { - if ((next = strchr(key, ','))) *next++ = '\0'; - if ((val = strchr(key, ':'))) *val++ = '\0'; - if (strcmp(key, "font") == 0 && val) - SS(sci, SCI_STYLESETFONT, num, reinterpret_cast<sptr_t>(val)); - else if (strcmp(key, "size") == 0 && val) - SS(sci, SCI_STYLESETSIZE, num, static_cast<int>(atoi(val))); - else if (strcmp(key, "bold") == 0 || strcmp(key, "notbold") == 0 || - strcmp(key, "weight") == 0) { + /** + * The lexer's Lua state. + * It is cleared each time the lexer language changes unless `own_lua` is + * `true`. + */ + lua_State *L; + /** + * The flag indicating whether or not the Lua State is owned by the lexer. + */ + bool own_lua = true; + /** + * The set of properties for the lexer. + * The `lexer.name`, `lexer.lpeg.home`, and `lexer.lpeg.color.theme` + * properties must be defined before running the lexer. + */ + PropSetSimple props; + /** The function to send Scintilla messages with. */ + SciFnDirect SS = nullptr; + /** The Scintilla object the lexer belongs to. */ + sptr_t sci = 0; + /** + * The flag indicating whether or not the lexer needs to be re-initialized. + * Re-initialization is required after the lexer language changes. + */ + bool reinit = true; + /** + * The flag indicating whether or not the lexer language has embedded lexers. + */ + bool multilang = false; + /** + * The list of style numbers considered to be whitespace styles. + * This is used in multi-language lexers when backtracking to whitespace to + * determine which lexer grammar to use. + */ + bool ws[STYLE_MAX + 1]; + + /** + * Logs the given error message or a Lua error message, prints it, and clears + * the stack. + * Error messages are logged to the "lexer.lpeg.error" property. + * @param L The Lua State. + * @param str The error message to log and print. If `nullptr`, logs and + * prints the Lua error message at the top of the stack. + */ + void log_error(lua_State *L, const char *str = nullptr) { + const char *key = "lexer.lpeg.error"; + const char *value = str ? str : lua_tostring(L, -1); + props.Set(key, value, strlen(key), strlen(value)); + fprintf(stderr, "Lua Error: %s.\n", value); + lua_settop(L, 0); + } + + /** + * Parses the given style string to set the properties for the given style + * number. + * @param num The style number to set properties for. + * @param style The style string containing properties to set. + */ + void SetStyle(int num, const char *style) { + auto style_copy = static_cast<char *>(malloc(strlen(style) + 1)); + char *key = strcpy(style_copy, style), *next = nullptr, *val = nullptr; + while (key) { + if ((next = strchr(key, ','))) *next++ = '\0'; + if ((val = strchr(key, ':'))) *val++ = '\0'; + if (strcmp(key, "font") == 0 && val) + SS(sci, SCI_STYLESETFONT, num, reinterpret_cast<sptr_t>(val)); + else if (strcmp(key, "size") == 0 && val) + SS(sci, SCI_STYLESETSIZE, num, static_cast<int>(atoi(val))); + else if (strcmp(key, "bold") == 0 || strcmp(key, "notbold") == 0 || + strcmp(key, "weight") == 0) { #if !CURSES - int weight = SC_WEIGHT_NORMAL; - if (*key == 'b') - weight = SC_WEIGHT_BOLD; - else if (*key == 'w' && val) - weight = atoi(val); - SS(sci, SCI_STYLESETWEIGHT, num, weight); + int weight = SC_WEIGHT_NORMAL; + if (*key == 'b') + weight = SC_WEIGHT_BOLD; + else if (*key == 'w' && val) + weight = atoi(val); + SS(sci, SCI_STYLESETWEIGHT, num, weight); #else - // Scintilla curses requires font attributes to be stored in the "font - // weight" style attribute. - // First, clear any existing SC_WEIGHT_NORMAL, SC_WEIGHT_SEMIBOLD, or - // SC_WEIGHT_BOLD values stored in the lower 16 bits. Then set the - // appropriate curses attr. - sptr_t weight = SS(sci, SCI_STYLEGETWEIGHT, num, 0) & ~(A_COLOR | - A_CHARTEXT); - int bold = *key == 'b' || - (*key == 'w' && val && atoi(val) > SC_WEIGHT_NORMAL); - SS(sci, SCI_STYLESETWEIGHT, num, - bold ? weight | A_BOLD : weight & ~A_BOLD); + // Scintilla curses requires font attributes to be stored in the "font + // weight" style attribute. + // First, clear any existing SC_WEIGHT_NORMAL, SC_WEIGHT_SEMIBOLD, or + // SC_WEIGHT_BOLD values stored in the lower 16 bits. Then set the + // appropriate curses attr. + sptr_t weight = + SS(sci, SCI_STYLEGETWEIGHT, num, 0) & ~(A_COLOR | A_CHARTEXT); + int bold = + *key == 'b' || (*key == 'w' && val && atoi(val) > SC_WEIGHT_NORMAL); + SS( + sci, SCI_STYLESETWEIGHT, num, + bold ? weight | A_BOLD : weight & ~A_BOLD); #endif - } else if (strcmp(key, "italics") == 0 || strcmp(key, "notitalics") == 0) - SS(sci, SCI_STYLESETITALIC, num, *key == 'i'); - else if (strcmp(key, "underlined") == 0 || - strcmp(key, "notunderlined") == 0) { + } else if (strcmp(key, "italics") == 0 || strcmp(key, "notitalics") == 0) + SS(sci, SCI_STYLESETITALIC, num, *key == 'i'); + else if (strcmp(key, "underlined") == 0 || + strcmp(key, "notunderlined") == 0) { #if !CURSES - SS(sci, SCI_STYLESETUNDERLINE, num, *key == 'u'); + SS(sci, SCI_STYLESETUNDERLINE, num, *key == 'u'); #else - // Scintilla curses requires font attributes to be stored in the "font - // weight" style attribute. - // First, clear any existing SC_WEIGHT_NORMAL, SC_WEIGHT_SEMIBOLD, or - // SC_WEIGHT_BOLD values stored in the lower 16 bits. Then set the - // appropriate curses attr. - sptr_t weight = SS(sci, SCI_STYLEGETWEIGHT, num, 0) & ~(A_COLOR | - A_CHARTEXT); - SS(sci, SCI_STYLESETWEIGHT, num, - (*key == 'u') ? weight | A_UNDERLINE : weight & ~A_UNDERLINE); + // Scintilla curses requires font attributes to be stored in the "font + // weight" style attribute. + // First, clear any existing SC_WEIGHT_NORMAL, SC_WEIGHT_SEMIBOLD, or + // SC_WEIGHT_BOLD values stored in the lower 16 bits. Then set the + // appropriate curses attr. + sptr_t weight = + SS(sci, SCI_STYLEGETWEIGHT, num, 0) & ~(A_COLOR | A_CHARTEXT); + SS( + sci, SCI_STYLESETWEIGHT, num, + (*key == 'u') ? weight | A_UNDERLINE : weight & ~A_UNDERLINE); #endif - } else if ((strcmp(key, "fore") == 0 || strcmp(key, "back") == 0) && - val) { - int msg = (*key == 'f') ? SCI_STYLESETFORE : SCI_STYLESETBACK; - int color = static_cast<int>(strtol(val, nullptr, 0)); - if (*val == '#') { // #RRGGBB format; Scintilla format is 0xBBGGRR - color = static_cast<int>(strtol(val + 1, nullptr, 16)); - color = ((color & 0xFF0000) >> 16) | (color & 0xFF00) | - ((color & 0xFF) << 16); // convert to 0xBBGGRR - } - SS(sci, msg, num, color); - } else if (strcmp(key, "eolfilled") == 0 || - strcmp(key, "noteolfilled") == 0) - SS(sci, SCI_STYLESETEOLFILLED, num, *key == 'e'); - else if (strcmp(key, "characterset") == 0 && val) - SS(sci, SCI_STYLESETCHARACTERSET, num, static_cast<int>(atoi(val))); - else if (strcmp(key, "case") == 0 && val) { - if (*val == 'u') - SS(sci, SCI_STYLESETCASE, num, SC_CASE_UPPER); - else if (*val == 'l') - SS(sci, SCI_STYLESETCASE, num, SC_CASE_LOWER); - } else if (strcmp(key, "visible") == 0 || strcmp(key, "notvisible") == 0) - SS(sci, SCI_STYLESETVISIBLE, num, *key == 'v'); - else if (strcmp(key, "changeable") == 0 || - strcmp(key, "notchangeable") == 0) - SS(sci, SCI_STYLESETCHANGEABLE, num, *key == 'c'); - else if (strcmp(key, "hotspot") == 0 || strcmp(key, "nothotspot") == 0) - SS(sci, SCI_STYLESETHOTSPOT, num, *key == 'h'); - key = next; - } - free(style_copy); - } - - /** - * Iterates through the lexer's `_TOKENSTYLES`, setting the style properties - * for all defined styles. - */ - bool SetStyles() { - //int orig_stack_top = lua_gettop(L); - // If the lexer defines additional styles, set their properties first (if - // the user has not already defined them). - lua_rawgetp(L, LUA_REGISTRYINDEX, reinterpret_cast<void *>(this)); - lua_getfield(L, -1, "_EXTRASTYLES"); - lua_pushnil(L); - while (lua_next(L, -2)) { - if (lua_isstring(L, -2) && lua_isstring(L, -1)) { - lua_pushstring(L, "style."), lua_pushvalue(L, -3), lua_concat(L, 2); - if (!*props.Get(lua_tostring(L, -1))) - props.Set(lua_tostring(L, -1), lua_tostring(L, -2), lua_rawlen(L, -1), - lua_rawlen(L, -2)); - lua_pop(L, 1); // style name - } - lua_pop(L, 1); // value - } - lua_pop(L, 1); // _EXTRASTYLES - - if (!SS || !sci) { - lua_pop(L, 1); // lexer object - // Skip, but do not report an error since `reinit` would remain `false` - // and subsequent calls to `Lex()` and `Fold()` would repeatedly call this - // function and error. - //assert(lua_gettop(L) == orig_stack_top); - return true; - } - lua_pushstring(L, "style.default"), expand_property(L); - SetStyle(STYLE_DEFAULT, lua_tostring(L, -1)); - lua_pop(L, 1); // style - SS(sci, SCI_STYLECLEARALL, 0, 0); // set default styles - lua_getfield(L, -1, "_TOKENSTYLES"); - lua_pushnil(L); - while (lua_next(L, -2)) { - if (lua_isstring(L, -2) && lua_isnumber(L, -1) && - lua_tointeger(L, -1) != STYLE_DEFAULT) { - lua_pushstring(L, "style."), lua_pushvalue(L, -3), lua_concat(L, 2); - expand_property(L); - SetStyle(lua_tointeger(L, -2), lua_tostring(L, -1)); - lua_pop(L, 1); // style - } - lua_pop(L, 1); // value - } - lua_pop(L, 2); // _TOKENSTYLES, lexer object - //assert(lua_gettop(L) == orig_stack_top); - return true; - } - - /** - * Returns the style name for the given style number. - * @param style The style number to get the style name for. - * @return style name or nullptr - */ - const char *GetStyleName(int style) { - if (!L) return nullptr; - //int orig_stack_top = lua_gettop(L); - const char *name = nullptr; - lua_rawgetp(L, LUA_REGISTRYINDEX, reinterpret_cast<void *>(this)); - lua_getfield(L, -1, "_TOKENSTYLES"); - lua_pushnil(L); - while (lua_next(L, -2)) - if (lua_tointeger(L, -1) == style) { - name = lua_tostring(L, -2); // no need to copy; will remain in memory - lua_pop(L, 2); // value and key - break; - } else lua_pop(L, 1); // value - lua_pop(L, 2); // _TOKENSTYLES, lexer object - //assert(lua_gettop(L) == orig_stack_top); - return name; - } - - /** - * Initializes the lexer once the `lexer.lpeg.home` and `lexer.name` - * properties are set. - */ - bool Init() { - char home[FILENAME_MAX], lexer[50], theme[FILENAME_MAX]; - props.GetExpanded("lexer.lpeg.home", home); - props.GetExpanded("lexer.name", lexer); - props.GetExpanded("lexer.lpeg.color.theme", theme); - if (!*home || !*lexer || !L) return false; - //int orig_stack_top = lua_gettop(L); - - // Designate the currently running LexerLPeg instance. - // This needs to be done prior to calling any Lua lexer code, particularly - // when `own_lua` is `false`, as there may be multiple LexerLPeg instances - // floating around, and the lexer module methods and metamethods need to - // know which instance to use. - lua_pushlightuserdata(L, reinterpret_cast<void *>(this)); - lua_setfield(L, LUA_REGISTRYINDEX, "sci_lexer_lpeg"); - - // If necessary, load the lexer module and theme. - lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); - if (lua_getfield(L, -1, "lexer") == LUA_TNIL) { - lua_pop(L, 2); // nil and _LOADED - - // Modify `package.path` to find lexers. - lua_getglobal(L, "package"), lua_getfield(L, -1, "path"); - int orig_path = luaL_ref(L, LUA_REGISTRYINDEX); // restore later - lua_pushstring(L, home), lua_pushstring(L, "/?.lua"), lua_concat(L, 2); - lua_setfield(L, -2, "path"), lua_pop(L, 1); // package - - // Load the lexer module. - lua_getglobal(L, "require"); - lua_pushstring(L, "lexer"); - if (lua_pcall(L, 1, 1, 0) != LUA_OK) return (log_error(L), false); - lua_pushinteger(L, SC_FOLDLEVELBASE); - lua_setfield(L, -2, "FOLD_BASE"); - lua_pushinteger(L, SC_FOLDLEVELWHITEFLAG); - lua_setfield(L, -2, "FOLD_BLANK"); - lua_pushinteger(L, SC_FOLDLEVELHEADERFLAG); - lua_setfield(L, -2, "FOLD_HEADER"); - if (luaL_newmetatable(L, "sci_lexer")) { - lua_pushcfunction(L, lexer_index), lua_setfield(L, -2, "__index"); - lua_pushcfunction(L, lexer_newindex), lua_setfield(L, -2, "__newindex"); - } - lua_setmetatable(L, -2); - - // Restore `package.path`. - lua_getglobal(L, "package"); - lua_getfield(L, -1, "path"), lua_setfield(L, -3, "path"); // lexer.path = - lua_rawgeti(L, LUA_REGISTRYINDEX, orig_path), lua_setfield(L, -2, "path"); - luaL_unref(L, LUA_REGISTRYINDEX, orig_path), lua_pop(L, 1); // package - } else lua_remove(L, -2); // _LOADED - - // Load the language lexer. - if (lua_getfield(L, -1, "load") != LUA_TFUNCTION) - return (log_error(L, "'lexer.load' function not found"), false); - lua_pushstring(L, lexer), lua_pushnil(L), lua_pushboolean(L, 1); - if (lua_pcall(L, 3, 1, 0) != LUA_OK) return (log_error(L), false); - lua_remove(L, -2); // lexer module - lua_pushlightuserdata(L, reinterpret_cast<void *>(&props)); - lua_setfield(L, -2, "_PROPS"); - lua_rawsetp(L, LUA_REGISTRYINDEX, reinterpret_cast<void *>(this)); - - // Load the theme and set up styles. - if (*theme) { - if (!(strstr(theme, "/") || strstr(theme, "\\"))) { // theme name - lua_pushstring(L, home); - lua_pushstring(L, "/themes/"); - lua_pushstring(L, theme); - lua_pushstring(L, ".lua"); - lua_concat(L, 4); - } else lua_pushstring(L, theme); // path to theme - if (luaL_loadfile(L, lua_tostring(L, -1)) != LUA_OK || - lua_pcall(L, 0, 0, 0) != LUA_OK) return (log_error(L), false); - lua_pop(L, 1); // theme - } - if (!SetStyles()) return false; - - // If the lexer is a parent, it will have children in its _CHILDREN table. - lua_rawgetp(L, LUA_REGISTRYINDEX, reinterpret_cast<void *>(this)); - if (lua_getfield(L, -1, "_CHILDREN") == LUA_TTABLE) { - multilang = true; - // Determine which styles are language whitespace styles - // ([lang]_whitespace). This is necessary for determining which language - // to start lexing with. - char style_name[50]; - for (int i = 0; i <= STYLE_MAX; i++) { - PrivateCall(i, reinterpret_cast<void *>(style_name)); - ws[i] = strstr(style_name, "whitespace") ? true : false; - } - } - lua_pop(L, 2); // _CHILDREN, lexer object - - reinit = false; - props.Set("lexer.lpeg.error", "", strlen("lexer.lpeg.error"), 0); - //assert(lua_gettop(L) == orig_stack_top); - return true; - } - - /** - * When *lparam* is `0`, returns the size of the buffer needed to store the - * given string *str* in; otherwise copies *str* into the buffer *lparam* and - * returns the number of bytes copied. - * @param lparam `0` to get the number of bytes needed to store *str* or a - * pointer to a buffer large enough to copy *str* into. - * @param str The string to copy. - * @return number of bytes needed to hold *str* - */ - void *StringResult(long lparam, const char *str) { - if (lparam) strcpy(reinterpret_cast<char *>(lparam), str); - return reinterpret_cast<void *>(strlen(str)); - } + } else if ((strcmp(key, "fore") == 0 || strcmp(key, "back") == 0) && + val) { + int msg = (*key == 'f') ? SCI_STYLESETFORE : SCI_STYLESETBACK; + int color = static_cast<int>(strtol(val, nullptr, 0)); + if (*val == '#') { // #RRGGBB format; Scintilla format is 0xBBGGRR + color = static_cast<int>(strtol(val + 1, nullptr, 16)); + color = + ((color & 0xFF0000) >> 16) | (color & 0xFF00) | + ((color & 0xFF) << 16); // convert to 0xBBGGRR + } + SS(sci, msg, num, color); + } else if (strcmp(key, "eolfilled") == 0 || + strcmp(key, "noteolfilled") == 0) + SS(sci, SCI_STYLESETEOLFILLED, num, *key == 'e'); + else if (strcmp(key, "characterset") == 0 && val) + SS(sci, SCI_STYLESETCHARACTERSET, num, static_cast<int>(atoi(val))); + else if (strcmp(key, "case") == 0 && val) { + if (*val == 'u') + SS(sci, SCI_STYLESETCASE, num, SC_CASE_UPPER); + else if (*val == 'l') + SS(sci, SCI_STYLESETCASE, num, SC_CASE_LOWER); + } else if (strcmp(key, "visible") == 0 || strcmp(key, "notvisible") == 0) + SS(sci, SCI_STYLESETVISIBLE, num, *key == 'v'); + else if (strcmp(key, "changeable") == 0 || + strcmp(key, "notchangeable") == 0) + SS(sci, SCI_STYLESETCHANGEABLE, num, *key == 'c'); + else if (strcmp(key, "hotspot") == 0 || strcmp(key, "nothotspot") == 0) + SS(sci, SCI_STYLESETHOTSPOT, num, *key == 'h'); + key = next; + } + free(style_copy); + } + + /** + * Iterates through the lexer's `_TOKENSTYLES`, setting the style properties + * for all defined styles. + */ + bool SetStyles() { + //int orig_stack_top = lua_gettop(L); + // If the lexer defines additional styles, set their properties first (if + // the user has not already defined them). + lua_rawgetp(L, LUA_REGISTRYINDEX, reinterpret_cast<void *>(this)); + lua_getfield(L, -1, "_EXTRASTYLES"); + lua_pushnil(L); + while (lua_next(L, -2)) { + if (lua_isstring(L, -2) && lua_isstring(L, -1)) { + lua_pushstring(L, "style."), lua_pushvalue(L, -3), lua_concat(L, 2); + if (!*props.Get(lua_tostring(L, -1))) + props.Set( + lua_tostring(L, -1), lua_tostring(L, -2), lua_rawlen(L, -1), + lua_rawlen(L, -2)); + lua_pop(L, 1); // style name + } + lua_pop(L, 1); // value + } + lua_pop(L, 1); // _EXTRASTYLES + + if (!SS || !sci) { + lua_pop(L, 1); // lexer object + // Skip, but do not report an error since `reinit` would remain `false` + // and subsequent calls to `Lex()` and `Fold()` would repeatedly call this + // function and error. + //assert(lua_gettop(L) == orig_stack_top); + return true; + } + lua_pushstring(L, "style.default"), expand_property(L); + SetStyle(STYLE_DEFAULT, lua_tostring(L, -1)); + lua_pop(L, 1); // style + SS(sci, SCI_STYLECLEARALL, 0, 0); // set default styles + lua_getfield(L, -1, "_TOKENSTYLES"); + lua_pushnil(L); + while (lua_next(L, -2)) { + if (lua_isstring(L, -2) && lua_isnumber(L, -1) && + lua_tointeger(L, -1) != STYLE_DEFAULT) { + lua_pushstring(L, "style."), lua_pushvalue(L, -3), lua_concat(L, 2); + expand_property(L); + SetStyle(lua_tointeger(L, -2), lua_tostring(L, -1)); + lua_pop(L, 1); // style + } + lua_pop(L, 1); // value + } + lua_pop(L, 2); // _TOKENSTYLES, lexer object + //assert(lua_gettop(L) == orig_stack_top); + return true; + } + + /** + * Returns the style name for the given style number. + * @param style The style number to get the style name for. + * @return style name or nullptr + */ + const char *GetStyleName(int style) { + if (!L) return nullptr; + //int orig_stack_top = lua_gettop(L); + const char *name = nullptr; + lua_rawgetp(L, LUA_REGISTRYINDEX, reinterpret_cast<void *>(this)); + lua_getfield(L, -1, "_TOKENSTYLES"); + lua_pushnil(L); + while (lua_next(L, -2)) + if (lua_tointeger(L, -1) == style) { + name = lua_tostring(L, -2); // no need to copy; will remain in memory + lua_pop(L, 2); // value and key + break; + } else lua_pop(L, 1); // value + lua_pop(L, 2); // _TOKENSTYLES, lexer object + //assert(lua_gettop(L) == orig_stack_top); + return name; + } + + /** + * Initializes the lexer once the `lexer.lpeg.home` and `lexer.name` + * properties are set. + */ + bool Init() { + char home[FILENAME_MAX], lexer[50], theme[FILENAME_MAX]; + props.GetExpanded("lexer.lpeg.home", home); + props.GetExpanded("lexer.name", lexer); + props.GetExpanded("lexer.lpeg.color.theme", theme); + if (!*home || !*lexer || !L) return false; + //int orig_stack_top = lua_gettop(L); + + // Designate the currently running LexerLPeg instance. + // This needs to be done prior to calling any Lua lexer code, particularly + // when `own_lua` is `false`, as there may be multiple LexerLPeg instances + // floating around, and the lexer module methods and metamethods need to + // know which instance to use. + lua_pushlightuserdata(L, reinterpret_cast<void *>(this)); + lua_setfield(L, LUA_REGISTRYINDEX, "sci_lexer_lpeg"); + + // If necessary, load the lexer module and theme. + lua_getfield(L, LUA_REGISTRYINDEX, "_LOADED"); + if (lua_getfield(L, -1, "lexer") == LUA_TNIL) { + lua_pop(L, 2); // nil and _LOADED + + // Modify `package.path` to find lexers. + lua_getglobal(L, "package"), lua_getfield(L, -1, "path"); + int orig_path = luaL_ref(L, LUA_REGISTRYINDEX); // restore later + lua_pushstring(L, home), lua_pushstring(L, "/?.lua"), lua_concat(L, 2); + lua_setfield(L, -2, "path"), lua_pop(L, 1); // package + + // Load the lexer module. + lua_getglobal(L, "require"); + lua_pushstring(L, "lexer"); + if (lua_pcall(L, 1, 1, 0) != LUA_OK) return (log_error(L), false); + lua_pushinteger(L, SC_FOLDLEVELBASE); + lua_setfield(L, -2, "FOLD_BASE"); + lua_pushinteger(L, SC_FOLDLEVELWHITEFLAG); + lua_setfield(L, -2, "FOLD_BLANK"); + lua_pushinteger(L, SC_FOLDLEVELHEADERFLAG); + lua_setfield(L, -2, "FOLD_HEADER"); + if (luaL_newmetatable(L, "sci_lexer")) { + lua_pushcfunction(L, lexer_index), lua_setfield(L, -2, "__index"); + lua_pushcfunction(L, lexer_newindex), lua_setfield(L, -2, "__newindex"); + } + lua_setmetatable(L, -2); + + // Restore `package.path`. + lua_getglobal(L, "package"); + lua_getfield(L, -1, "path"), lua_setfield(L, -3, "path"); // lexer.path = + lua_rawgeti(L, LUA_REGISTRYINDEX, orig_path), lua_setfield(L, -2, "path"); + luaL_unref(L, LUA_REGISTRYINDEX, orig_path), lua_pop(L, 1); // package + } else lua_remove(L, -2); // _LOADED + + // Load the language lexer. + if (lua_getfield(L, -1, "load") != LUA_TFUNCTION) + return (log_error(L, "'lexer.load' function not found"), false); + lua_pushstring(L, lexer), lua_pushnil(L), lua_pushboolean(L, 1); + if (lua_pcall(L, 3, 1, 0) != LUA_OK) return (log_error(L), false); + lua_remove(L, -2); // lexer module + lua_pushlightuserdata(L, reinterpret_cast<void *>(&props)); + lua_setfield(L, -2, "_PROPS"); + lua_rawsetp(L, LUA_REGISTRYINDEX, reinterpret_cast<void *>(this)); + + // Load the theme and set up styles. + if (*theme) { + if (!(strstr(theme, "/") || strstr(theme, "\\"))) { // theme name + lua_pushstring(L, home); + lua_pushstring(L, "/themes/"); + lua_pushstring(L, theme); + lua_pushstring(L, ".lua"); + lua_concat(L, 4); + } else lua_pushstring(L, theme); // path to theme + if (luaL_loadfile(L, lua_tostring(L, -1)) != LUA_OK || + lua_pcall(L, 0, 0, 0) != LUA_OK) return (log_error(L), false); + lua_pop(L, 1); // theme + } + if (!SetStyles()) return false; + + // If the lexer is a parent, it will have children in its _CHILDREN table. + lua_rawgetp(L, LUA_REGISTRYINDEX, reinterpret_cast<void *>(this)); + if (lua_getfield(L, -1, "_CHILDREN") == LUA_TTABLE) { + multilang = true; + // Determine which styles are language whitespace styles + // ([lang]_whitespace). This is necessary for determining which language + // to start lexing with. + char style_name[50]; + for (int i = 0; i <= STYLE_MAX; i++) { + PrivateCall(i, reinterpret_cast<void *>(style_name)); + ws[i] = strstr(style_name, "whitespace") ? true : false; + } + } + lua_pop(L, 2); // _CHILDREN, lexer object + + reinit = false; + props.Set("lexer.lpeg.error", "", strlen("lexer.lpeg.error"), 0); + //assert(lua_gettop(L) == orig_stack_top); + return true; + } + + /** + * When *lparam* is `0`, returns the size of the buffer needed to store the + * given string *str* in; otherwise copies *str* into the buffer *lparam* and + * returns the number of bytes copied. + * @param lparam `0` to get the number of bytes needed to store *str* or a + * pointer to a buffer large enough to copy *str* into. + * @param str The string to copy. + * @return number of bytes needed to hold *str* + */ + void *StringResult(long lparam, const char *str) { + if (lparam) strcpy(reinterpret_cast<char *>(lparam), str); + return reinterpret_cast<void *>(strlen(str)); + } public: - /** Constructor. */ - LexerLPeg() : L(luaL_newstate()) { - // Initialize the Lua state, load libraries, and set platform variables. - if (!L) { - fprintf(stderr, "Lua failed to initialize.\n"); - return; - } + /** Constructor. */ + LexerLPeg() : L(luaL_newstate()) { + // Initialize the Lua state, load libraries, and set platform variables. + if (!L) { + fprintf(stderr, "Lua failed to initialize.\n"); + return; + } #if LUA_VERSION_NUM < 502 - luaL_requiref(L, "", luaopen_base, 1), lua_pop(L, 1); + luaL_requiref(L, "", luaopen_base, 1), lua_pop(L, 1); #else - luaL_requiref(L, "_G", luaopen_base, 1), lua_pop(L, 1); + luaL_requiref(L, "_G", luaopen_base, 1), lua_pop(L, 1); #endif - luaL_requiref(L, LUA_TABLIBNAME, luaopen_table, 1), lua_pop(L, 1); - luaL_requiref(L, LUA_STRLIBNAME, luaopen_string, 1), lua_pop(L, 1); + luaL_requiref(L, LUA_TABLIBNAME, luaopen_table, 1), lua_pop(L, 1); + luaL_requiref(L, LUA_STRLIBNAME, luaopen_string, 1), lua_pop(L, 1); #if LUA_VERSION_NUM < 502 - // `package.searchpath()` emulation requires io. - luaL_requiref(L, LUA_IOLIBNAME, luaopen_io, 1), lua_pop(L, 1); + // `package.searchpath()` emulation requires io. + luaL_requiref(L, LUA_IOLIBNAME, luaopen_io, 1), lua_pop(L, 1); #endif - luaL_requiref(L, LUA_LOADLIBNAME, luaopen_package, 1), lua_pop(L, 1); - luaL_requiref(L, "lpeg", luaopen_lpeg, 1), lua_pop(L, 1); + luaL_requiref(L, LUA_LOADLIBNAME, luaopen_package, 1), lua_pop(L, 1); + luaL_requiref(L, "lpeg", luaopen_lpeg, 1), lua_pop(L, 1); #if _WIN32 - lua_pushboolean(L, 1), lua_setglobal(L, "WIN32"); + lua_pushboolean(L, 1), lua_setglobal(L, "WIN32"); #endif #if __APPLE__ - lua_pushboolean(L, 1), lua_setglobal(L, "OSX"); + lua_pushboolean(L, 1), lua_setglobal(L, "OSX"); #endif #if GTK - lua_pushboolean(L, 1), lua_setglobal(L, "GTK"); + lua_pushboolean(L, 1), lua_setglobal(L, "GTK"); #endif #if CURSES - lua_pushboolean(L, 1), lua_setglobal(L, "CURSES"); + lua_pushboolean(L, 1), lua_setglobal(L, "CURSES"); #endif - } - - /** Destructor. */ - virtual ~LexerLPeg() = default; - - /** Destroys the lexer object. */ - void SCI_METHOD Release() override { - if (own_lua && L) - lua_close(L); - else if (!own_lua) { - lua_pushnil(L); - lua_rawsetp(L, LUA_REGISTRYINDEX, reinterpret_cast<void *>(this)); - } - delete this; - } - - /** - * Lexes the Scintilla document. - * @param startPos The position in the document to start lexing at. - * @param lengthDoc The number of bytes in the document to lex. - * @param initStyle The initial style at position *startPos* in the document. - * @param buffer The document interface. - */ - void SCI_METHOD Lex(Sci_PositionU startPos, Sci_Position lengthDoc, - int initStyle, IDocument *buffer) override { - LexAccessor styler(buffer); - if ((reinit && !Init()) || !L) { - // Style everything in the default style. - styler.StartAt(startPos); - styler.StartSegment(startPos); - styler.ColourTo(startPos + lengthDoc - 1, STYLE_DEFAULT); - styler.Flush(); - return; - } - //int orig_stack_top = lua_gettop(L); - lua_pushlightuserdata(L, reinterpret_cast<void *>(this)); - lua_setfield(L, LUA_REGISTRYINDEX, "sci_lexer_lpeg"); - lua_rawgetp(L, LUA_REGISTRYINDEX, reinterpret_cast<void *>(this)); - lua_pushlightuserdata(L, reinterpret_cast<void *>(buffer)); - lua_setfield(L, -2, "_BUFFER"); - - // Ensure the lexer has a grammar. - // This could be done in the lexer module's `lex()`, but for large files, - // passing string arguments from C to Lua is expensive. - if (!lua_getfield(L, -1, "_GRAMMAR")) { - lua_pop(L, 2); // _GRAMMAR, lexer object - // Style everything in the default style. - styler.StartAt(startPos); - styler.StartSegment(startPos); - styler.ColourTo(startPos + lengthDoc - 1, STYLE_DEFAULT); - styler.Flush(); - return; - } else lua_pop(L, 1); // _GRAMMAR - - // Start from the beginning of the current style so LPeg matches it. - // For multilang lexers, start at whitespace since embedded languages have - // [lang]_whitespace styles. This is so LPeg can start matching child - // languages instead of parent ones if necessary. - if (startPos > 0) { - Sci_PositionU i = startPos; - while (i > 0 && styler.StyleAt(i - 1) == initStyle) i--; - if (multilang) - while (i > 0 && !ws[static_cast<size_t>(styler.StyleAt(i))]) i--; - lengthDoc += startPos - i, startPos = i; - } - - if (lua_getfield(L, -1, "lex") != LUA_TFUNCTION) - return log_error(L, "'lexer.lex' function not found"); - lua_pushvalue(L, -2); - lua_pushlstring(L, buffer->BufferPointer() + startPos, lengthDoc); - lua_pushinteger(L, styler.StyleAt(startPos)); - if (lua_pcall(L, 3, 1, 0) != LUA_OK) return log_error(L); - if (!lua_istable(L, -1)) - return log_error(L, "Table of tokens expected from 'lexer.lex'"); - // Style the text from the token table returned. - int len = lua_rawlen(L, -1); - if (len > 0) { - int style = STYLE_DEFAULT; - styler.StartAt(startPos); - styler.StartSegment(startPos); - lua_getfield(L, -2, "_TOKENSTYLES"); - // Loop through token-position pairs. - for (int i = 1; i < len; i += 2) { - style = STYLE_DEFAULT; - if (lua_rawgeti(L, -2, i), lua_rawget(L, -2)) - style = lua_tointeger(L, -1); - lua_pop(L, 1); // _TOKENSTYLES[token] - lua_rawgeti(L, -2, i + 1); // pos - unsigned int position = lua_tointeger(L, -1) - 1; - lua_pop(L, 1); // pos - if (style >= 0 && style <= STYLE_MAX) - styler.ColourTo(startPos + position - 1, style); - else - log_error(L, "Bad style number"); - if (position > startPos + lengthDoc) break; - } - lua_pop(L, 1); // _TOKENSTYLES - styler.ColourTo(startPos + lengthDoc - 1, style); - styler.Flush(); - } - lua_pop(L, 2); // token table returned, lexer object - //assert(lua_gettop(L) == orig_stack_top); - } - - /** - * Folds the Scintilla document. - * @param startPos The position in the document to start folding at. - * @param lengthDoc The number of bytes in the document to fold. - * @param initStyle The initial style at position *startPos* in the document. - * @param buffer The document interface. - */ - void SCI_METHOD Fold(Sci_PositionU startPos, Sci_Position lengthDoc, - int, IDocument *buffer) override { - if ((reinit && !Init()) || !L) return; - //int orig_stack_top = lua_gettop(L); - lua_pushlightuserdata(L, reinterpret_cast<void *>(this)); - lua_setfield(L, LUA_REGISTRYINDEX, "sci_lexer_lpeg"); - lua_rawgetp(L, LUA_REGISTRYINDEX, reinterpret_cast<void *>(this)); - lua_pushlightuserdata(L, reinterpret_cast<void *>(buffer)); - lua_setfield(L, -2, "_BUFFER"); - LexAccessor styler(buffer); - - if (lua_getfield(L, -1, "fold") != LUA_TFUNCTION) - return log_error(L, "'lexer.fold' function not found"); - lua_insert(L, -2); - Sci_Position currentLine = styler.GetLine(startPos); - lua_pushlstring(L, buffer->BufferPointer() + startPos, lengthDoc); - lua_pushinteger(L, startPos); - lua_pushinteger(L, currentLine); - lua_pushinteger(L, styler.LevelAt(currentLine) & SC_FOLDLEVELNUMBERMASK); - if (lua_pcall(L, 5, 1, 0) != LUA_OK) return log_error(L); - if (!lua_istable(L, -1)) - return log_error(L, "Table of folds expected from 'lexer.fold'"); - // Fold the text from the fold table returned. - lua_pushnil(L); - while (lua_next(L, -2)) { // line = level - styler.SetLevel(lua_tointeger(L, -2), lua_tointeger(L, -1)); - lua_pop(L, 1); // level - } - lua_pop(L, 1); // fold table returned - //assert(lua_gettop(L) == orig_stack_top); - } - - /** This lexer implements the original lexer interface. */ - int SCI_METHOD Version() const override { return lvOriginal; } - /** Returning property names is not implemented. */ - const char * SCI_METHOD PropertyNames() override { return ""; } - /** Returning property types is not implemented. */ - int SCI_METHOD PropertyType(const char *) override { return 0; } - /** Returning property descriptions is not implemented. */ - const char * SCI_METHOD DescribeProperty(const char *) override { return ""; } - - /** - * Sets the *key* lexer property to *value*. - * If *key* starts with "style.", also set the style for the token. - * @param key The string keyword. - * @param val The string value. - */ - Sci_Position SCI_METHOD PropertySet(const char *key, - const char *value) override { - props.Set(key, value, strlen(key), strlen(value)); - if (reinit) - Init(); - else if (L && SS && sci && strncmp(key, "style.", 6) == 0) { - //int orig_stack_top = lua_gettop(L); - lua_pushlightuserdata(L, reinterpret_cast<void *>(this)); - lua_setfield(L, LUA_REGISTRYINDEX, "sci_lexer_lpeg"); - lua_rawgetp(L, LUA_REGISTRYINDEX, reinterpret_cast<void *>(this)); - lua_getfield(L, -1, "_TOKENSTYLES"); - if (lua_pushstring(L, key + 6), lua_rawget(L, -2) == LUA_TNUMBER) { - lua_pushstring(L, key), expand_property(L); - int style_num = lua_tointeger(L, -2); - SetStyle(style_num, lua_tostring(L, -1)); - if (style_num == STYLE_DEFAULT) - // Assume a theme change, with the default style being set first. - // Subsequent style settings will be based on the default. - SS(sci, SCI_STYLECLEARALL, 0, 0); - } - lua_pop(L, 4); // style, style number, _TOKENSTYLES, lexer object - //assert(lua_gettop(L) == orig_stack_top); - } - return -1; // no need to re-lex - } - - /** Returning keyword list descriptions is not implemented. */ - const char * SCI_METHOD DescribeWordListSets() override { return ""; } - /** Setting keyword lists is not applicable. */ - Sci_Position SCI_METHOD WordListSet(int, const char *) override { return -1; } - - /** - * Allows for direct communication between the application and the lexer. - * The application uses this to set `SS`, `sci`, `L`, and lexer properties, - * and to retrieve style names. - * @param code The communication code. - * @param arg The argument. - * @return void *data - */ - void * SCI_METHOD PrivateCall(int code, void *arg) override { - auto lParam = reinterpret_cast<sptr_t>(arg); - const char *val = nullptr; - switch(code) { - case SCI_GETDIRECTFUNCTION: - SS = reinterpret_cast<SciFnDirect>(lParam); - return nullptr; - case SCI_SETDOCPOINTER: - sci = lParam; - return nullptr; - case SCI_CHANGELEXERSTATE: - if (own_lua) lua_close(L); - L = reinterpret_cast<lua_State *>(lParam); - own_lua = false; - return nullptr; - case SCI_SETLEXERLANGUAGE: - char lexer_name[50]; - props.GetExpanded("lexer.name", lexer_name); - if (strcmp(lexer_name, reinterpret_cast<const char *>(arg)) != 0) { - reinit = true; - props.Set("lexer.lpeg.error", "", strlen("lexer.lpeg.error"), 0); - PropertySet("lexer.name", reinterpret_cast<const char *>(arg)); - } else if (L) - own_lua ? SetStyles() : Init(); - return nullptr; - case SCI_GETLEXERLANGUAGE: - if (L) { - //int orig_stack_top = lua_gettop(L); - lua_rawgetp(L, LUA_REGISTRYINDEX, reinterpret_cast<void *>(this)); - lua_getfield(L, -1, "_NAME"); - if (SS && sci && multilang) { - int pos = SS(sci, SCI_GETCURRENTPOS, 0, 0); - while (pos >= 0 && !ws[SS(sci, SCI_GETSTYLEAT, pos, 0)]) pos--; - const char *name = nullptr, *p = nullptr; - if (pos >= 0) { - name = GetStyleName(SS(sci, SCI_GETSTYLEAT, pos, 0)); - if (name) p = strstr(name, "_whitespace"); - } - if (!name) name = lua_tostring(L, -1); // "lexer:lexer" fallback - if (!p) p = name + strlen(name); // "lexer:lexer" fallback - lua_pushstring(L, "/"); - lua_pushlstring(L, name, p - name); - lua_concat(L, 3); - } - val = lua_tostring(L, -1); // no need to copy; will remain in memory - lua_pop(L, 2); // lexer_name or lexer language string, lexer object - //assert(lua_gettop(L) == orig_stack_top); - } - return StringResult(lParam, val ? val : "null"); - case SCI_GETSTATUS: - return StringResult(lParam, props.Get("lexer.lpeg.error")); - default: // style-related - if (code >= 0 && code <= STYLE_MAX) { // retrieve style names - val = GetStyleName(code); - return StringResult(lParam, val ? val : "Not Available"); - } else return nullptr; - } - } - - /** Constructs a new instance of the lexer. */ - static ILexer *LexerFactoryLPeg() { return new LexerLPeg(); } + } + + /** Destructor. */ + virtual ~LexerLPeg() = default; + + /** Destroys the lexer object. */ + void SCI_METHOD Release() override { + if (own_lua && L) + lua_close(L); + else if (!own_lua) { + lua_pushnil(L); + lua_rawsetp(L, LUA_REGISTRYINDEX, reinterpret_cast<void *>(this)); + } + delete this; + } + + /** + * Lexes the Scintilla document. + * @param startPos The position in the document to start lexing at. + * @param lengthDoc The number of bytes in the document to lex. + * @param initStyle The initial style at position *startPos* in the document. + * @param buffer The document interface. + */ + void SCI_METHOD Lex( + Sci_PositionU startPos, Sci_Position lengthDoc, int initStyle, + IDocument *buffer) override + { + LexAccessor styler(buffer); + if ((reinit && !Init()) || !L) { + // Style everything in the default style. + styler.StartAt(startPos); + styler.StartSegment(startPos); + styler.ColourTo(startPos + lengthDoc - 1, STYLE_DEFAULT); + styler.Flush(); + return; + } + //int orig_stack_top = lua_gettop(L); + lua_pushlightuserdata(L, reinterpret_cast<void *>(this)); + lua_setfield(L, LUA_REGISTRYINDEX, "sci_lexer_lpeg"); + lua_rawgetp(L, LUA_REGISTRYINDEX, reinterpret_cast<void *>(this)); + lua_pushlightuserdata(L, reinterpret_cast<void *>(buffer)); + lua_setfield(L, -2, "_BUFFER"); + + // Ensure the lexer has a grammar. + // This could be done in the lexer module's `lex()`, but for large files, + // passing string arguments from C to Lua is expensive. + if (!lua_getfield(L, -1, "_GRAMMAR")) { + lua_pop(L, 2); // _GRAMMAR, lexer object + // Style everything in the default style. + styler.StartAt(startPos); + styler.StartSegment(startPos); + styler.ColourTo(startPos + lengthDoc - 1, STYLE_DEFAULT); + styler.Flush(); + return; + } else lua_pop(L, 1); // _GRAMMAR + + // Start from the beginning of the current style so LPeg matches it. + // For multilang lexers, start at whitespace since embedded languages have + // [lang]_whitespace styles. This is so LPeg can start matching child + // languages instead of parent ones if necessary. + if (startPos > 0) { + Sci_PositionU i = startPos; + while (i > 0 && styler.StyleAt(i - 1) == initStyle) i--; + if (multilang) + while (i > 0 && !ws[static_cast<size_t>(styler.StyleAt(i))]) i--; + lengthDoc += startPos - i, startPos = i; + } + + if (lua_getfield(L, -1, "lex") != LUA_TFUNCTION) + return log_error(L, "'lexer.lex' function not found"); + lua_pushvalue(L, -2); + lua_pushlstring(L, buffer->BufferPointer() + startPos, lengthDoc); + lua_pushinteger(L, styler.StyleAt(startPos)); + if (lua_pcall(L, 3, 1, 0) != LUA_OK) return log_error(L); + if (!lua_istable(L, -1)) + return log_error(L, "Table of tokens expected from 'lexer.lex'"); + // Style the text from the token table returned. + int len = lua_rawlen(L, -1); + if (len > 0) { + int style = STYLE_DEFAULT; + styler.StartAt(startPos); + styler.StartSegment(startPos); + lua_getfield(L, -2, "_TOKENSTYLES"); + // Loop through token-position pairs. + for (int i = 1; i < len; i += 2) { + style = STYLE_DEFAULT; + if (lua_rawgeti(L, -2, i), lua_rawget(L, -2)) + style = lua_tointeger(L, -1); + lua_pop(L, 1); // _TOKENSTYLES[token] + lua_rawgeti(L, -2, i + 1); // pos + unsigned int position = lua_tointeger(L, -1) - 1; + lua_pop(L, 1); // pos + if (style >= 0 && style <= STYLE_MAX) + styler.ColourTo(startPos + position - 1, style); + else + log_error(L, "Bad style number"); + if (position > startPos + lengthDoc) break; + } + lua_pop(L, 1); // _TOKENSTYLES + styler.ColourTo(startPos + lengthDoc - 1, style); + styler.Flush(); + } + lua_pop(L, 2); // token table returned, lexer object + //assert(lua_gettop(L) == orig_stack_top); + } + + /** + * Folds the Scintilla document. + * @param startPos The position in the document to start folding at. + * @param lengthDoc The number of bytes in the document to fold. + * @param initStyle The initial style at position *startPos* in the document. + * @param buffer The document interface. + */ + void SCI_METHOD Fold( + Sci_PositionU startPos, Sci_Position lengthDoc, int, IDocument *buffer) + override + { + if ((reinit && !Init()) || !L) return; + //int orig_stack_top = lua_gettop(L); + lua_pushlightuserdata(L, reinterpret_cast<void *>(this)); + lua_setfield(L, LUA_REGISTRYINDEX, "sci_lexer_lpeg"); + lua_rawgetp(L, LUA_REGISTRYINDEX, reinterpret_cast<void *>(this)); + lua_pushlightuserdata(L, reinterpret_cast<void *>(buffer)); + lua_setfield(L, -2, "_BUFFER"); + LexAccessor styler(buffer); + + if (lua_getfield(L, -1, "fold") != LUA_TFUNCTION) + return log_error(L, "'lexer.fold' function not found"); + lua_insert(L, -2); + Sci_Position currentLine = styler.GetLine(startPos); + lua_pushlstring(L, buffer->BufferPointer() + startPos, lengthDoc); + lua_pushinteger(L, startPos); + lua_pushinteger(L, currentLine); + lua_pushinteger(L, styler.LevelAt(currentLine) & SC_FOLDLEVELNUMBERMASK); + if (lua_pcall(L, 5, 1, 0) != LUA_OK) return log_error(L); + if (!lua_istable(L, -1)) + return log_error(L, "Table of folds expected from 'lexer.fold'"); + // Fold the text from the fold table returned. + lua_pushnil(L); + while (lua_next(L, -2)) { // line = level + styler.SetLevel(lua_tointeger(L, -2), lua_tointeger(L, -1)); + lua_pop(L, 1); // level + } + lua_pop(L, 1); // fold table returned + //assert(lua_gettop(L) == orig_stack_top); + } + + /** This lexer implements the original lexer interface. */ + int SCI_METHOD Version() const override { return lvOriginal; } + /** Returning property names is not implemented. */ + const char * SCI_METHOD PropertyNames() override { return ""; } + /** Returning property types is not implemented. */ + int SCI_METHOD PropertyType(const char *) override { return 0; } + /** Returning property descriptions is not implemented. */ + const char * SCI_METHOD DescribeProperty(const char *) override { return ""; } + + /** + * Sets the *key* lexer property to *value*. + * If *key* starts with "style.", also set the style for the token. + * @param key The string keyword. + * @param val The string value. + */ + Sci_Position SCI_METHOD PropertySet( + const char *key, const char *value) override + { + props.Set(key, value, strlen(key), strlen(value)); + if (reinit) + Init(); + else if (L && SS && sci && strncmp(key, "style.", 6) == 0) { + //int orig_stack_top = lua_gettop(L); + lua_pushlightuserdata(L, reinterpret_cast<void *>(this)); + lua_setfield(L, LUA_REGISTRYINDEX, "sci_lexer_lpeg"); + lua_rawgetp(L, LUA_REGISTRYINDEX, reinterpret_cast<void *>(this)); + lua_getfield(L, -1, "_TOKENSTYLES"); + if (lua_pushstring(L, key + 6), lua_rawget(L, -2) == LUA_TNUMBER) { + lua_pushstring(L, key), expand_property(L); + int style_num = lua_tointeger(L, -2); + SetStyle(style_num, lua_tostring(L, -1)); + if (style_num == STYLE_DEFAULT) + // Assume a theme change, with the default style being set first. + // Subsequent style settings will be based on the default. + SS(sci, SCI_STYLECLEARALL, 0, 0); + } + lua_pop(L, 4); // style, style number, _TOKENSTYLES, lexer object + //assert(lua_gettop(L) == orig_stack_top); + } + return -1; // no need to re-lex + } + + /** Returning keyword list descriptions is not implemented. */ + const char * SCI_METHOD DescribeWordListSets() override { return ""; } + /** Setting keyword lists is not applicable. */ + Sci_Position SCI_METHOD WordListSet(int, const char *) override { return -1; } + + /** + * Allows for direct communication between the application and the lexer. + * The application uses this to set `SS`, `sci`, `L`, and lexer properties, + * and to retrieve style names. + * @param code The communication code. + * @param arg The argument. + * @return void *data + */ + void * SCI_METHOD PrivateCall(int code, void *arg) override { + auto lParam = reinterpret_cast<sptr_t>(arg); + const char *val = nullptr; + switch(code) { + case SCI_GETDIRECTFUNCTION: + SS = reinterpret_cast<SciFnDirect>(lParam); + return nullptr; + case SCI_SETDOCPOINTER: + sci = lParam; + return nullptr; + case SCI_CHANGELEXERSTATE: + if (own_lua) lua_close(L); + L = reinterpret_cast<lua_State *>(lParam); + own_lua = false; + return nullptr; + case SCI_SETLEXERLANGUAGE: + char lexer_name[50]; + props.GetExpanded("lexer.name", lexer_name); + if (strcmp(lexer_name, reinterpret_cast<const char *>(arg)) != 0) { + reinit = true; + props.Set("lexer.lpeg.error", "", strlen("lexer.lpeg.error"), 0); + PropertySet("lexer.name", reinterpret_cast<const char *>(arg)); + } else if (L) + own_lua ? SetStyles() : Init(); + return nullptr; + case SCI_GETLEXERLANGUAGE: + if (L) { + //int orig_stack_top = lua_gettop(L); + lua_rawgetp(L, LUA_REGISTRYINDEX, reinterpret_cast<void *>(this)); + lua_getfield(L, -1, "_NAME"); + if (SS && sci && multilang) { + int pos = SS(sci, SCI_GETCURRENTPOS, 0, 0); + while (pos >= 0 && !ws[SS(sci, SCI_GETSTYLEAT, pos, 0)]) pos--; + const char *name = nullptr, *p = nullptr; + if (pos >= 0) { + name = GetStyleName(SS(sci, SCI_GETSTYLEAT, pos, 0)); + if (name) p = strstr(name, "_whitespace"); + } + if (!name) name = lua_tostring(L, -1); // "lexer:lexer" fallback + if (!p) p = name + strlen(name); // "lexer:lexer" fallback + lua_pushstring(L, "/"); + lua_pushlstring(L, name, p - name); + lua_concat(L, 3); + } + val = lua_tostring(L, -1); // no need to copy; will remain in memory + lua_pop(L, 2); // lexer_name or lexer language string, lexer object + //assert(lua_gettop(L) == orig_stack_top); + } + return StringResult(lParam, val ? val : "null"); + case SCI_GETSTATUS: + return StringResult(lParam, props.Get("lexer.lpeg.error")); + default: // style-related + if (code >= 0 && code <= STYLE_MAX) { // retrieve style names + val = GetStyleName(code); + return StringResult(lParam, val ? val : "Not Available"); + } else return nullptr; + } + } + + /** Constructs a new instance of the lexer. */ + static ILexer *LexerFactoryLPeg() { return new LexerLPeg(); } }; LexerModule lmLPeg(SCLEX_LPEG, LexerLPeg::LexerFactoryLPeg, "lpeg"); @@ -812,7 +819,7 @@ LexerModule lmLPeg(SCLEX_LPEG, LexerLPeg::LexerFactoryLPeg, "lpeg"); using namespace Scintilla; static void LPegLex(Sci_PositionU, Sci_Position, int, WordList*[], Accessor&) { - return; + return; } LexerModule lmLPeg(SCLEX_LPEG, LPegLex, "lpeg"); |
