diff --git a/.gitignore b/.gitignore index b8fb45e994..bf9732efea 100644 --- a/.gitignore +++ b/.gitignore @@ -27,3 +27,4 @@ html/ latex/ shaders/ABuffer/constants.hglsl *.OpenSpaceGenerated.glsl +LuaScripting.txt diff --git a/include/openspace/scripting/scriptengine.h b/include/openspace/scripting/scriptengine.h index 5d5a76eead..4fb8671917 100644 --- a/include/openspace/scripting/scriptengine.h +++ b/include/openspace/scripting/scriptengine.h @@ -57,12 +57,13 @@ public: void initializeLuaState(lua_State* state); - void addLibrary(const LuaLibrary& library); + void addLibrary(LuaLibrary library); bool hasLibrary(const std::string& name); bool runScript(const std::string& script); bool runScriptFile(const std::string& filename); + bool writeDocumentation(const std::string& filename, const std::string& type) const; private: bool registerLuaLibrary(lua_State* state, const LuaLibrary& library); diff --git a/include/openspace/util/constants.h b/include/openspace/util/constants.h index 92c1ce7ee3..b22a100b55 100644 --- a/include/openspace/util/constants.h +++ b/include/openspace/util/constants.h @@ -42,6 +42,8 @@ namespace configurationmanager { const std::string keyCachePath = keyPaths + "." + keyCache; const std::string keyFonts = "Fonts"; const std::string keyConfigSgct = "SGCTConfig"; + const std::string keyLuaDocumentationType = "LuaDocumentationFile.Type"; + const std::string keyLuaDocumentationFile = "LuaDocumentationFile.File"; const std::string keyConfigScene = "Scene"; const std::string keyStartupScript = "StartupScripts"; const std::string keySpiceTimeKernel = "SpiceKernel.Time"; diff --git a/openspace.cfg b/openspace.cfg index 66d687f9fd..490f8d53ac 100644 --- a/openspace.cfg +++ b/openspace.cfg @@ -26,5 +26,9 @@ return { Scene = "${OPENSPACE_DATA}/scene/default.scene", StartupScripts = { "${SCRIPTS}/default_startup.lua" + }, + LuaDocumentationFile = { + Type = "text", + File = "${BASE_PATH}/LuaScripting.txt" } } \ No newline at end of file diff --git a/src/engine/openspaceengine.cpp b/src/engine/openspaceengine.cpp index 8b7ad33e88..509b924e80 100644 --- a/src/engine/openspaceengine.cpp +++ b/src/engine/openspaceengine.cpp @@ -344,6 +344,22 @@ bool OpenSpaceEngine::initialize() { // TODO: Maybe move all scenegraph and renderengine stuff to initializeGL scriptEngine().initialize(); + // If a LuaDocumentationFile was specified, generate it now + using constants::configurationmanager::keyLuaDocumentationType; + using constants::configurationmanager::keyLuaDocumentationFile; + const bool hasType = configurationManager().hasKey(keyLuaDocumentationType); + const bool hasFile = configurationManager().hasKey(keyLuaDocumentationFile); + if (hasType && hasFile) { + std::string luaDocumentationType; + configurationManager().getValue(keyLuaDocumentationType, luaDocumentationType); + std::string luaDocumentationFile; + configurationManager().getValue(keyLuaDocumentationFile, luaDocumentationFile); + + luaDocumentationFile = absPath(luaDocumentationFile); + _scriptEngine.writeDocumentation(luaDocumentationFile, luaDocumentationType); + } + + // Load scenegraph SceneGraph* sceneGraph = new SceneGraph; _renderEngine.setSceneGraph(sceneGraph); diff --git a/src/scripting/scriptengine.cpp b/src/scripting/scriptengine.cpp index 1fcfecc4aa..279285dd23 100644 --- a/src/scripting/scriptengine.cpp +++ b/src/scripting/scriptengine.cpp @@ -29,6 +29,7 @@ #include #include +#include namespace openspace { @@ -208,14 +209,21 @@ void ScriptEngine::deinitialize() { _state = nullptr; } -void ScriptEngine::addLibrary(const ScriptEngine::LuaLibrary& library) { +void ScriptEngine::addLibrary(LuaLibrary library) { + auto sortFunc = [](const LuaLibrary::Function& lhs, const LuaLibrary::Function& rhs) + { + return lhs.name < rhs.name; + }; + // do we have a library with the same name as the incoming one auto it = std::find_if(_registeredLibraries.begin(), _registeredLibraries.end(), [&library](const LuaLibrary& lib) { return lib.name == library.name; }); - if (it == _registeredLibraries.end()) - // If not, we can add it - _registeredLibraries.insert(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)); + } else { // otherwise, we merge the libraries @@ -235,7 +243,10 @@ void ScriptEngine::addLibrary(const ScriptEngine::LuaLibrary& library) { } _registeredLibraries.erase(it); - _registeredLibraries.insert(merged); + + // Sort the merged library before inserting it + std::sort(merged.functions.begin(), merged.functions.end(), sortFunc); + _registeredLibraries.insert(std::move(merged)); } } @@ -475,6 +486,55 @@ bool ScriptEngine::registerLuaLibrary(lua_State* state, const LuaLibrary& librar return true; } +bool ScriptEngine::writeDocumentation(const std::string& filename, const std::string& type) const { + if (type == "text") { + // The additional space between the longest function name and the descriptions + const size_t AdditionalSpace = 5; + LDEBUG("Writing Lua documentation of type '" << type << + "' to file '" << filename << "'"); + std::ofstream file(filename); + if (file.good()) { + + auto concatenate = [](std::string library, std::string function) { + std::string total = "openspace."; + if (!library.empty()) + total += std::move(library) + "."; + total += std::move(function); + return std::move(total); + }; + // First iterate over all libraries and functions to find the longest + // combination so that can be used as the 'width' parameter for the output + // stream + size_t maxLength = 0; + for (auto library : _registeredLibraries) { + for (auto function : library.functions) { + std::string functionName = concatenate(library.name, function.name); + maxLength = std::max(maxLength, functionName.size()); + } + } + maxLength += AdditionalSpace; + + // Now write out the functions + for (auto library : _registeredLibraries) { + for (auto function : library.functions) { + std::string functionName = concatenate(library.name, function.name); + file << std::setw(maxLength) << std::left << functionName; + file << std::setw(0) << function.helpText << std::endl; + } + } + return true; + } + else { + LERROR("Could not open file '" << filename << "' for writing documentation"); + return false; + } + } + else { + LERROR("Undefined type '" << type << "' for Lua documentation"); + return false; + } +} + } // namespace scripting } // namespace openspace