diff --git a/include/openspace/documentation/documentationgenerator.h b/include/openspace/documentation/documentationgenerator.h index ca91bc9eff..10c6d7ad91 100644 --- a/include/openspace/documentation/documentationgenerator.h +++ b/include/openspace/documentation/documentationgenerator.h @@ -100,6 +100,15 @@ private: const std::string _javascriptFile; }; + +/** + * This function takes a \p text and escapes all necessary characters () that JSON + * does not want in its strings. + * \param text The text that is to be escaped + * \return The same text will all required characteres escaped + */ +std::string escapedJson(const std::string& text); + } // namespace openspace #endif // __OPENSPACE_CORE___DOCUMENTATIONGENERATOR___H__ diff --git a/src/documentation/documentationengine.cpp b/src/documentation/documentationengine.cpp index 66ef92f7f0..5d41d4d3d8 100644 --- a/src/documentation/documentationengine.cpp +++ b/src/documentation/documentationengine.cpp @@ -156,7 +156,7 @@ std::string generateJsonDocumentation(const Documentation& d) { result << "\"key\": \"" << p.key << "\","; result << "\"optional\": " << (p.optional ? "true" : "false") << ","; result << "\"type\": \"" << p.verifier->type() << "\","; - result << "\"documentation\": \"" << p.documentation << "\","; + result << "\"documentation\": \"" << escapedJson(p.documentation) << "\","; TableVerifier* tv = dynamic_cast(p.verifier.get()); ReferencingVerifier* rv = dynamic_cast(p.verifier.get()); @@ -210,7 +210,7 @@ std::string generateHtmlDocumentation(const Documentation& d) { << "\t\t\n" << "\t\t" << p.key << "\n" << "\t\t" << (p.optional ? "Optional" : "Required") << "\n" - << "\t\t" << p.documentation << "\n" + << "\t\t" << escapedJson(p.documentation) << "\n" << "\t\t" << p.verifier->type() << "\n"; TableVerifier* tv = dynamic_cast(p.verifier.get()); @@ -285,11 +285,12 @@ std::string DocumentationEngine::generateJson() const { std::string jsonString = ""; for (const char& c : json.str()) { - if (c == '\'') { - jsonString += "\\'"; - } - else { - jsonString += c; + switch (c) { + case '\'': + jsonString += "\\'"; + break; + default: + jsonString += c; } } @@ -297,26 +298,6 @@ std::string DocumentationEngine::generateJson() const { } void DocumentationEngine::addDocumentation(Documentation doc) { - std::function testDocumentation = - [&testDocumentation](const DocumentationEntry& e) { - (void)e; // Unused variable in Release mode - ghoul_assert( - e.documentation.find('"') == std::string::npos, - fmt::format("Documentation cannot contain \" character. {} does", e.key) - ); - - TableVerifier* v = dynamic_cast(e.verifier.get()); - if (v) { - for (const DocumentationEntry& e : v->documentations) { - testDocumentation(e); - } - } - }; - - for (const DocumentationEntry& e : doc.entries) { - testDocumentation(e); - } - if (doc.id.empty()) { _documentations.push_back(std::move(doc)); } diff --git a/src/documentation/documentationgenerator.cpp b/src/documentation/documentationgenerator.cpp index 3f11f02563..96eb80d1f0 100644 --- a/src/documentation/documentationgenerator.cpp +++ b/src/documentation/documentationgenerator.cpp @@ -150,4 +150,28 @@ void DocumentationGenerator::writeDocumentation(const std::string& filename) { << "" << '\n'; } +std::string escapedJson(const std::string& text) { + std::string jsonString = ""; + for (const char& c : text) { + switch (c) { + case '\t': + jsonString += "\\t"; + break; + case '"': + // The " character has to be double escaped as JSON.parse will remove a single + // escape character, thus leaving only " behind that breaks the string + jsonString += "\\\\\""; + break; + case '\\': + jsonString += "\\\\"; + break; + default: + jsonString += c; + } + } + + return jsonString; +} + + } // namespace openspace diff --git a/src/engine/configurationmanager_doc.inl b/src/engine/configurationmanager_doc.inl index 2d181ae301..96da8110e5 100644 --- a/src/engine/configurationmanager_doc.inl +++ b/src/engine/configurationmanager_doc.inl @@ -89,14 +89,14 @@ documentation::Documentation ConfigurationManager::Documentation() { { ConfigurationManager::PartLogDir, new StringVerifier, - "The directory for logs. Default value is '${BASE_PATH}'", + "The directory for logs. Default value is \"${BASE_PATH}\"", Optional::Yes }, { ConfigurationManager::PartLogPerformancePrefix, new StringVerifier, "A string to prefix PerformanceMeasurement logfiles." - "Default value is 'PM-'", + "Default value is \"PM-\"", Optional::Yes }, { diff --git a/src/interaction/keybindingmanager.cpp b/src/interaction/keybindingmanager.cpp index 2f3db8e2bc..9756791af6 100644 --- a/src/interaction/keybindingmanager.cpp +++ b/src/interaction/keybindingmanager.cpp @@ -124,7 +124,7 @@ std::string KeyBindingManager::generateJson() const { json << "\"key\": \"" << std::to_string(p.first) << "\","; json << "\"script\": \"" << p.second.command << "\","; json << "\"remoteScripting\": " << (p.second.synchronization ? "true," : "false,"); - json << "\"documentation\": \"" << p.second.documentation << "\""; + json << "\"documentation\": \"" << escapedJson(p.second.documentation) << "\""; json << "}"; } json << "]"; diff --git a/src/scripting/scriptengine.cpp b/src/scripting/scriptengine.cpp index f81e9d7f2c..b2520f8707 100644 --- a/src/scripting/scriptengine.cpp +++ b/src/scripting/scriptengine.cpp @@ -603,8 +603,8 @@ std::string ScriptEngine::generateJson() const { for (const LuaLibrary::Function& f : l.functions) { json << "{"; json << "\"name\": \"" << f.name << "\", "; - json << "\"arguments\": \"" << f.argumentText << "\", "; - json << "\"help\": \"" << f.helpText << "\""; + json << "\"arguments\": \"" << escapedJson(f.argumentText) << "\", "; + json << "\"help\": \"" << escapedJson(f.helpText) << "\""; json << "}"; if (&f != &l.functions.back() || !l.documentations.empty()) { json << ","; @@ -614,8 +614,8 @@ std::string ScriptEngine::generateJson() const { for (const LuaLibrary::Documentation& doc : l.documentations) { json << "{"; json << "\"name\": \"" << doc.name << "\", "; - json << "\"arguments\": \"" << doc.parameter<< "\", "; - json << "\"help\": \"" << doc.description<< "\""; + json << "\"arguments\": \"" << escapedJson(doc.parameter) << "\", "; + json << "\"help\": \"" << escapedJson(doc.description) << "\""; json << "}"; if (&doc != &l.documentations.back()) { json << ",";