Add the ability for LuaLibrary%s to reference script files that will be executed (closing #328)

Create documentation for Lua scripts
Add Lua script to check if a file exists
This commit is contained in:
Alexander Bock
2017-06-02 16:33:17 -04:00
parent bb3256d9e4
commit 994ba32f44
9 changed files with 210 additions and 57 deletions
+17 -1
View File
@@ -55,8 +55,24 @@ struct LuaLibrary {
};
/// The name of the library
std::string name;
/// The list of all functions for this library
/// The list of all C-based callback functions for this library
std::vector<Function> functions;
/// A list of script files that are executed for each Lua state
std::vector<std::string> scripts;
/// This struct contains information about a function or constant that is defined in
/// a Lua script
struct Documentation {
/// The name of the function/variable
std::string name;
/// The description of the parameters for a function
std::string parameter;
/// The description of the function/variable
std::string description;
};
/// The list of documentations will be populated automatically by parsing the Lua
/// scripts
std::vector<Documentation> documentations;
/// Comparison function that compares two LuaLibrary%s name
bool operator<(const LuaLibrary& rhs) const;
+4 -3
View File
@@ -101,8 +101,8 @@ public:
static std::string OpenSpaceLibraryName;
private:
bool registerLuaLibrary(lua_State* state, const LuaLibrary& library);
void addLibraryFunctions(lua_State* state, const LuaLibrary& library, bool replace);
bool registerLuaLibrary(lua_State* state, LuaLibrary& library);
void addLibraryFunctions(lua_State* state, LuaLibrary& library, bool replace);
bool isLibraryNameAllowed(lua_State* state, const std::string& name);
@@ -112,7 +112,8 @@ private:
std::string generateJson() const override;
ghoul::lua::LuaState _state;
std::set<LuaLibrary> _registeredLibraries;
std::vector<LuaLibrary> _registeredLibraries;
//sync variables
std::mutex _mutex;
@@ -111,4 +111,14 @@ globebrowsing::cache::MemoryAwareTileCache* GlobeBrowsingModule::tileCache() {
return _tileCache.get();
}
scripting::LuaLibrary GlobeBrowsingModule::luaLibrary() const {
return {
"globebrowsing",
{},
{
"${MODULE_GLOBEBROWSING}/scripts/layer_support.lua"
}
};
}
} // namespace openspace
+5 -1
View File
@@ -38,14 +38,18 @@ namespace cache {
class GlobeBrowsingModule : public OpenSpaceModule {
public:
static const std::string name;
GlobeBrowsingModule();
globebrowsing::cache::MemoryAwareTileCache* tileCache();
static const std::string name;
scripting::LuaLibrary luaLibrary() const override;
protected:
void internalInitialize() override;
private:
std::unique_ptr<globebrowsing::cache::MemoryAwareTileCache> _tileCache;
};
@@ -0,0 +1,37 @@
openspace.globebrowsing.documentation = {
{
Name = "createTextureLayers",
Arguments = "table",
Documentation = "Creates a table used in the 'ColorLayers', 'GrayScaleLayers', or 'GrayScaleColorOverlays' of a RenderableGlobe."
},
{
Name = "createHeightLayers",
Arguments = "table",
Documentation = "Creates a table used in the 'HeightLayers' of a RenderableGlobe."
}
}
-- Creates a table used in the 'ColorLayers', 'GrayScaleLayers', or 'GrayScaleColorOverlays'
-- of a RenderableGlobe
-- Usage:
-- table.unpack(openspace.globebrowsing.createTextureLayers(p))
-- where p is an array that contains tables with 'Name' and 'Texture' values
openspace.globebrowsing.createTextureLayers = function (patches)
result = {}
for k,v in pairs(patches) do
table.insert(result, { Name = v["Name"], FilePath = v["Texture"] })
end
return result
end
-- Creates a table used in the 'HeightLayers' of a RenderableGlobe
-- Usage:
-- table.unpack(openspace.globebrowsing.openspace.globebrowsing.createHeightLayers(p))
-- where p is an array that contains tables with 'Name' and 'Height' values
openspace.globebrowsing.createHeightLayers = function (patches)
result = {}
for k,v in pairs(patches) do
table.insert(result, { Name = v["Name"], FilePath = v["Height"], TilePixelSize = 90, DoPreProcessing = true })
end
return result
end
+3 -3
View File
@@ -311,7 +311,7 @@ void RenderEngine::deinitialize() {
}
void RenderEngine::updateScene() {
const Time& currentTime = OsEng.timeManager().time();
const Time& currentTime = OsEng.timeManager().time();
_scene->update({
{ glm::dvec3(0), glm::dmat3(1), 1.0 },
currentTime,
@@ -485,9 +485,9 @@ void RenderEngine::renderShutdownInformation(float timer, float fullTime) {
}
void RenderEngine::postDraw() {
Time& currentTime = OsEng.timeManager().time();
Time& currentTime = OsEng.timeManager().time();
if (currentTime.timeJumped()) {
currentTime.setTimeJumped(false);
currentTime.setTimeJumped(false);
}
if (_shouldTakeScreenshot) {
+109 -48
View File
@@ -76,8 +76,8 @@ void ScriptEngine::initialize() {
addBaseLibrary();
LDEBUG("Initializing Lua state");
initializeLuaState(_state);
LDEBUG("Remapping Print functions");
remapPrintFunction();
//LDEBUG("Remapping Print functions");
//remapPrintFunction();
}
void ScriptEngine::deinitialize() {}
@@ -88,7 +88,7 @@ void ScriptEngine::initializeLuaState(lua_State* state) {
lua_setglobal(state, OpenSpaceLibraryName.c_str());
LDEBUG("Add OpenSpace modules");
for (const LuaLibrary& lib : _registeredLibraries) {
for (LuaLibrary& lib : _registeredLibraries) {
registerLuaLibrary(state, lib);
}
}
@@ -106,7 +106,8 @@ void ScriptEngine::addLibrary(LuaLibrary library) {
if (it == _registeredLibraries.end()) {
// If not, we can add it after we sorted it
std::sort(library.functions.begin(), library.functions.end(), sortFunc);
_registeredLibraries.insert(std::move(library));
_registeredLibraries.push_back(std::move(library));
std::sort(_registeredLibraries.begin(), _registeredLibraries.end());
}
else {
// otherwise, we merge the libraries
@@ -127,15 +128,21 @@ void ScriptEngine::addLibrary(LuaLibrary library) {
"' has been defined twice");
return;
}
else
else {
merged.functions.push_back(fun);
}
}
for (const std::string& script : library.scripts) {
merged.scripts.push_back(script);
}
_registeredLibraries.erase(it);
// Sort the merged library before inserting it
std::sort(merged.functions.begin(), merged.functions.end(), sortFunc);
_registeredLibraries.insert(std::move(merged));
_registeredLibraries.push_back(std::move(merged));
std::sort(_registeredLibraries.begin(), _registeredLibraries.end());
}
}
@@ -345,13 +352,11 @@ bool ScriptEngine::isLibraryNameAllowed(lua_State* state, const std::string& nam
return result;
}
void ScriptEngine::addLibraryFunctions(lua_State* state, const LuaLibrary& library, bool replace) {
assert(state);
void ScriptEngine::addLibraryFunctions(lua_State* state, LuaLibrary& library, bool replace) {
ghoul_assert(state, "State must not be nullptr");
for (LuaLibrary::Function p : library.functions) {
if (!replace) {
//ghoul::lua::logStack(_state);
lua_getfield(state, -1, p.name.c_str());
//ghoul::lua::logStack(_state);
const bool isNil = lua_isnil(state, -1);
if (!isNil) {
LERROR("Function name '" << p.name << "' was already assigned");
@@ -359,13 +364,49 @@ void ScriptEngine::addLibraryFunctions(lua_State* state, const LuaLibrary& libra
}
lua_pop(state, 1);
}
//ghoul::lua::logStack(_state);
lua_pushstring(state, p.name.c_str());
//ghoul::lua::logStack(_state);
lua_pushcfunction(state, p.function);
//ghoul::lua::logStack(_state);
lua_settable(state, TableOffset);
//ghoul::lua::logStack(_state);
}
for (const std::string& script : library.scripts) {
// First we run the script to set its values in the current state
ghoul::lua::runScriptFile(state, absPath(script));
library.documentations.clear();
// Then, we extract the documentation information from the file
lua_pushstring(state, "documentation");
lua_gettable(state, -2);
if (lua_isnil(state, -1)) {
LERROR(
"Module '" << library.name << "' did not provide a documentation in " <<
"script file '" << script << "'");
}
else {
lua_pushnil(state);
while (lua_next(state, -2)) {
lua_pushstring(state, "Name");
lua_gettable(state, -2);
const std::string name = lua_tostring(state, -1);
lua_pop(state, 1);
lua_pushstring(state, "Arguments");
lua_gettable(state, -2);
const std::string arguments = lua_tostring(state, -1);
lua_pop(state, 1);
lua_pushstring(state, "Documentation");
lua_gettable(state, -2);
const std::string documentation = lua_tostring(state, -1);
lua_pop(state, 1);
lua_pop(state, 1);
library.documentations.push_back({ name, arguments, documentation });
}
lua_pop(state, 1);
}
}
}
@@ -422,6 +463,12 @@ void ScriptEngine::addBaseLibrary() {
"Returns the absolute path to the passed path, resolving path tokens as "
"well as resolving relative paths"
},
{
"fileExists",
&luascriptfunctions::fileExists,
"string",
"Checks whether the provided file exists."
},
{
"setPathToken",
&luascriptfunctions::setPathToken,
@@ -476,42 +523,37 @@ void ScriptEngine::remapPrintFunction() {
//ghoul::lua::logStack(_state);
}
bool ScriptEngine::registerLuaLibrary(lua_State* state, const LuaLibrary& library) {
bool ScriptEngine::registerLuaLibrary(lua_State* state, LuaLibrary& library) {
ghoul_assert(state, "State must not be nullptr");
if (library.functions.empty()) {
LERROR("Lua library '" << library.name << "' does not have any functions");
return false;
}
//ghoul::lua::logStack(_state);
lua_getglobal(state, OpenSpaceLibraryName.c_str());
//ghoul::lua::logStack(_state);
if (library.name.empty()) {
//ghoul::lua::logStack(_state);
addLibraryFunctions(state, library, true);
//ghoul::lua::logStack(_state);
lua_pop(state, 1);
//ghoul::lua::logStack(_state);
}
else {
const bool allowed = isLibraryNameAllowed(state, library.name);
if (!allowed) {
return false;
}
//ghoul::lua::logStack(_state);
lua_pushstring(state, library.name.c_str());
//ghoul::lua::logStack(_state);
lua_newtable(state);
//ghoul::lua::logStack(_state);
addLibraryFunctions(state, library, false);
lua_settable(state, TableOffset);
//ghoul::lua::logStack(_state);
//_registeredLibraries.insert(library);
//_registeredLibraries.push_back(library);
// We need to first create the table and then retrieve it as the table will
// probably be used by scripts already
// Add the table
lua_pushstring(state, library.name.c_str());
lua_newtable(state);
lua_settable(state, TableOffset);
// Retrieve the table
lua_pushstring(state, library.name.c_str());
lua_gettable(state, -2);
// Add the library functions into the table
addLibraryFunctions(state, library, false);
// Pop the table
lua_pop(state, 1);
}
return true;
}
@@ -522,11 +564,21 @@ std::vector<std::string> ScriptEngine::allLuaFunctions() const {
for (const LuaLibrary& library : _registeredLibraries) {
for (const LuaLibrary::Function& function : library.functions) {
std::string total = "openspace.";
if (!library.name.empty())
if (!library.name.empty()) {
total += library.name + ".";
}
total += function.name;
result.push_back(std::move(total));
}
for (const LuaLibrary::Documentation& doc : library.documentations) {
std::string total = "openspace.";
if (!library.name.empty()) {
total += library.name + ".";
}
total += doc.name;
result.push_back(std::move(total));
}
}
return result;
@@ -554,10 +606,23 @@ std::string ScriptEngine::generateJson() const {
json << "\"arguments\": \"" << f.argumentText << "\", ";
json << "\"help\": \"" << f.helpText << "\"";
json << "}";
if (&f != &l.functions.back()) {
if (&f != &l.functions.back() || !l.documentations.empty()) {
json << ",";
}
}
for (const LuaLibrary::Documentation& doc : l.documentations) {
json << "{";
json << "\"name\": \"" << doc.name << "\", ";
json << "\"arguments\": \"" << doc.parameter<< "\", ";
json << "\"help\": \"" << doc.description<< "\"";
json << "}";
if (&doc != &l.documentations.back()) {
json << ",";
}
}
json << "]}";
}
@@ -624,15 +689,15 @@ bool ScriptEngine::writeLog(const std::string& script) {
}
void ScriptEngine::presync(bool isMaster) {
if (!isMaster) return;
if (!isMaster) {
return;
}
_mutex.lock();
if (!_queuedScripts.empty()) {
_currentSyncedScript = _queuedScripts.back().first;
bool remoteScripting = _queuedScripts.back().second;
//Not really a received script but the master also needs to run the script...
_receivedScripts.push_back(_currentSyncedScript);
_queuedScripts.pop_back();
@@ -640,11 +705,8 @@ void ScriptEngine::presync(bool isMaster) {
if (OsEng.parallelConnection().isHost() && remoteScripting) {
OsEng.parallelConnection().sendScript(_currentSyncedScript);
}
}
_mutex.unlock();
}
void ScriptEngine::encode(SyncBuffer* syncBuffer) {
@@ -682,13 +744,12 @@ void ScriptEngine::postsync(bool) {
}
void ScriptEngine::queueScript(const std::string &script, ScriptEngine::RemoteScripting remoteScripting){
if (script.empty())
if (script.empty()) {
return;
}
_mutex.lock();
_queuedScripts.insert(_queuedScripts.begin(), std::make_pair(script, remoteScripting));
_mutex.unlock();
}
+24
View File
@@ -226,6 +226,30 @@ int setPathToken(lua_State* L) {
return 0;
}
/**
* \ingroup LuaScripts
* walkDirectory(string, bool, bool):
* Walks a directory and returns the contents of the directory as absolute paths. The
* first argument is the path of the directory that should be walked, the second argument
* determines if the walk is recursive and will continue in contained directories. The
* default value for this parameter is "false". The third argument determines whether the
* table that is returned is sorted. The default value for this parameter is "false".
*/
int fileExists(lua_State* L) {
const int nArguments = lua_gettop(L);
if (nArguments != 1) {
return luaL_error(L, "Expected %i arguments, got %i", 1, nArguments);
}
const std::string file = luaL_checkstring(L, -1);
const bool e = FileSys.fileExists(absPath(file));
lua_pushboolean(
L,
e ? 1 : 0
);
return 1;
}
/**
* \ingroup LuaScripts
* walkDirectory(string, bool, bool):