From b71aa2db040b7477f3523e38000810444aea8f1a Mon Sep 17 00:00:00 2001 From: 20dka Date: Thu, 17 Mar 2022 00:46:55 +0100 Subject: [PATCH] advanced autocomplete for lua --- deps/commandline | 2 +- include/TLuaEngine.h | 9 ++++++ src/TConsole.cpp | 71 +++++++++++++++++++++++++++++++++----------- src/TLuaEngine.cpp | 60 +++++++++++++++++++++++++++++++++++++ 4 files changed, 124 insertions(+), 18 deletions(-) diff --git a/deps/commandline b/deps/commandline index 7b9f51d..d345985 160000 --- a/deps/commandline +++ b/deps/commandline @@ -1 +1 @@ -Subproject commit 7b9f51d6a0402500a8f84a2fb37223ac4309f2d7 +Subproject commit d3459852fe387a1911a8298d3b02f70cc7de5093 diff --git a/include/TLuaEngine.h b/include/TLuaEngine.h index b3cf883..049b554 100644 --- a/include/TLuaEngine.h +++ b/include/TLuaEngine.h @@ -96,6 +96,13 @@ public: std::unique_lock Lock(mLuaStatesMutex); return mLuaStates.size(); } + std::vector GetLuaStateNames() { + std::vector names{}; + for(auto const& [stateId, _ ] : mLuaStates) { + names.push_back(stateId); + } + return names; + } size_t GetTimedEventsCount() { std::unique_lock Lock(mTimedEventsMutex); return mTimedEvents.size(); @@ -172,6 +179,7 @@ public: static constexpr const char* BeamMPFnNotFoundError = "BEAMMP_FN_NOT_FOUND"; std::vector GetStateGlobalKeysForState(TLuaStateId StateId); + std::vector GetStateTableKeysForState(TLuaStateId StateId, std::vector keys); // Debugging functions (slow) std::unordered_map /* handlers */> Debug_GetEventsForState(TLuaStateId StateId); @@ -199,6 +207,7 @@ private: sol::state_view State() { return sol::state_view(mState); } std::vector GetStateGlobalKeys(); + std::vector GetStateTableKeys(const std::vector& keys); // Debug functions, slow std::queue>> Debug_GetStateExecuteQueue(); diff --git a/src/TConsole.cpp b/src/TConsole.cpp index d26c95e..e2a39b5 100644 --- a/src/TConsole.cpp +++ b/src/TConsole.cpp @@ -51,6 +51,18 @@ TEST_CASE("TrimString") { CHECK(TrimString("") == ""); } +// TODO: add unit tests to SplitString +static inline void SplitString(std::string const& str, const char delim, std::vector& out) { + size_t start; + size_t end = 0; + + while ((start = str.find_first_not_of(delim, end)) != std::string::npos) { + end = str.find(delim, start); + out.push_back(str.substr(start, end - start)); + } +} + + std::string GetDate() { std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); time_t tt = std::chrono::system_clock::to_time_t(now); @@ -618,45 +630,70 @@ TConsole::TConsole() { beammp_error("Console died with: " + std::string(e.what()) + ". This could be a fatal error and could cause the server to terminate."); } }; - mCommandline.on_autocomplete = [this](Commandline&, std::string stub, int) { + mCommandline.on_autocomplete = [this](Commandline& c, std::string stub, int cursorPos) { std::vector suggestions; try { - auto cmd = TrimString(stub); - // beammp_error("yes 1"); - // beammp_error(stub); if (mIsLuaConsole) { // if lua if (!mLuaEngine) { beammp_info("Lua not started yet, please try again in a second"); } else { - std::string prefix {}; - for (size_t i = stub.length(); i > 0; i--) { - if (!std::isalnum(stub[i - 1]) && stub[i - 1] != '_') { + std::string prefix {}; // stores non-table part of input + for (size_t i = stub.length(); i > 0; i--) { //separate table from input + if (!std::isalnum(stub[i - 1]) && stub[i - 1] != '_' && stub[i - 1] != '.') { prefix = stub.substr(0, i); stub = stub.substr(i); break; } } - auto keys = mLuaEngine->GetStateGlobalKeysForState(mStateId); - for (const auto& key : keys) { - std::string::size_type n = key.find(stub); + + // turn string into vector of keys + std::vector tablekeys; + + SplitString(stub, '.', tablekeys); + + // remove last key if incomplete + if (stub.rfind('.') != stub.size() - 1 && !tablekeys.empty()) { + tablekeys.pop_back(); + } + + auto keys = mLuaEngine->GetStateTableKeysForState(mStateId, tablekeys); + + for (const auto& key : keys) { // go through each bottom-level key + auto last_dot = stub.rfind('.'); + std::string last_atom; + if (last_dot != std::string::npos) { + last_atom = stub.substr(last_dot + 1); + } + std::string before_last_atom = stub.substr(0, last_dot + 1); // get last confirmed key + auto last = stub.substr(stub.rfind('.') + 1); + std::string::size_type n = key.find(last); if (n == 0) { - suggestions.push_back(prefix + key); - // beammp_warn(cmd_name); + suggestions.push_back(prefix + before_last_atom + key); } } } } else { // if not lua - for (const auto& [cmd_name, cmd_fn] : mCommandMap) { - std::string::size_type n = cmd_name.find(stub); - if (n == 0) { - suggestions.push_back(cmd_name); - // beammp_warn(cmd_name); + if (stub.find("lua") == 0) { // starts with "lua" means we should suggest state names + std::string after_prefix = TrimString(stub.substr(3)); + auto stateNames = mLuaEngine->GetLuaStateNames(); + + for (const auto& name : stateNames) { + if (name.find(after_prefix) == 0) { + suggestions.push_back("lua " + name); + } + } + } else { + for (const auto& [cmd_name, cmd_fn] : mCommandMap) { + if (cmd_name.find(stub) == 0) { + suggestions.push_back(cmd_name); + } } } } } catch (const std::exception& e) { beammp_error("Console died with: " + std::string(e.what()) + ". This could be a fatal error and could cause the server to terminate."); } + std::sort(suggestions.begin(), suggestions.end()); return suggestions; }; } diff --git a/src/TLuaEngine.cpp b/src/TLuaEngine.cpp index 303f8bf..b493296 100644 --- a/src/TLuaEngine.cpp +++ b/src/TLuaEngine.cpp @@ -221,6 +221,66 @@ std::vector TLuaEngine::StateThreadData::GetStateGlobalKeys() { return Result; } +std::vector TLuaEngine::GetStateTableKeysForState(TLuaStateId StateId, std::vector keys) { + std::unique_lock Lock(mLuaStatesMutex); + auto Result = mLuaStates.at(StateId)->GetStateTableKeys(keys); + return Result; +} + +std::vector TLuaEngine::StateThreadData::GetStateTableKeys(const std::vector& keys) { + auto globals = mStateView.globals(); + + sol::table current = globals; + std::vector Result {}; + + for (const auto& [key, value] : current) { + std::string s = key.as(); + if (value.get_type() == sol::type::function) { + s += "("; + } + Result.push_back(s); + } + + if (!keys.empty()) { + Result.clear(); + } + + for (size_t i = 0; i < keys.size(); ++i) { + auto obj = current.get(keys.at(i)); + if (obj.get_type() == sol::type::nil) { + // error + break; + } else if (i == keys.size() - 1) { + if (obj.get_type() == sol::type::table) { + for (const auto& [key, value] : obj.as()) { + std::string s = key.as(); + if (value.get_type() == sol::type::function) { + s += "("; + } + Result.push_back(s); + } + } else { + Result = { obj.as() }; + } + break; + } + if (obj.get_type() == sol::type::table) { + current = obj; + } else { + // error + break; + } + } + + return Result; +} + +/* + + _G.a.b.c.d. + +*/ + void TLuaEngine::WaitForAll(std::vector>& Results, const std::optional& Max) { for (const auto& Result : Results) { bool Cancelled = false;