Created
October 17, 2022 09:42
-
-
Save sfinktah/1c788aebfe7b901e8df2d978f1fd97f5 to your computer and use it in GitHub Desktop.
lua autocompletion for C++/sol3
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#define SOL_ALL_SAFETIES_ON 1 | |
#include <sol/sol.hpp> | |
#include <iostream> | |
#include <string> | |
#include <algorithm> | |
#include <pystring/pystring.h> | |
using namespace std::string_literals; | |
template <typename T> | |
bool is_in(const T& val, const std::initializer_list<T>& list) { | |
return std::any_of(list.begin(), list.end(), [&val](const auto& a) { return a == val; }); | |
} | |
template <typename T> | |
std::string join(const T& elements, const std::string& separator = ",", bool skip_empty = false) { | |
std::ostringstream os; | |
std::string _separator; | |
for (const auto& item : elements) { | |
if (skip_empty && std::empty(item)) continue; | |
os << _separator << item; | |
_separator = separator; | |
} | |
return os.str(); | |
} | |
template <typename T> | |
std::string join(const std::initializer_list<T>& elements, const std::string& separator = ",", bool skip_empty = false) { | |
std::ostringstream os; | |
std::string _separator; | |
for (const auto& item : elements) { | |
if (skip_empty && std::empty(item)) continue; | |
os << _separator << item; | |
_separator = separator; | |
} | |
return os.str(); | |
} | |
int main(int, char*[]) { | |
std::cout << "=== autocompletion ===" << std::endl; | |
sol::state lua; | |
lua.open_libraries(sol::lib::base, sol::lib::bit32, sol::lib::io, sol::lib::math, sol::lib::os); | |
auto sscript = [&](const std::string_view& script) -> sol::protected_function_result { | |
try { | |
auto result = lua.safe_script( | |
script, sol::script_pass_on_error); | |
if (!result.valid()) { | |
auto err = result.get<sol::error>(); | |
std::cerr << "The code has failed to run!\n" | |
<< err.what() << "\nPanicking and exiting..." | |
<< std::endl; | |
} | |
return result; | |
} | |
catch (const std::exception& err) { | |
std::cerr << "The code has triggered a C++ exception!\n" | |
<< err.what() << "\nPanicking and exiting..." | |
<< std::endl; | |
} | |
return sol::protected_function_result{}; | |
}; | |
const auto autocomplete = [](const sol::table& table_, const std::string& partial, size_t limit = 64) { | |
// All problems in computer science can be solved by another level of indirection. | |
std::vector<std::string> matches; | |
auto ac_impl = [&matches, partial, limit](const sol::table& t, std::vector<std::string> parents, auto& ac_ref) -> void { | |
for (const auto& [key, value] : t) { | |
if (matches.size() >= limit) break; | |
auto key_string = key.as<std::string>(); | |
if (!parents.empty() && is_in<std::string>(key_string, { "base", "_G" })) continue; | |
auto current = join(parents, "."); | |
if (!current.empty()) | |
current += value.is<sol::function>() ? ":" : "."; | |
current += key_string; | |
if (!(partial.length() < current.length() | |
? pystring::startswith(current, partial) | |
: pystring::startswith(partial, current))) { | |
std::cout << "does not start with partial: " << current << std::endl; | |
continue; | |
} | |
if (value.is<sol::table>()) { | |
// recurse to next table depth | |
parents.emplace_back(key_string); | |
ac_ref(value.as<sol::table>(), parents, ac_ref); | |
continue; | |
} | |
std::cout << "matched: " << current << std::endl; | |
matches.emplace_back(current); | |
} | |
}; | |
ac_impl(table_, {}, ac_impl); | |
return matches; | |
}; | |
lua.set_function("autocomplete", [&](const char* partial) | |
{ | |
return autocomplete(lua.globals(), partial); | |
}); | |
lua.script(R"( autocomplete("math:m") )"); | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment