#include "doc_html.h" namespace scripting { namespace { void ade_output_toc(FILE* fp, const std::unique_ptr& el) { Assert(fp != nullptr); Assert(el != nullptr); // WMC - sanity checking if (el->name.empty() && el->shortName.empty()) { Warning(LOCATION, "Found ade_table_entry with no name or shortname"); return; } fputs("
", fp); if (el->name.empty()) { fprintf(fp, "%s", el->shortName.c_str(), el->shortName.c_str()); } else { fprintf(fp, "%s", el->name.c_str(), el->name.c_str()); if (!el->shortName.empty()) fprintf(fp, " (%s)", el->shortName.c_str()); } fputs("", fp); if (!el->description.empty()) fprintf(fp, " - %s\n", el->description.c_str()); fputs("
\n", fp); } void OutputToc(FILE* fp, const ScriptingDocumentation& doc) { //***TOC: Libraries fputs("
Libraries
\n", fp); for (const auto& el : doc.elements) { if (el->type != ElementType::Library) { continue; } ade_output_toc(fp, el); } //***TOC: Objects fputs("
Types
\n", fp); for (const auto& el : doc.elements) { if (el->type != ElementType::Class) { continue; } ade_output_toc(fp, el); } //***TOC: Enumerations fputs("
Enumerations
", fp); //***End TOC fputs("

", fp); } SCP_string element_name(const std::unique_ptr& el) { if (!el->name.empty()) { return el->name; } return el->shortName; } void ade_output_type_link(FILE* fp, const ade_type_info& type_info) { switch (type_info.getType()) { case ade_type_info_type::Empty: fputs("nothing", fp); break; case ade_type_info_type::Simple: if (ade_is_internal_type(type_info.getIdentifier())) { fputs(type_info.getIdentifier(), fp); } else { fprintf(fp, "%s", type_info.getIdentifier(), type_info.getIdentifier()); } break; case ade_type_info_type::Tuple: { bool first = true; for (const auto& type : type_info.elements()) { if (!first) { fprintf(fp, ", "); } first = false; ade_output_type_link(fp, type); } break; } case ade_type_info_type::Array: fputs("{ ", fp); ade_output_type_link(fp, type_info.arrayType()); fputs(" ... }", fp); break; case ade_type_info_type::Map: fputs("{ ", fp); ade_output_type_link(fp, type_info.elements()[0]); fputs(" -> ", fp); ade_output_type_link(fp, type_info.elements()[1]); fputs(" ... }", fp); break; case ade_type_info_type::Iterator: fputs("iterator< ", fp); ade_output_type_link(fp, type_info.arrayType()); fputs(" >", fp); break; case ade_type_info_type::Alternative: { bool first = true; for (const auto& type : type_info.elements()) { if (!first) { fputs(" | ", fp); } first = false; ade_output_type_link(fp, type); } break; } case ade_type_info_type::Function: { const auto& elements = type_info.elements(); if (elements.size() == 1) { fputs("function() -> ", fp); } else { fputs("function(", fp); bool first = true; for (auto iter = elements.begin() + 1; iter != elements.end(); ++iter) { if (!first) { fputs(", ", fp); } first = false; ade_output_type_link(fp, *iter); fprintf(fp, " %s", iter->getName().c_str()); } fputs(") -> ", fp); } ade_output_type_link(fp, elements.front()); } } } void output_argument_list(FILE* fp, const DocumentationElementFunction::argument_list& overload) { if (overload.simple.empty()) { bool first = true; bool optional = false; for (const auto& arg : overload.arguments) { if (!first) { fputs(", ", fp); } first = false; if (arg.optional && !optional) { fputs("[", fp); optional = true; } ade_output_type_link(fp, arg.type); if (!arg.name.empty()) { fprintf(fp, " %s", arg.name.c_str()); if (!arg.def_val.empty()) { fprintf(fp, " = %s", arg.def_val.c_str()); } } } if (optional) { fputs("]", fp); } } else { fprintf(fp, "%s", overload.simple.c_str()); } } void OutputElement(FILE* fp, const std::unique_ptr& el, const DocumentationElement* parent, const DocumentationElement* parentParent) { if (el->name.empty() && el->shortName.empty()) { Warning(LOCATION, "Found ade_table_entry with no name or shortname"); return; } bool skip_this = false; // WMC - Hack if (parent != nullptr) { for (const auto& child : el->children) { if (child->name == "__indexer") { OutputElement(fp, child, el.get(), parent); skip_this = true; break; } } } //***Begin entry if (!skip_this) { fputs("
", fp); switch (el->type) { case ElementType::Library: case ElementType::Class: { //***Name (ShortName) if (parent == nullptr) { fprintf(fp, "
", element_name(el).c_str()); } if (el->name.empty()) { if (el->type == ElementType::Class) { const auto classEl = static_cast(el.get()); if (classEl->superClass.empty()) fprintf(fp, "

%s

\n", classEl->shortName.c_str()); else { fprintf(fp, "

%s:%s

\n", classEl->superClass.c_str(), classEl->superClass.c_str(), classEl->superClass.c_str()); } } else { fprintf(fp, "

%s

\n", el->shortName.c_str()); } } else { fprintf(fp, "

%s", el->name.c_str()); if (!el->shortName.empty()) fprintf(fp, " (%s)", el->shortName.c_str()); fputs("

\n", fp); if (el->type == ElementType::Class) { const auto classEl = static_cast(el.get()); if (!classEl->superClass.empty()) { fprintf(fp, ":%s", classEl->superClass.c_str(), classEl->superClass.c_str()); } } } fputs("
\n", fp); //***Description if (!el->description.empty()) { fprintf(fp, "
%s
\n", el->description.c_str()); } break; } case ElementType::Operator: case ElementType::Function: { const auto funcEl = static_cast(el.get()); //***Name(ShortName)(Arguments) for (const auto& overload : funcEl->overloads) { fputs("
", fp); fputs("", fp); if (!funcEl->returnType.isEmpty()) ade_output_type_link(fp, funcEl->returnType); else fputs("nil", fp); fputs(" ", fp); const string_conv* op = nullptr; if (el->name.empty()) { fprintf(fp, "%s", el->shortName.c_str()); } else { op = ade_get_operator(el->name.c_str()); // WMC - Do we have an operator? if (op == nullptr) { fprintf(fp, "%s", el->name.c_str()); if (!el->shortName.empty()) { const auto op2 = ade_get_operator(el->shortName.c_str()); if (op2 == nullptr) fprintf(fp, "(%s)", el->shortName.c_str()); else fprintf(fp, "(%s)", op2->dest); } } else { // WMC - Hack if (parent != nullptr && !parent->name.empty() && parentParent != nullptr && el->name == "__indexer") { fprintf(fp, "%s%s", parent->name.c_str(), op->dest); } else fprintf(fp, "%s", op->dest); } } if (op == nullptr) { fputs("(", fp); output_argument_list(fp, overload); fputs(")\n", fp); } else { fputs(" ", fp); output_argument_list(fp, overload); } fputs("
\n", fp); } //***Description if (!el->description.empty()) { fprintf(fp, "
%s
\n", el->description.c_str()); } if (el->deprecationVersion.isValid()) { if (!el->deprecationMessage.empty()) { fprintf(fp, "
Deprecated starting with version %d.%d.%d: %s
\n", el->deprecationVersion.major, el->deprecationVersion.minor, el->deprecationVersion.build, el->deprecationMessage.c_str()); } else { fprintf(fp, "
Deprecated starting with version %d.%d.%d.
\n", el->deprecationVersion.major, el->deprecationVersion.minor, el->deprecationVersion.build); } } //***Result: ReturnDescription if (!funcEl->returnDocumentation.empty()) { fprintf(fp, "
Returns: %s
\n", funcEl->returnDocumentation.c_str()); } else { fputs("
Returns: Nothing
\n", fp); } break; } case ElementType::Property: { const auto propEl = static_cast(el.get()); //***Type Name(ShortName) fputs("
\n", fp); fputs("", fp); ade_output_type_link(fp, propEl->getterType); fputs(" ", fp); if (propEl->name.empty()) { fprintf(fp, "%s\n", propEl->shortName.c_str()); } else { fprintf(fp, "%s", propEl->name.c_str()); if (!propEl->shortName.empty()) fputs(propEl->shortName.c_str(), fp); fputs("\n", fp); } if (!propEl->setterType.empty()) fprintf(fp, " = %s", propEl->setterType.c_str()); fputs("
\n", fp); //***Description if (!propEl->description.empty()) fprintf(fp, "
%s
\n", propEl->description.c_str()); //***Also settable with: Arguments if (!propEl->returnDocumentation.empty()) fprintf(fp, "
Value: %s
\n", propEl->returnDocumentation.c_str()); break; } default: Warning(LOCATION, "Unknown type '%d' passed to ade_table_entry::OutputMeta", static_cast(el->type)); break; } } fputs("
\n", fp); for (const auto& child : el->children) { if (parent == nullptr || child->name != "__indexer") OutputElement(fp, child, el.get(), parent); } fputs("
\n", fp); if (!skip_this) fputs("
\n", fp); } void OutputLuaMeta(FILE* fp, const ScriptingDocumentation& doc) { OutputToc(fp, doc); SCP_vector table_entries; //***Everything fputs("
\n", fp); for (const auto& el : doc.elements) { OutputElement(fp, el, nullptr, nullptr); } fputs("
\n", fp); } template static void output_hook_variable_list(FILE* fp, const Container& vars) { fputs("
", fp); for (const auto& param : vars) { fputs("
", fp); ade_output_type_link(fp, param.type); fprintf(fp, " %s
", param.name); fprintf(fp, "
Description: %s", param.description); } fputs("
", fp); } } // namespace void output_html_doc(const ScriptingDocumentation& doc, const SCP_string& filename) { FILE* fp = fopen(filename.c_str(), "w"); if (fp == nullptr) { return; } fprintf(fp, "\n\n\tScript Output - FSO v%s (%s)\n\n", FS_VERSION_FULL, doc.name.c_str()); fputs("", fp); fprintf(fp, "\t

Script Output - FSO v%s (%s)

\n", FS_VERSION_FULL, doc.name.c_str()); // Scripting languages links fputs("
", fp); //***Hooks fputs("

Conditional Hooks

", fp); fputs("
", fp); // Conditions fputs("
Conditions
", fp); for (const auto& conditions : doc.conditions) { fprintf(fp, "
%s
", conditions.c_str()); } // Actions fputs("
Actions
", fp); fputs("
", fp); for (const auto& hook : doc.actions) { fprintf(fp, "
%s
", hook.name.c_str()); if (!hook.description.empty()) { fprintf(fp, "
Description: %s", hook.description.c_str()); // Only write this for hooks with descriptions since this information is useless for legacy hooks if (hook.overridable) { fputs("
This hook is overridable", fp); } else { fputs("
This hook is not overridable", fp); } if (!hook.parameters.empty()) { fputs("
Hook Variables:", fp); output_hook_variable_list(fp, hook.parameters); } fputs("
", fp); } } fputs("
", fp); fputs("
Global Hook Variables: (set globally independent of a specific hook)
", fp); output_hook_variable_list(fp, doc.globalVariables); fputs("

", fp); // Languages fputs("
", fp); //***Version info fprintf(fp, "
Lua Version: %s
\n", LUA_RELEASE); fputs("
", fp); OutputLuaMeta(fp, doc); fputs("
", fp); //***Enumerations fprintf(fp, "

Enumerations

"); for (const auto& enumeration : doc.enumerations) { // Cyborg17 -- Omit the deprecated flag if (enumeration.name == "VM_EXTERNAL_CAMERA_LOCKED") { continue; } // WMC - For now, print to the file without the description. fprintf(fp, "
%s
", enumeration.name.c_str()); } fputs("
", fp); fclose(fp); } } // namespace scripting