diff --git a/builtin/common/misc_helpers.lua b/builtin/common/misc_helpers.lua index c33465e21d..87af191199 100644 --- a/builtin/common/misc_helpers.lua +++ b/builtin/common/misc_helpers.lua @@ -658,7 +658,7 @@ function core.colorize(color, message) lines[i] = color_code .. line end - return table.concat(lines, "\n") .. core.get_color_escape_sequence("#ffffff") + return table.concat(lines, "\n") .. core.get_color_escape_sequence("#fff") end @@ -674,6 +674,7 @@ function core.strip_colors(str) return (str:gsub(ESCAPE_CHAR .. "%([bc]@[^)]+%)", "")) end + local function translate(textdomain, str, num, ...) local start_seq if textdomain == "" and num == "" then diff --git a/builtin/profiler/reporter.lua b/builtin/profiler/reporter.lua index fbd93c4954..935a2ea83f 100644 --- a/builtin/profiler/reporter.lua +++ b/builtin/profiler/reporter.lua @@ -182,7 +182,7 @@ local function format_statistics(profile, format, filter, enable_translation) formatter:format(filter) local out = formatter:flush() if not enable_translation then - out = core.get_translated_string("en", out) + out = core.strip_escapes(out) end return out end diff --git a/doc/lua_api.md b/doc/lua_api.md index d4a991a1cb..9b72edb182 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -3920,10 +3920,8 @@ The following functions provide escape sequences: * `color` is a ColorString * The escape sequence sets the text color to `color` * `core.colorize(color, message)`: - * Equivalent to: - `core.get_color_escape_sequence(color) .. - message .. - core.get_color_escape_sequence("#ffffff")` + * Equivalent to including the right color escape sequence in the front, + and resetting to `#fff` after the text (plus newline handling). * `core.get_background_escape_sequence(color)` * `color` is a ColorString * The escape sequence sets the background of the whole text element to @@ -3934,6 +3932,10 @@ The following functions provide escape sequences: * Removes background colors added by `get_background_escape_sequence`. * `core.strip_colors(str)` * Removes all color escape sequences. +* `core.strip_escapes(str)` + * Removes all escape sequences, including client-side translations and + any unknown or future escape sequences that Luanti might define. + * You can use this to clean text before logging or handing to an external system. Coordinate System diff --git a/src/script/lua_api/l_util.cpp b/src/script/lua_api/l_util.cpp index 5ac290b2ec..f91f4ca6fa 100644 --- a/src/script/lua_api/l_util.cpp +++ b/src/script/lua_api/l_util.cpp @@ -695,6 +695,17 @@ int ModApiUtil::l_is_valid_player_name(lua_State *L) return 1; } +// strip_escapes(str) +int ModApiUtil::l_strip_escapes(lua_State *L) +{ + NO_MAP_LOCK_REQUIRED; + + auto s = readParam(L, 1); + auto r = unescape_enriched(s); + lua_pushlstring(L, r.c_str(), r.size()); + return 1; +} + void ModApiUtil::Initialize(lua_State *L, int top) { API_FCT(log); @@ -746,6 +757,7 @@ void ModApiUtil::Initialize(lua_State *L, int top) API_FCT(urlencode); API_FCT(is_valid_player_name); + API_FCT(strip_escapes); LuaSettings::create(L, g_settings, g_settings_path); lua_setfield(L, top, "settings"); @@ -780,6 +792,7 @@ void ModApiUtil::InitializeClient(lua_State *L, int top) API_FCT(set_last_run_mod); API_FCT(urlencode); + API_FCT(strip_escapes); LuaSettings::create(L, g_settings, g_settings_path); lua_setfield(L, top, "settings"); @@ -828,6 +841,7 @@ void ModApiUtil::InitializeAsync(lua_State *L, int top) API_FCT(set_last_run_mod); API_FCT(urlencode); + API_FCT(strip_escapes); LuaSettings::create(L, g_settings, g_settings_path); lua_setfield(L, top, "settings"); diff --git a/src/script/lua_api/l_util.h b/src/script/lua_api/l_util.h index 3d6aa741d1..7185f369a0 100644 --- a/src/script/lua_api/l_util.h +++ b/src/script/lua_api/l_util.h @@ -127,6 +127,9 @@ private: // is_valid_player_name(name) static int l_is_valid_player_name(lua_State *L); + // strip_escapes(str) + static int l_strip_escapes(lua_State *L); + public: static void Initialize(lua_State *L, int top); static void InitializeAsync(lua_State *L, int top); diff --git a/src/unittest/test_scriptapi.cpp b/src/unittest/test_scriptapi.cpp index 03c3713b23..676c5f7fa8 100644 --- a/src/unittest/test_scriptapi.cpp +++ b/src/unittest/test_scriptapi.cpp @@ -17,7 +17,7 @@ namespace { public: MyScriptApi() : ScriptApiBase(ScriptingType::Async) {}; void init(); - using ScriptApiBase::getStack; + using ScriptApiBase::getStack; // make public }; } diff --git a/src/unittest/test_utilities.cpp b/src/unittest/test_utilities.cpp index 62ac6110c3..fedacb311d 100644 --- a/src/unittest/test_utilities.cpp +++ b/src/unittest/test_utilities.cpp @@ -335,6 +335,8 @@ void TestUtilities::testRemoveEscapes() L"abc\x1b(escaped)def") == L"abcdef"); UASSERT(unescape_enriched( L"abc\x1b((escaped with parenthesis\\))def") == L"abcdef"); + UASSERTEQ(auto, unescape_enriched("abc\x1b(not this\\\\)def"), + "abcdef"); UASSERT(unescape_enriched( L"abc\x1b(incomplete") == L"abc"); UASSERT(unescape_enriched( @@ -342,6 +344,9 @@ void TestUtilities::testRemoveEscapes() // Nested escapes not supported UASSERT(unescape_enriched( L"abc\x1b(outer \x1b(inner escape)escape)def") == L"abcescape)def"); + // Multiple + UASSERTEQ(auto, unescape_enriched("one\x1bX two \x1b(four)three"), + "one two three"); } void TestUtilities::testWrapRows() diff --git a/src/util/string.h b/src/util/string.h index 6674aae8a0..78881a9a4c 100644 --- a/src/util/string.h +++ b/src/util/string.h @@ -592,27 +592,25 @@ inline std::basic_string unescape_string(const std::basic_string &s) */ template [[nodiscard]] -std::basic_string unescape_enriched(const std::basic_string &s) +std::basic_string unescape_enriched(std::basic_string_view s) { std::basic_string output; output.reserve(s.size()); size_t i = 0; while (i < s.length()) { - if (s[i] == '\x1b') { + if (s[i] == static_cast('\x1b')) { ++i; - if (i == s.length()) continue; - if (s[i] == '(') { + if (i == s.length()) + continue; + if (s[i] == static_cast('(')) { ++i; - while (i < s.length() && s[i] != ')') { - if (s[i] == '\\') { + while (i < s.length() && s[i] != static_cast(')')) { + if (s[i] == static_cast('\\')) ++i; - } ++i; } - ++i; - } else { - ++i; } + ++i; continue; } output += s[i]; @@ -621,6 +619,18 @@ std::basic_string unescape_enriched(const std::basic_string &s) return output; } +// (same templating issue here) +[[nodiscard]] +inline std::string unescape_enriched(std::string_view s) +{ + return unescape_enriched(s); +} +[[nodiscard]] +inline std::wstring unescape_enriched(std::wstring_view s) +{ + return unescape_enriched(s); +} + template [[nodiscard]] std::vector > split(const std::basic_string &s, T delim)