Skip to content

Instantly share code, notes, and snippets.

@sfinktah
Created October 17, 2022 09:42
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save sfinktah/1c788aebfe7b901e8df2d978f1fd97f5 to your computer and use it in GitHub Desktop.
Save sfinktah/1c788aebfe7b901e8df2d978f1fd97f5 to your computer and use it in GitHub Desktop.
lua autocompletion for C++/sol3
#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