mirror of
https://github.com/OpenSpace/OpenSpace.git
synced 2026-01-11 22:20:23 -06:00
747 lines
28 KiB
C++
747 lines
28 KiB
C++
/*****************************************************************************************
|
|
* *
|
|
* OpenSpace *
|
|
* *
|
|
* Copyright (c) 2014-2025 *
|
|
* *
|
|
* Permission is hereby granted, free of charge, to any person obtaining a copy of this *
|
|
* software and associated documentation files (the "Software"), to deal in the Software *
|
|
* without restriction, including without limitation the rights to use, copy, modify, *
|
|
* merge, publish, distribute, sublicense, and/or sell copies of the Software, and to *
|
|
* permit persons to whom the Software is furnished to do so, subject to the following *
|
|
* conditions: *
|
|
* *
|
|
* The above copyright notice and this permission notice shall be included in all copies *
|
|
* or substantial portions of the Software. *
|
|
* *
|
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, *
|
|
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A *
|
|
* PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT *
|
|
* HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF *
|
|
* CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE *
|
|
* OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. *
|
|
****************************************************************************************/
|
|
|
|
#include <openspace/documentation/documentationengine.h>
|
|
|
|
#include <openspace/openspace.h>
|
|
#include <openspace/documentation/core_registration.h>
|
|
#include <openspace/documentation/verifier.h>
|
|
#include <openspace/engine/openspaceengine.h>
|
|
#include <openspace/engine/globals.h>
|
|
#include <openspace/engine/configuration.h>
|
|
#include <openspace/events/eventengine.h>
|
|
#include <openspace/interaction/action.h>
|
|
#include <openspace/interaction/actionmanager.h>
|
|
#include <openspace/interaction/keybindingmanager.h>
|
|
#include <openspace/json.h>
|
|
#include <openspace/rendering/renderengine.h>
|
|
#include <openspace/scene/asset.h>
|
|
#include <openspace/scene/assetmanager.h>
|
|
#include <openspace/scene/scene.h>
|
|
#include <openspace/scripting/scriptscheduler.h>
|
|
#include <openspace/scripting/scriptengine.h>
|
|
#include <openspace/util/factorymanager.h>
|
|
#include <openspace/util/json_helper.h>
|
|
#include <ghoul/filesystem/filesystem.h>
|
|
#include <ghoul/format.h>
|
|
#include <ghoul/logging/logmanager.h>
|
|
#include <ghoul/misc/profiling.h>
|
|
#include <ghoul/misc/stringhelper.h>
|
|
#include <fstream>
|
|
#include <future>
|
|
|
|
namespace {
|
|
constexpr std::string_view _loggerCat = "DocumentationEngine";
|
|
|
|
// General keys
|
|
constexpr const char* NameKey = "name";
|
|
constexpr const char* IdentifierKey = "identifier";
|
|
constexpr const char* DescriptionKey = "description";
|
|
constexpr const char* DataKey = "data";
|
|
constexpr const char* TypeKey = "type";
|
|
constexpr const char* DocumentationKey = "documentation";
|
|
constexpr const char* ActionKey = "action";
|
|
constexpr const char* IdKey = "id";
|
|
|
|
// Actions
|
|
constexpr const char* ActionTitle = "Actions";
|
|
constexpr const char* GuiNameKey = "guiName";
|
|
constexpr const char* CommandKey = "command";
|
|
constexpr const char* ColorKey = "color";
|
|
constexpr const char* TextColorKey = "textColor";
|
|
|
|
// Factory
|
|
constexpr const char* MembersKey = "members";
|
|
constexpr const char* OptionalKey = "optional";
|
|
constexpr const char* ReferenceKey = "reference";
|
|
constexpr const char* FoundKey = "found";
|
|
constexpr const char* ClassesKey = "classes";
|
|
|
|
constexpr const char* OtherName = "Other";
|
|
constexpr const char* OtherIdentifierName = "other";
|
|
constexpr const char* propertyOwnerName = "propertyOwner";
|
|
constexpr const char* categoryName = "category";
|
|
|
|
// Properties
|
|
constexpr const char* SettingsTitle = "Settings";
|
|
constexpr const char* SceneTitle = "Scene";
|
|
constexpr const char* PropertiesKeys = "properties";
|
|
constexpr const char* PropertyOwnersKey = "propertyOwners";
|
|
constexpr const char* TagsKey = "tags";
|
|
constexpr const char* UriKey = "uri";
|
|
|
|
// Scripting
|
|
constexpr const char* DefaultValueKey = "defaultValue";
|
|
constexpr const char* ArgumentsKey = "arguments";
|
|
constexpr const char* ReturnTypeKey = "returnType";
|
|
constexpr const char* HelpKey = "help";
|
|
constexpr const char* FileKey = "file";
|
|
constexpr const char* LineKey = "line";
|
|
constexpr const char* LibraryKey = "library";
|
|
constexpr const char* FullNameKey = "fullName";
|
|
constexpr const char* FunctionsKey = "functions";
|
|
constexpr const char* SourceLocationKey = "sourceLocation";
|
|
constexpr const char* OpenSpaceScriptingKey = "openspace";
|
|
|
|
// Licenses
|
|
constexpr const char* LicensesTitle = "Licenses";
|
|
constexpr const char* ProfileName = "Profile";
|
|
constexpr const char* AssetsName = "Assets";
|
|
constexpr const char* LicensesName = "Licenses";
|
|
constexpr const char* NoLicenseName = "No License";
|
|
constexpr const char* NoDataName = "";
|
|
|
|
constexpr const char* ProfileNameKey = "profileName";
|
|
constexpr const char* VersionKey = "version";
|
|
constexpr const char* AuthorKey = "author";
|
|
constexpr const char* UrlKey = "url";
|
|
constexpr const char* LicenseKey = "license";
|
|
constexpr const char* IdentifiersKey = "identifiers";
|
|
constexpr const char* PathKey = "path";
|
|
constexpr const char* AssetKey = "assets";
|
|
constexpr const char* LicensesKey = "licenses";
|
|
|
|
// Keybindings
|
|
constexpr const char* KeybindingsTitle = "Keybindings";
|
|
constexpr const char* KeybindingsKey = "keybindings";
|
|
|
|
// Events
|
|
constexpr const char* EventsTitle = "Events";
|
|
constexpr const char* FiltersKey = "filters";
|
|
constexpr const char* ActionsKey = "actions";
|
|
|
|
nlohmann::json documentationToJson(
|
|
const openspace::documentation::Documentation& documentation)
|
|
{
|
|
using namespace openspace::documentation;
|
|
|
|
nlohmann::json json;
|
|
|
|
json[NameKey] = documentation.name;
|
|
json[IdentifierKey] = documentation.id;
|
|
json[DescriptionKey] = documentation.description;
|
|
json[MembersKey] = nlohmann::json::array();
|
|
|
|
for (const DocumentationEntry& p : documentation.entries) {
|
|
nlohmann::json entry;
|
|
entry[NameKey] = p.key;
|
|
entry[OptionalKey] = p.optional.value;
|
|
entry[TypeKey] = p.verifier->type();
|
|
entry[DocumentationKey] = p.documentation;
|
|
|
|
auto* tv = dynamic_cast<TableVerifier*>(p.verifier.get());
|
|
auto* rv = dynamic_cast<ReferencingVerifier*>(p.verifier.get());
|
|
|
|
if (rv) {
|
|
const std::vector<Documentation>& doc = DocEng.documentations();
|
|
auto it = std::find_if(
|
|
doc.begin(),
|
|
doc.end(),
|
|
[rv](const Documentation& d) { return d.id == rv->identifier; }
|
|
);
|
|
|
|
if (it == doc.end()) {
|
|
entry[ReferenceKey][FoundKey] = false;
|
|
}
|
|
else {
|
|
nlohmann::json reference;
|
|
reference[FoundKey] = true;
|
|
reference[NameKey] = it->name;
|
|
reference[IdentifierKey] = rv->identifier;
|
|
|
|
entry[ReferenceKey] = reference;
|
|
}
|
|
}
|
|
else if (tv) {
|
|
Documentation doc = { .entries = tv->documentations };
|
|
|
|
// Since this is a table we need to recurse this function to extract data
|
|
nlohmann::json tableDocs = documentationToJson(doc);
|
|
|
|
// Set the members entry to the members of the table
|
|
// to remove unnecessary nestling
|
|
entry[MembersKey] = tableDocs[MembersKey];
|
|
}
|
|
else {
|
|
entry[DescriptionKey] = p.verifier->documentation();
|
|
}
|
|
json[MembersKey].push_back(entry);
|
|
}
|
|
openspace::sortJson(json[MembersKey], NameKey);
|
|
|
|
return json;
|
|
}
|
|
|
|
nlohmann::json propertyOwnerToJson(openspace::properties::PropertyOwner* owner) {
|
|
ZoneScoped;
|
|
|
|
using namespace openspace;
|
|
nlohmann::json json;
|
|
json[NameKey] =
|
|
!owner->guiName().empty() ? owner->guiName() : owner->identifier();
|
|
|
|
json[DescriptionKey] = owner->description();
|
|
json[PropertiesKeys] = nlohmann::json::array();
|
|
json[PropertyOwnersKey] = nlohmann::json::array();
|
|
json[TypeKey] = owner->type();
|
|
json[TagsKey] = owner->tags();
|
|
|
|
for (properties::Property* p : owner->properties()) {
|
|
nlohmann::json propertyJson;
|
|
std::string name = !p->guiName().empty() ? p->guiName() : p->identifier();
|
|
propertyJson[NameKey] = name;
|
|
propertyJson[TypeKey] = p->className();
|
|
propertyJson[UriKey] = p->uri();
|
|
propertyJson[IdentifierKey] = p->identifier();
|
|
propertyJson[DescriptionKey] = p->description();
|
|
|
|
json[PropertiesKeys].push_back(propertyJson);
|
|
}
|
|
sortJson(json[PropertiesKeys], NameKey);
|
|
|
|
for (properties::PropertyOwner* o : owner->propertySubOwners()) {
|
|
nlohmann::json propertyOwner;
|
|
json[PropertyOwnersKey].push_back(propertyOwnerToJson(o));
|
|
}
|
|
sortJson(json[PropertyOwnersKey], NameKey);
|
|
|
|
return json;
|
|
}
|
|
|
|
nlohmann::json luaFunctionToJson(const openspace::scripting::LuaLibrary::Function& f,
|
|
bool includeSourceLocation)
|
|
{
|
|
using namespace openspace::scripting;
|
|
|
|
nlohmann::json function;
|
|
function[NameKey] = f.name;
|
|
nlohmann::json arguments = nlohmann::json::array();
|
|
|
|
for (const LuaLibrary::Function::Argument& arg : f.arguments) {
|
|
nlohmann::json argument;
|
|
argument[NameKey] = arg.name;
|
|
argument[TypeKey] = arg.type;
|
|
argument[DefaultValueKey] = arg.defaultValue.value_or(NoDataName);
|
|
arguments.push_back(argument);
|
|
}
|
|
|
|
function[ArgumentsKey] = arguments;
|
|
function[ReturnTypeKey] = f.returnType;
|
|
|
|
// Remove all double whitespaces from the helptext (these may be generated when
|
|
// using multi-line strings in Lua)
|
|
std::string cleanedHelpText = f.helpText;
|
|
ghoul::trimWhitespace(cleanedHelpText);
|
|
std::size_t doubleSpace = cleanedHelpText.find(" ");
|
|
while (doubleSpace != std::string::npos) {
|
|
cleanedHelpText.erase(doubleSpace, 1);
|
|
doubleSpace = cleanedHelpText.find(" ");
|
|
}
|
|
|
|
function[HelpKey] = cleanedHelpText;
|
|
|
|
if (includeSourceLocation) {
|
|
nlohmann::json sourceLocation;
|
|
sourceLocation[FileKey] = f.sourceLocation.file;
|
|
sourceLocation[LineKey] = f.sourceLocation.line;
|
|
function[SourceLocationKey] = sourceLocation;
|
|
}
|
|
|
|
return function;
|
|
}
|
|
} // namespace
|
|
|
|
namespace openspace::documentation {
|
|
|
|
DocumentationEngine* DocumentationEngine::_instance = nullptr;
|
|
|
|
DocumentationEngine::DuplicateDocumentationException::DuplicateDocumentationException(
|
|
Documentation doc)
|
|
: ghoul::RuntimeError(std::format(
|
|
"Duplicate Documentation with name '{}' and id '{}'", doc.name, doc.id
|
|
))
|
|
, documentation(std::move(doc))
|
|
{}
|
|
|
|
DocumentationEngine::DocumentationEngine() {}
|
|
|
|
void DocumentationEngine::initialize() {
|
|
ghoul_assert(!isInitialized(), "DocumentationEngine is already initialized");
|
|
_instance = new DocumentationEngine;
|
|
}
|
|
|
|
void DocumentationEngine::deinitialize() {
|
|
ghoul_assert(isInitialized(), "DocumentationEngine is not initialized");
|
|
delete _instance;
|
|
_instance = nullptr;
|
|
}
|
|
|
|
bool DocumentationEngine::isInitialized() {
|
|
return _instance != nullptr;
|
|
}
|
|
|
|
DocumentationEngine& DocumentationEngine::ref() {
|
|
if (_instance == nullptr) {
|
|
_instance = new DocumentationEngine;
|
|
registerCoreClasses(*_instance);
|
|
}
|
|
return *_instance;
|
|
}
|
|
|
|
nlohmann::json DocumentationEngine::generateScriptEngineJson() const {
|
|
ZoneScoped;
|
|
|
|
using namespace openspace::scripting;
|
|
const std::vector<LuaLibrary> libraries = global::scriptEngine->allLuaLibraries();
|
|
nlohmann::json json;
|
|
|
|
for (const LuaLibrary& l : libraries) {
|
|
nlohmann::json library;
|
|
std::string libraryName = l.name;
|
|
// Keep the library key for backwards compatability
|
|
library[LibraryKey] = libraryName;
|
|
library[NameKey] = libraryName;
|
|
std::string os = OpenSpaceScriptingKey;
|
|
library[FullNameKey] = libraryName.empty() ? os : os + "." + libraryName;
|
|
|
|
for (const LuaLibrary::Function& f : l.functions) {
|
|
constexpr bool HasSourceLocation = true;
|
|
library[FunctionsKey].push_back(luaFunctionToJson(f, HasSourceLocation));
|
|
}
|
|
|
|
for (const LuaLibrary::Function& f : l.documentations) {
|
|
constexpr bool HasSourceLocation = false;
|
|
library[FunctionsKey].push_back(luaFunctionToJson(f, HasSourceLocation));
|
|
}
|
|
sortJson(library[FunctionsKey], NameKey);
|
|
json.push_back(library);
|
|
|
|
sortJson(json, LibraryKey);
|
|
}
|
|
return json;
|
|
}
|
|
|
|
nlohmann::json DocumentationEngine::generateLicenseGroupsJson() const {
|
|
nlohmann::json json;
|
|
|
|
if (global::profile->meta.has_value()) {
|
|
Profile::Meta meta = *global::profile->meta;
|
|
|
|
nlohmann::json metaJson;
|
|
metaJson[NameKey] = ProfileName;
|
|
metaJson[ProfileNameKey] = meta.name.value_or(NoDataName);
|
|
metaJson[VersionKey] = meta.version.value_or(NoDataName);
|
|
metaJson[DescriptionKey] = meta.description.value_or(NoDataName);
|
|
metaJson[AuthorKey] = meta.author.value_or(NoDataName);
|
|
metaJson[UrlKey] = meta.url.value_or(NoDataName);
|
|
metaJson[LicenseKey] = meta.license.value_or(NoDataName);
|
|
json.push_back(std::move(metaJson));
|
|
}
|
|
|
|
// Go through all assets and group them in a map with the key as the license name
|
|
std::vector<const Asset*> assets =
|
|
global::openSpaceEngine->assetManager().allAssets();
|
|
|
|
std::map<std::string, nlohmann::json> assetLicenses;
|
|
for (const Asset* asset : assets) {
|
|
std::optional<Asset::MetaInformation> meta = asset->metaInformation();
|
|
|
|
// Ensure the license is not going to be an empty string
|
|
std::string licenseName = NoLicenseName;
|
|
if (meta.has_value() && meta->license != NoDataName) {
|
|
licenseName = meta->license;
|
|
}
|
|
|
|
nlohmann::json assetJson;
|
|
assetJson[NameKey] = meta.has_value() ? meta->name : NoDataName;
|
|
assetJson[VersionKey] = meta.has_value() ? meta->version : NoDataName;
|
|
assetJson[DescriptionKey] = meta.has_value() ? meta->description : NoDataName;
|
|
assetJson[AuthorKey] = meta.has_value() ? meta->author : NoDataName;
|
|
assetJson[UrlKey] = meta.has_value() ? meta->url : NoDataName;
|
|
assetJson[LicenseKey] = licenseName;
|
|
assetJson[PathKey] = asset->path().string();
|
|
assetJson[IdKey] = asset->path().string();
|
|
assetJson[IdentifiersKey] = meta.has_value() ? meta->identifiers :
|
|
std::vector<std::string>();
|
|
|
|
assetLicenses[licenseName].push_back(assetJson);
|
|
}
|
|
|
|
nlohmann::json assetsJson;
|
|
assetsJson[NameKey] = AssetsName;
|
|
assetsJson[TypeKey] = LicensesName;
|
|
|
|
using K = std::string;
|
|
using V = nlohmann::json;
|
|
for (std::pair<const K, V>& assetLicense : assetLicenses) {
|
|
nlohmann::json entry;
|
|
entry[NameKey] = assetLicense.first;
|
|
entry[AssetKey] = std::move(assetLicense.second);
|
|
sortJson(entry[AssetKey], NameKey);
|
|
assetsJson[LicensesKey].push_back(entry);
|
|
}
|
|
json.push_back(assetsJson);
|
|
|
|
nlohmann::json result;
|
|
result[NameKey] = LicensesTitle;
|
|
result[DataKey] = json;
|
|
return result;
|
|
}
|
|
|
|
nlohmann::json DocumentationEngine::generateLicenseListJson() const {
|
|
nlohmann::json json;
|
|
|
|
if (global::profile->meta.has_value()) {
|
|
nlohmann::json profile;
|
|
profile[NameKey] = global::profile->meta->name.value_or(NoDataName);
|
|
profile[VersionKey] = global::profile->meta->version.value_or(NoDataName);
|
|
profile[DescriptionKey] = global::profile->meta->description.value_or(NoDataName);
|
|
profile[AuthorKey] = global::profile->meta->author.value_or(NoDataName);
|
|
profile[UrlKey] = global::profile->meta->url.value_or(NoDataName);
|
|
profile[LicenseKey] = global::profile->meta->license.value_or(NoDataName);
|
|
json.push_back(profile);
|
|
}
|
|
|
|
std::vector<const Asset*> assets =
|
|
global::openSpaceEngine->assetManager().allAssets();
|
|
|
|
for (const Asset* asset : assets) {
|
|
std::optional<Asset::MetaInformation> meta = asset->metaInformation();
|
|
|
|
if (!meta.has_value()) {
|
|
continue;
|
|
}
|
|
|
|
nlohmann::json assetJson;
|
|
assetJson[NameKey] = meta->name;
|
|
assetJson[VersionKey] = meta->version;
|
|
assetJson[DescriptionKey] = meta->description;
|
|
assetJson[AuthorKey] = meta->author;
|
|
assetJson[UrlKey] = meta->url;
|
|
assetJson[LicenseKey] = meta->license;
|
|
assetJson[IdentifiersKey] = meta->identifiers;
|
|
assetJson[PathKey] = asset->path().string();
|
|
json.push_back(assetJson);
|
|
}
|
|
return json;
|
|
}
|
|
|
|
nlohmann::json DocumentationEngine::generateEventJson() const {
|
|
using Type = events::Event::Type;
|
|
const std::unordered_map<Type, std::vector<EventEngine::ActionInfo>>& eventActions =
|
|
global::eventEngine->eventActions();
|
|
nlohmann::json events;
|
|
|
|
nlohmann::json data = nlohmann::json::array();
|
|
|
|
// Group actions by events
|
|
for (const auto& [eventType, actions] : eventActions) {
|
|
nlohmann::json eventJson;
|
|
|
|
eventJson[NameKey] = std::string(events::toString(eventType));
|
|
nlohmann::json actionsJson = nlohmann::json::array();
|
|
|
|
for (const EventEngine::ActionInfo& action : actions) {
|
|
nlohmann::json actionJson;
|
|
actionJson[NameKey] = eventJson[NameKey];
|
|
actionJson[ActionKey] = action.action;
|
|
// Create a unique ID
|
|
actionJson[IdKey] = std::format("{}{}", action.action, action.id);
|
|
|
|
// Output filters as a string
|
|
if (action.filter.has_value()) {
|
|
ghoul::Dictionary filters = action.filter.value();
|
|
std::vector<std::string_view> keys = filters.keys();
|
|
nlohmann::json filtersJson = nlohmann::json::array();
|
|
|
|
std::string filtersString = "";
|
|
for (std::string_view key : keys) {
|
|
std::string value = filters.value<std::string>(key);
|
|
filtersString += std::format("{} = {}, ", key, value);
|
|
}
|
|
filtersString.pop_back(); // Remove last space from last entry
|
|
filtersString.pop_back(); // Remove last comma from last entry
|
|
|
|
actionJson[FiltersKey] = filtersString;
|
|
|
|
}
|
|
actionsJson.push_back(actionJson);
|
|
}
|
|
eventJson[ActionsKey] = actionsJson;
|
|
data.push_back(eventJson);
|
|
}
|
|
|
|
// Format resulting json
|
|
nlohmann::json result;
|
|
result[NameKey] = EventsTitle;
|
|
result[DataKey] = data;
|
|
return result;
|
|
}
|
|
|
|
nlohmann::json DocumentationEngine::generateFactoryManagerJson() const {
|
|
nlohmann::json json;
|
|
|
|
std::vector<Documentation> docs = _documentations; // Copy the documentations
|
|
const std::vector<FactoryManager::FactoryInfo>& factories =
|
|
FactoryManager::ref().factories();
|
|
|
|
for (const FactoryManager::FactoryInfo& factoryInfo : factories) {
|
|
if (factoryInfo.name == "") {
|
|
LERROR("Factory documentation without identifier");
|
|
continue;
|
|
}
|
|
nlohmann::json factory;
|
|
factory[NameKey] = factoryInfo.name;
|
|
factory[IdentifierKey] = categoryName + factoryInfo.name;
|
|
|
|
ghoul::TemplateFactoryBase* f = factoryInfo.factory.get();
|
|
// Add documentation about base class
|
|
auto factoryDoc = std::find_if(
|
|
docs.begin(),
|
|
docs.end(),
|
|
[&factoryInfo](const Documentation& d) { return d.name == factoryInfo.name; }
|
|
);
|
|
if (factoryDoc != docs.end()) {
|
|
nlohmann::json documentation = documentationToJson(*factoryDoc);
|
|
factory[ClassesKey].push_back(documentation);
|
|
// Remove documentation from list check at the end if all docs got put in
|
|
docs.erase(factoryDoc);
|
|
}
|
|
else {
|
|
nlohmann::json documentation;
|
|
documentation[NameKey] = factoryInfo.name;
|
|
documentation[IdentifierKey] = factoryInfo.name;
|
|
documentation[MembersKey] = nlohmann::json::array();
|
|
factory[ClassesKey].push_back(documentation);
|
|
}
|
|
|
|
// Add documentation about derived classes
|
|
const std::vector<std::string>& registeredClasses = f->registeredClasses();
|
|
for (const std::string& c : registeredClasses) {
|
|
if (c == "") {
|
|
LERROR("Factory documentation, derived class, without identifier");
|
|
continue;
|
|
}
|
|
auto found = std::find_if(
|
|
docs.begin(),
|
|
docs.end(),
|
|
[&c](const Documentation& d) { return d.name == c; }
|
|
);
|
|
if (found != docs.end()) {
|
|
nlohmann::json documentation = documentationToJson(*found);
|
|
factory[ClassesKey].push_back(documentation);
|
|
docs.erase(found);
|
|
}
|
|
else {
|
|
nlohmann::json documentation;
|
|
documentation[NameKey] = c;
|
|
documentation[IdentifierKey] = c;
|
|
documentation[MembersKey] = nlohmann::json::array();
|
|
factory[ClassesKey].push_back(documentation);
|
|
}
|
|
}
|
|
sortJson(factory[ClassesKey], NameKey);
|
|
json.push_back(factory);
|
|
}
|
|
// Add all leftover docs
|
|
nlohmann::json leftovers;
|
|
leftovers[NameKey] = OtherName;
|
|
leftovers[IdentifierKey] = OtherIdentifierName;
|
|
|
|
for (const Documentation& doc : docs) {
|
|
if (doc.id == "") {
|
|
LERROR("Documentation without identifier");
|
|
continue;
|
|
}
|
|
leftovers[ClassesKey].push_back(documentationToJson(doc));
|
|
}
|
|
sortJson(leftovers[ClassesKey], NameKey);
|
|
json.push_back(leftovers);
|
|
sortJson(json, NameKey);
|
|
|
|
return json;
|
|
}
|
|
|
|
nlohmann::json DocumentationEngine::generateKeybindingsJson() const {
|
|
ZoneScoped;
|
|
|
|
nlohmann::json json;
|
|
const std::multimap<KeyWithModifier, std::string>& luaKeys =
|
|
global::keybindingManager->keyBindings();
|
|
|
|
for (const std::pair<const KeyWithModifier, std::string>& p : luaKeys) {
|
|
nlohmann::json keybind;
|
|
keybind[NameKey] = ghoul::to_string(p.first);
|
|
keybind[ActionKey] = p.second;
|
|
json.push_back(std::move(keybind));
|
|
}
|
|
sortJson(json, NameKey);
|
|
|
|
nlohmann::json result;
|
|
result[NameKey] = KeybindingsTitle;
|
|
result[KeybindingsKey] = json;
|
|
return result;
|
|
}
|
|
|
|
nlohmann::json DocumentationEngine::generatePropertyOwnerJson(
|
|
properties::PropertyOwner* owner) const
|
|
{
|
|
ZoneScoped;
|
|
|
|
ghoul_assert(owner, "Owner must not be nullptr");
|
|
|
|
nlohmann::json json;
|
|
std::vector<properties::PropertyOwner*> subOwners = owner->propertySubOwners();
|
|
for (properties::PropertyOwner* o : subOwners) {
|
|
if (o->identifier() != SceneTitle) {
|
|
nlohmann::json jsonOwner = propertyOwnerToJson(o);
|
|
|
|
json.push_back(jsonOwner);
|
|
}
|
|
}
|
|
sortJson(json, NameKey);
|
|
|
|
nlohmann::json result;
|
|
result[NameKey] = propertyOwnerName;
|
|
result[DataKey] = json;
|
|
|
|
return result;
|
|
}
|
|
|
|
void DocumentationEngine::writeJavascriptDocumentation() const {
|
|
ZoneScoped;
|
|
|
|
// Write documentation to json files if config file supplies path for doc files
|
|
if (global::configuration->documentation.path.empty()) {
|
|
// if path was empty, that means that no documentation is requested
|
|
return;
|
|
}
|
|
|
|
// Start the async requests as soon as possible so they are finished when we need them
|
|
std::future<nlohmann::json> settings = std::async(
|
|
&DocumentationEngine::generatePropertyOwnerJson,
|
|
this,
|
|
global::rootPropertyOwner
|
|
);
|
|
|
|
std::future<nlohmann::json> sceneJson = std::async(
|
|
&DocumentationEngine::generatePropertyOwnerJson,
|
|
this,
|
|
global::renderEngine->scene()
|
|
);
|
|
|
|
nlohmann::json keybindings = generateKeybindingsJson();
|
|
nlohmann::json license = generateLicenseGroupsJson();
|
|
nlohmann::json sceneProperties = settings.get();
|
|
nlohmann::json sceneGraph = sceneJson.get();
|
|
nlohmann::json actions = generateActionJson();
|
|
nlohmann::json events = generateEventJson();
|
|
|
|
sceneProperties[NameKey] = SettingsTitle;
|
|
sceneGraph[NameKey] = SceneTitle;
|
|
|
|
nlohmann::json documentation = {
|
|
sceneGraph, sceneProperties, actions, events, keybindings, license
|
|
};
|
|
|
|
nlohmann::json result;
|
|
result[DocumentationKey] = documentation;
|
|
|
|
// Make into a javascript variable so that it is possible to open with static html
|
|
std::ofstream out = std::ofstream(absPath("${DOCUMENTATION}/documentationData.js"));
|
|
out << "var data = " << result.dump();
|
|
out.close();
|
|
}
|
|
|
|
void DocumentationEngine::writeJsonDocumentation() const {
|
|
nlohmann::json factory = generateFactoryManagerJson();
|
|
nlohmann::json scripting = generateScriptEngineJson();
|
|
|
|
// Write two json files for the static docs page - asset components and scripting api
|
|
std::ofstream out = std::ofstream(absPath("${DOCUMENTATION}/assetComponents.json"));
|
|
if (out) {
|
|
out << factory.dump();
|
|
}
|
|
out.close();
|
|
|
|
out.open(absPath("${DOCUMENTATION}/scriptingApi.json"));
|
|
if (out) {
|
|
out << scripting.dump();
|
|
}
|
|
out.close();
|
|
}
|
|
|
|
nlohmann::json DocumentationEngine::generateActionJson() const {
|
|
using namespace interaction;
|
|
|
|
nlohmann::json res;
|
|
res[NameKey] = ActionTitle;
|
|
res[DataKey] = nlohmann::json::array();
|
|
std::vector<Action> actions = global::actionManager->actions();
|
|
|
|
for (const Action& action : actions) {
|
|
nlohmann::json d;
|
|
// Use identifier as name to make it more similar to scripting api
|
|
d[NameKey] = action.identifier;
|
|
d[GuiNameKey] = action.name;
|
|
d[DocumentationKey] = action.documentation;
|
|
d[CommandKey] = action.command;
|
|
if (action.color.has_value()) {
|
|
d[ColorKey] = std::format("{}", action.color);
|
|
}
|
|
if (action.textColor.has_value()) {
|
|
d[TextColorKey] = std::format("{}", action.textColor);
|
|
}
|
|
res[DataKey].push_back(d);
|
|
}
|
|
sortJson(res[DataKey], NameKey);
|
|
return res;
|
|
}
|
|
|
|
|
|
void DocumentationEngine::addDocumentation(Documentation documentation) {
|
|
if (documentation.id.empty()) {
|
|
_documentations.push_back(std::move(documentation));
|
|
}
|
|
else {
|
|
auto it = std::find_if(
|
|
_documentations.begin(),
|
|
_documentations.end(),
|
|
[documentation](const Documentation& d) { return documentation.id == d.id; }
|
|
);
|
|
|
|
if (it != _documentations.end()) {
|
|
throw DuplicateDocumentationException(std::move(documentation));
|
|
}
|
|
else {
|
|
_documentations.push_back(std::move(documentation));
|
|
}
|
|
}
|
|
}
|
|
|
|
std::vector<Documentation> DocumentationEngine::documentations() const {
|
|
return _documentations;
|
|
}
|
|
} // namespace openspace::documentation
|