summaryrefslogtreecommitdiff
path: root/indra
diff options
context:
space:
mode:
authorNat Goodspeed <nat@lindenlab.com>2024-02-13 12:24:13 -0500
committerNat Goodspeed <nat@lindenlab.com>2024-02-13 12:24:13 -0500
commit3044a6e62e628fdb3c4b8832fd23e566216d7bb9 (patch)
tree9a398d435bf16afae597add1204cc1e9742778ef /indra
parentc8ca31de41b29a43789a8213645fcdf52c9a29a9 (diff)
Add help() function to Lua "builtins."
help() with no argument lists all our viewer builtins. help(function, function, ...) shows help text for each named function. Each argument can be either a string or the function in question (e.g. help(help)). To support Lua-related text containing line breaks, make LLTextEditor:: pasteTextWithLinebreaks() a public template method. Change the existing implementation, which specifically accepts (const LLWString&), into its LLWString specialization. The generic template passes llconvert(arg) to that specialization, the one real implementation. Make LLFloaterLUADebug methods call pasteTextWithLinebreaks() instead of insertText(), which ignores newline characters. To allow help() to accept an actual function as well as a string name, add a lookup-by-function-pointer map to LuaFunction. (A Lua function does not store a name.) Make the constructor store an entry in the new lookup map as well as in the original registry map. Change LuaFunction::getRegistry() and getRegistered() to getState() and getRState(), respectively. Each returns a std::pair, but the first binds non-const references while the second binds const references.
Diffstat (limited to 'indra')
-rw-r--r--indra/llcommon/lua_function.cpp113
-rw-r--r--indra/llcommon/lua_function.h6
-rw-r--r--indra/llui/lltexteditor.cpp3
-rw-r--r--indra/llui/lltexteditor.h15
4 files changed, 86 insertions, 51 deletions
diff --git a/indra/llcommon/lua_function.cpp b/indra/llcommon/lua_function.cpp
index 455e853e8f..467ca04449 100644
--- a/indra/llcommon/lua_function.cpp
+++ b/indra/llcommon/lua_function.cpp
@@ -525,12 +525,15 @@ LuaPopper::~LuaPopper()
LuaFunction::LuaFunction(const std::string_view& name, lua_CFunction function,
const std::string_view& helptext)
{
- getRegistry().emplace(name, Registry::mapped_type{ function, helptext });
+ const auto& [registry, lookup] = getState();
+ registry.emplace(name, Registry::mapped_type{ function, helptext });
+ lookup.emplace(function, name);
}
void LuaFunction::init(lua_State* L)
{
- for (const auto& [name, pair]: getRegistry())
+ const auto& [registry, lookup] = getRState();
+ for (const auto& [name, pair]: registry)
{
const auto& [funcptr, helptext] = pair;
lua_register(L, name.c_str(), funcptr);
@@ -541,16 +544,75 @@ lua_CFunction LuaFunction::get(const std::string& key)
{
// use find() instead of subscripting to avoid creating an entry for
// unknown key
- const auto& registry{ getRegistry() };
+ const auto& [registry, lookup] = getState();
auto found{ registry.find(key) };
return (found == registry.end())? nullptr : found->second.first;
}
-LuaFunction::Registry& LuaFunction::getRegistry()
+std::pair<LuaFunction::Registry&, LuaFunction::Lookup&> LuaFunction::getState()
{
- // use a function-local static to ensure it's initialized
+ // use function-local statics to ensure they're initialized
static Registry registry;
- return registry;
+ static Lookup lookup;
+ return { registry, lookup };
+}
+
+/*****************************************************************************
+* help()
+*****************************************************************************/
+lua_function(help,
+ "help(): list viewer's Lua functions\n"
+ "help(function): show help string for specific function")
+{
+ auto& luapump{ LLEventPumps::instance().obtain("lua output") };
+ const auto& [registry, lookup]{ LuaFunction::getRState() };
+ if (! lua_gettop(L))
+ {
+ // no arguments passed: list all lua_functions
+ for (const auto& [name, pair] : registry)
+ {
+ const auto& [fptr, helptext] = pair;
+ luapump.post(helptext);
+ }
+ }
+ else
+ {
+ // arguments passed: list each of the specified lua_functions
+ for (int idx = 1, top = lua_gettop(L); idx <= top; ++idx)
+ {
+ std::string arg{ stringize("<unknown ", lua_typename(L, lua_type(L, idx)), ">") };
+ if (lua_type(L, idx) == LUA_TSTRING)
+ {
+ arg = lua_tostdstring(L, idx);
+ }
+ else if (lua_type(L, idx) == LUA_TFUNCTION)
+ {
+ // Caller passed the actual function instead of its string
+ // name. A Lua function is an anonymous callable object; it
+ // has a name only by assigment. You can't ask Lua for a
+ // function's name, which is why our constructor maintains a
+ // reverse Lookup map.
+ auto function{ lua_tocfunction(L, idx) };
+ if (auto found = lookup.find(function); found != lookup.end())
+ {
+ // okay, pass found name to lookup below
+ arg = found->second;
+ }
+ }
+
+ if (auto found = registry.find(arg); found != registry.end())
+ {
+ luapump.post(found->second.second);
+ }
+ else
+ {
+ luapump.post(arg + ": NOT FOUND");
+ }
+ }
+ // pop all arguments
+ lua_settop(L, 0);
+ }
+ return 0; // void return
}
/*****************************************************************************
@@ -633,42 +695,3 @@ DebugExit::~DebugExit()
{
LL_DEBUGS("Lua") << "exit " << mName << LL_ENDL;
}
-
-/*****************************************************************************
-* help()
-*****************************************************************************/
-lua_function(help,
- "help(): list viewer's Lua functions\n"
- "help(function): show help string for specific function")
-{
- auto& luapump{ LLEventPumps::instance().obtain("lua output") };
- const auto& registered{ LuaFunction::getRegistered() };
- if (! lua_gettop(L))
- {
- // no arguments passed: list all lua_functions
- for (const auto& [name, pair] : registered)
- {
- const auto& [fptr, helptext] = pair;
- luapump.post(helptext);
- }
- }
- else
- {
- // arguments passed: list each of the specified lua_functions
- for (int idx = 1, top = lua_gettop(L); idx <= top; ++idx)
- {
- auto arg{ lua_tostdstring(L, idx) };
- if (auto found = registered.find(arg); found != registered.end())
- {
- luapump.post(found->second.second);
- }
- else
- {
- luapump.post(arg + ": NOT FOUND");
- }
- }
- // pop all arguments
- lua_settop(L, 0);
- }
- return 0; // void return
-}
diff --git a/indra/llcommon/lua_function.h b/indra/llcommon/lua_function.h
index 3129b5eaca..b3a0bb0d7e 100644
--- a/indra/llcommon/lua_function.h
+++ b/indra/llcommon/lua_function.h
@@ -131,11 +131,13 @@ public:
static lua_CFunction get(const std::string& key);
+protected:
using Registry = std::map<std::string, std::pair<lua_CFunction, std::string>>;
- static const Registry& getRegistered() { return getRegistry(); }
+ using Lookup = std::map<lua_CFunction, std::string>;
+ static std::pair<const Registry&, const Lookup&> getRState() { return getState(); }
private:
- static Registry& getRegistry();
+ static std::pair<Registry&, Lookup&> getState();
};
/**
diff --git a/indra/llui/lltexteditor.cpp b/indra/llui/lltexteditor.cpp
index 99154bab83..ccd04f83e7 100644
--- a/indra/llui/lltexteditor.cpp
+++ b/indra/llui/lltexteditor.cpp
@@ -1504,7 +1504,8 @@ void LLTextEditor::cleanStringForPaste(LLWString & clean_string)
}
-void LLTextEditor::pasteTextWithLinebreaks(const LLWString & clean_string)
+template <>
+void LLTextEditor::pasteTextWithLinebreaks<LLWString>(const LLWString & clean_string)
{
std::basic_string<llwchar>::size_type start = 0;
std::basic_string<llwchar>::size_type pos = clean_string.find('\n',start);
diff --git a/indra/llui/lltexteditor.h b/indra/llui/lltexteditor.h
index 6cc2c32d4e..01b5ce5fbd 100644
--- a/indra/llui/lltexteditor.h
+++ b/indra/llui/lltexteditor.h
@@ -34,6 +34,7 @@
#include "llstyle.h"
#include "lleditmenuhandler.h"
#include "llviewborder.h" // for params
+#include "llstring.h"
#include "lltextbase.h"
#include "lltextvalidate.h"
@@ -289,16 +290,24 @@ protected:
void updateLinkSegments();
void keepSelectionOnReturn(bool keep) { mKeepSelectionOnReturn = keep; }
class LLViewBorder* mBorder;
- void pasteTextWithLinebreaks(const LLWString & clean_string);
-// void pasteTextWithLinebreaks(const std::string & clean_string);
private:
//
// Methods
//
- void pasteHelper(bool is_primary);
+ void pasteHelper(bool is_primary);
void cleanStringForPaste(LLWString & clean_string);
+public:
+ template <typename STRINGTYPE>
+ void pasteTextWithLinebreaks(const STRINGTYPE& clean_string)
+ {
+ pasteTextWithLinebreaks<LLWString>(ll_convert(clean_string));
+ }
+ template <>
+ void pasteTextWithLinebreaks<LLWString>(const LLWString & clean_string);
+
+private:
void onKeyStroke();
// Concrete TextCmd sub-classes used by the LLTextEditor base class