diff --git a/LexLPeg.cxx b/LexLPeg.cxx index fdf8370..e82ca92 100644 --- a/LexLPeg.cxx +++ b/LexLPeg.cxx @@ -39,6 +39,7 @@ LUALIB_API int luaopen_lpeg(lua_State *L); } using namespace Scintilla; +using namespace Lexilla; #ifndef NDEBUG #define RECORD_STACK_TOP(l) int orig_stack_top = lua_gettop(l) @@ -223,6 +224,29 @@ static int lua_error_handler(lua_State *L) { return 1; } +/** + * Replaces the string property key the top of the stack with its expanded value. + * @param L The Lua State. + * @param lexer The LPeg lexer with properties. + */ +static void expand_property(lua_State *L, LexerLPeg *lexer) { + RECORD_STACK_TOP(L); + lua_getglobal(L, "string"), lua_getfield(L, -1, "gsub"), lua_replace(L, -2); + lua_pushstring(L, lexer->PropertyGet(luaL_checkstring(L, -2))); + lua_pushstring(L, "[$%%](%b())"); + lua_pushlightuserdata(L, lexer); + static auto closure = [](lua_State *L) -> int { + lua_getglobal(L, "string"), lua_getfield(L, -1, "sub"); + lua_pushvalue(L, 1), lua_pushnumber(L, -2), lua_pushnumber(L, 2), lua_call(L, 3, 1); + auto lexer = reinterpret_cast(lua_touserdata(L, lua_upvalueindex(1))); + lua_pushstring(L, lexer->PropertyGet(lua_tostring(L, -1))); + return 1; + }; + lua_pushcclosure(L, closure, 1); + lua_call(L, 3, 1), lua_replace(L, -2); + ASSERT_STACK_TOP(L); +} + /** lexer.property[key] metamethod. */ static int lexer_property_index(lua_State *L) { const char *property = lua_tostring(L, lua_upvalueindex(1)); @@ -242,6 +266,8 @@ static int lexer_property_index(lua_State *L) { } else if (strcmp(property, "property_int") == 0) { lua_pushstring(L, lexer->PropertyGet(luaL_checkstring(L, 2))); lua_pushinteger(L, lua_tointeger(L, -1)); + } else if (strcmp(property, "property_expanded") == 0) { + expand_property(L, lexer); } 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); @@ -261,8 +287,8 @@ 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, + strcmp(property, "property_int") != 0 && strcmp(property, "property_expanded") != 0 && + strcmp(property, "style_at") != 0 && strcmp(property, "line_from_position") != 0, 3, "read-only property"); lua_getfield(L, LUA_REGISTRYINDEX, "sci_lexer_lpeg"); LexerLPeg *lexer = reinterpret_cast(lua_touserdata(L, -1)); @@ -289,7 +315,8 @@ 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) { + strcmp(key, "property_expanded") == 0 || strcmp(key, "style_at") == 0 || + strcmp(key, "line_state") == 0) { lua_newtable(L); lua_createtable(L, 0, 2); lua_pushvalue(L, 2), lua_pushcclosure(L, lexer_property_index, 1); @@ -338,20 +365,6 @@ static int lexer_newindex(lua_State *L) { return 0; } -/** - * Replaces the string property key the top of the stack with its expanded value. - * Invokes `lexer.property_expanded[]` to perform the expansion. - * @param L The Lua State. - */ -static void expand_property(lua_State *L) { - RECORD_STACK_TOP(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_STACK_TOP(L); -} - void LexerLPeg::ReadLexerNames(const char *path) { #if !_WIN32 DIR *dir = opendir(path); @@ -483,14 +496,14 @@ void LexerLPeg::SetStyles() { ASSERT_STACK_TOP(L); return; } - lua_pushstring(L, "style.default"), expand_property(L); + lua_pushstring(L, "style.default"), expand_property(L, this); SetStyle(STYLE_DEFAULT, lua_tostring(L, -1)); lua_pop(L, 1); // style SS(sci, SCI_STYLECLEARALL, 0, 0); // set default styles for (lua_pushnil(L); lua_next(L, -2); lua_pop(L, 1)) if (lua_isstring(L, -2) && lua_isnumber(L, -1) && lua_tointeger(L, -1) - 1 != STYLE_DEFAULT) { lua_pushstring(L, "style."), lua_pushvalue(L, -3), lua_concat(L, 2); - expand_property(L); + expand_property(L, this); SetStyle(lua_tointeger(L, -2) - 1, lua_tostring(L, -1)); lua_pop(L, 1); // style } @@ -501,7 +514,7 @@ void LexerLPeg::SetStyles() { snprintf(prop_name, 64, "style.lpeg.%s.%0d", props.Get(LexerNameKey), static_cast(lua_tointeger(L, -1)) - 1); lua_pushstring(L, "style."), lua_pushvalue(L, -3), lua_concat(L, 2); - expand_property(L); + expand_property(L, this); PropertySet(prop_name, lua_tostring(L, -1)); lua_pop(L, 1); // style } @@ -511,11 +524,11 @@ void LexerLPeg::SetStyles() { } bool LexerLPeg::Init() { - if (!props.GetExpanded(LexerHomeKey, nullptr) || !*props.Get(LexerNameKey) || !L) return false; - char *_home = reinterpret_cast(malloc(props.GetExpanded(LexerHomeKey, nullptr) + 1)); - props.GetExpanded(LexerHomeKey, _home); - std::string home(_home); - free(_home); + if (!*props.Get(LexerHomeKey) || !*props.Get(LexerNameKey) || !L) return false; + lua_pushstring(L, LexerHomeKey), expand_property(L, this); + std::string home(lua_tostring(L, -1)); + lua_pop(L, 1); + if (home.empty()) return false; const char *lexer = props.Get(LexerNameKey); RECORD_STACK_TOP(L); @@ -587,14 +600,16 @@ bool LexerLPeg::Init() { lua_rawsetp(L, LUA_REGISTRYINDEX, reinterpret_cast(this)); // Load the theme and set up styles. - if (props.GetExpanded(LexerThemeKey, nullptr)) { - char *theme = reinterpret_cast(malloc(props.GetExpanded(LexerThemeKey, nullptr) + 1)); - props.GetExpanded(LexerThemeKey, theme); - if (!strstr(theme, "/") && !strstr(theme, "\\")) { // theme name + lua_pushstring(L, LexerThemeKey), expand_property(L, this); + std::string theme(lua_tostring(L, -1)); + lua_pop(L, 1); + if (!theme.empty()) { + if (theme.find('/') == std::string::npos && + theme.find('\\') == std::string::npos) { // theme name for (const std::string &dir : dirs) { lua_pushstring(L, dir.c_str()); lua_pushstring(L, "/themes/"); - lua_pushstring(L, theme); + lua_pushstring(L, theme.c_str()); lua_pushstring(L, ".lua"); lua_concat(L, 4); if (luaL_loadfile(L, lua_tostring(L, -1)) != LUA_ERRFILE || dir == dirs.back()) { @@ -604,14 +619,13 @@ bool LexerLPeg::Init() { lua_pop(L, 2); // error message, filename } } else - lua_pushstring(L, theme); // path to theme + lua_pushstring(L, theme.c_str()); // path to theme lua_pushcfunction(L, lua_error_handler); lua_insert(L, -2); if (luaL_loadfile(L, lua_tostring(L, -1)) == LUA_OK && lua_pcall(L, 0, 0, -3) == LUA_OK) lua_pop(L, 2); // theme, lua_error_handler else LogError(L); - free(theme); } SetStyles(); @@ -788,7 +802,7 @@ void SCI_METHOD LexerLPeg::Fold( } Sci_Position SCI_METHOD LexerLPeg::PropertySet(const char *key, const char *value) { - props.Set(key, value, strlen(key), strlen(value)); + props.Set(key, value); if (strcmp(key, LexerHomeKey) == 0 && lexerNames.empty()) ReadLexerNames(value); // not using SCI_CREATELOADER private call if (reinit && (strcmp(key, LexerHomeKey) == 0 || strcmp(key, LexerNameKey) == 0)) Init(); @@ -802,7 +816,7 @@ Sci_Position SCI_METHOD LexerLPeg::PropertySet(const char *key, const char *valu lua_getfield(L, -1, "_TOKENSTYLES"); lua_pushstring(L, key + 6); if (lua_rawget(L, -2) == LUA_TNUMBER) { - lua_pushstring(L, key), expand_property(L); + lua_pushstring(L, key), expand_property(L, this); int style_num = lua_tointeger(L, -2) - 1; SetStyle(style_num, lua_tostring(L, -1)); if (style_num == STYLE_DEFAULT) diff --git a/Makefile b/Makefile index 4abec4b..2063c0f 100644 --- a/Makefile +++ b/Makefile @@ -85,7 +85,7 @@ release: $(basedir) # Tests. tests: test-lexers test-scite test-wscite -test-lexers: tests.lua ; lua5.1 $< +test-lexers: tests.lua ; lua $< # Tests SciTE GTK using ~/.SciTEUser.properties. test-scite: scintilla make -C scintilla/gtk -j4 @@ -93,7 +93,7 @@ test-scite: scintilla make -C scite/gtk -j4 scite/bin/SciTE # Tests, via Wine, SciTE Win64 using SciTEGlobal.properties. -wscite_zip = wscite503.zip +wscite_zip = wscite510.zip /tmp/$(wscite_zip): ; wget -O $@ https://www.scintilla.org/$(wscite_zip) /tmp/wscite: /tmp/$(wscite_zip) unzip -d /tmp $< @@ -107,7 +107,7 @@ test-wscite: /tmp/wscite # External dependencies. scintilla_tgz = scintilla501.tgz -lexilla_tgz = lexilla500.tgz +lexilla_tgz = lexilla510.tgz lua_tgz = lua-5.3.5.tar.gz lpeg_tgz = lpeg-1.0.2.tar.gz diff --git a/README.md b/README.md index e859371..354e4ba 100644 --- a/README.md +++ b/README.md @@ -26,12 +26,11 @@ information of source code snippets. Scintilla is not required in that case. ## Requirements -Scintillua requires Scintilla 4.4.5 or greater for a drop-in installation. When compiling -Scintillua, Scintilla 5.0.1 or greater and [Lexilla][] 5.0.0 are required. The drop-in external -lexer already has Lua and LPeg are pre-compiled into it. +Scintillua requires Scintilla 5.0.1 or greater and [Lexilla][] 5.1.0 or greater for a drop-in +installation. The drop-in external lexer already has Lua and LPeg pre-compiled into it. -When used a standalone Lua library, Scintillua requires Lua 5.1 or greater and [LPeg][] 1.0.0 -or greater. Scintilla is not required. +When used as a standalone Lua library, Scintillua requires Lua 5.1 or greater and [LPeg][] +1.0.0 or greater. Scintilla is not required. [Lexilla]: https://www.scintilla.org/Lexilla.html [LPeg]: http://www.inf.puc-rio.br/~roberto/lpeg/ diff --git a/docs/manual.md b/docs/manual.md index b106575..b97bb06 100644 --- a/docs/manual.md +++ b/docs/manual.md @@ -11,7 +11,7 @@ These usages are discussed in the following sections. ### Drop-in External Lexer Scintillua can be dropped into any existing installation of a Scintilla-based application as -long as that application supports the [Lexilla][] protocol as defined in Scintilla 4.4.5 and 5.0.0. +long as that application supports [Lexilla][] 5.1.0 or greater. Scintillua releases come with three external lexers in the *lexers/* directory: *liblexlpeg.so*, which is a 64-bit Linux shared library; *LexLPeg.cxx*, which is a 64-bit Windows DLL, and @@ -22,7 +22,7 @@ which is a 64-bit Linux shared library; *LexLPeg.cxx*, which is a 64-bit Windows #### Using Scintillua with SciTE [SciTE][] is the SCIntilla based Text Editor. Scintillua can be dropped into any SciTE -installation version 5.0.3 or higher with or without administrator privileges. +installation version 5.1.0 or higher with or without administrator privileges. In order to install Scintillua for all users (likely requiring administrator privileges): diff --git a/lexers/lexer.lua b/lexers/lexer.lua index 7fcbfc7..5c56c21 100644 --- a/lexers/lexer.lua +++ b/lexers/lexer.lua @@ -1454,8 +1454,9 @@ local lexers = {} -- cache of loaded lexers function M.load(name, alt_name, cache) if cache and lexers[alt_name or name] then return lexers[alt_name or name] end - -- When using Scintillua as a stand-alone module, the `property` and `property_int` tables - -- do not exist (they are not useful). Create them in order prevent errors from occurring. + -- When using Scintillua as a stand-alone module, the `property`, `property_int`, and + -- `property_expanded` tables do not exist (they are not useful). Create them in order prevent + -- errors from occurring. if not M.property then M.property = setmetatable({['lexer.lpeg.home'] = package.path:gsub('/%?%.lua', '')}, { __index = function() return '' end, @@ -1465,6 +1466,11 @@ function M.load(name, alt_name, cache) __index = function(t, k) return tonumber(M.property[k]) or 0 end, __newindex = function() error('read-only property') end }) + M.property_expanded = setmetatable({}, { + __index = function(t, key) + return M.property[key]:gsub('[$%%](%b())', function(key) return t[key:sub(2, -2)] end) + end, __newindex = function() error('read-only property') end + }) end -- Load the language lexer with its rules, styles, etc. @@ -1827,14 +1833,6 @@ function M.fold_line_comments(prefix) return select(2, M.fold_consecutive_lines(prefix)) end -M.property_expanded = setmetatable({}, { - -- Returns the string property value associated with string property *key*, replacing any - -- "$()" and "%()" expressions with the values of their keys. - __index = function(t, key) - return M.property[key]:gsub('[$%%]%b()', function(key) return t[key:sub(3, -2)] end) - end, __newindex = function() error('read-only property') end -}) - --[[ The functions and fields below were defined in C. ---