Unify and clean up Lua documentation generation

I added a pretty generic container structure for the scripting
documentation when adding JSON output. However, I did not use that new
structure for the HTML output since that was just too much work.

This moves the HTML output over to the new structure so now the
scripting API handling code is independent from how the resulting
documentation is generated. I also moved that code out of the main
scripting code which removes quite a lot of clutter in those files.

This is mostly a straight port of the old code with no significant
changes (except the changes to make it use the new data).
This commit is contained in:
asarium committed 2020-06-13 16:46:26 +02:00
1 parent bf8416d0cf
commit 4a6628e16f
13 files changed
+954 -903

No files matched your search

+34 -334
View File
@@ -8,7 +8,9 @@
#include "ade.h"
#include "ade_api.h"
#include "ade_args.h"
#include "doc_parser.h"
#include "scripting.h"
#include "scripting_doc.h"
#include "def_files/def_files.h"
#include "mod_table/mod_table.h"
@@ -22,8 +24,6 @@
#include "scripting/lua/LuaFunction.h"
#include "ship/ship.h"
#include <utility>
namespace {
using namespace scripting;
@@ -180,12 +180,6 @@ ade_table_entry& getTableEntry(size_t idx) {
return ade_manager::getInstance()->getEntry(idx);
}
//Struct for converting one string for another. whee!
struct string_conv {
const char *src;
const char *dest;
};
//*************************Lua operators*************************
//These are the various types of operators you can
//set in Lua. Use these as function name to activate.
@@ -215,17 +209,6 @@ string_conv ade_Operators[] = {
{"__indexer", "[]"}, //obj[var]
};
int ade_get_operator(const char *tablename)
{
for(auto iter = std::begin(ade_Operators); iter != std::end(ade_Operators); ++iter)
{
if(!strcmp(tablename, iter->src))
return (int)std::distance(std::begin(ade_Operators), iter);
}
return -1;
}
//WMC - Sometimes this gets out of sync between Lua versions
static const char *Lua_type_names[] = {
"nil",
@@ -241,97 +224,10 @@ static const char *Lua_type_names[] = {
const ptrdiff_t Lua_type_names_num = std::distance(std::begin(Lua_type_names), std::end(Lua_type_names));
static bool is_internal_type(const char* typeName) {
for (int i = 0; i < Lua_type_names_num; i++) {
if (!stricmp(Lua_type_names[i], typeName)) {
return true;
}
}
return false;
}
}
namespace scripting {
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 (is_internal_type(type_info.getIdentifier())) {
fputs(type_info.getIdentifier(), fp);
} else {
fprintf(fp, "<a href=\"#%s\">%s</a>", 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());
}
}
}
ade_manager* ade_manager::getInstance() {
static std::unique_ptr<ade_manager> manager(new ade_manager());
return manager.get();
@@ -590,235 +486,10 @@ size_t ade_table_entry::AddSubentry(ade_table_entry& n_ate) {
return new_idx;
}
void ade_table_entry::OutputMeta(FILE *fp)
{
if(Name == NULL && ShortName == NULL) {
Warning(LOCATION, "Data entry with no name or shortname");
return;
}
uint i;
bool skip_this = false;
//WMC - Hack
if(ParentIdx != UINT_MAX)
{
for(i = 0; i < Num_subentries; i++)
{
if(!stricmp(ade_manager::getInstance()->getEntry(Subentries[i]).Name, "__indexer"))
{
ade_manager::getInstance()->getEntry(Subentries[i]).OutputMeta(fp);
skip_this = true;
break;
}
}
}
//***Begin entry
if(!skip_this)
{
fputs("<dd><dl>", fp);
switch(Type)
{
case 'o':
{
//***Name (ShortName)
if(ParentIdx == UINT_MAX) {
fprintf(fp, "<dt id=\"%s\">", GetName());
}
if(Name == NULL)
{
if(DerivatorIdx == UINT_MAX)
fprintf(fp, "<h2>%s</h2>\n", ShortName);
else
fprintf(fp, "<h2>%s:<a href=\"#%s\">%s</a></h2>\n", getTableEntry(DerivatorIdx).GetName(), getTableEntry(DerivatorIdx).GetName(), getTableEntry(DerivatorIdx).GetName());
}
else
{
fprintf(fp, "<h2>%s", Name);
if(ShortName != NULL)
fprintf(fp, " (%s)", ShortName);
if(DerivatorIdx != UINT_MAX)
fprintf(fp, ":<a href=\"#%s\">%s</a>", getTableEntry(DerivatorIdx).GetName(), getTableEntry(DerivatorIdx).GetName());
fputs("</h2>\n", fp);
}
fputs("</dt>\n", fp);
//***Description
if(Description != NULL) {
fprintf(fp, "<dd>%s</dd>\n", this->Description);
}
}
break;
case 'u': {
//***Name(ShortName)(Arguments)
for (const auto& overload : Arguments.overloads()) {
fputs("<dt>", fp);
int ao = -1;
fputs("<i>", fp);
if (!ReturnType.isEmpty())
ade_output_type_link(fp, ReturnType);
else
fputs("nil", fp);
fputs(" </i>", fp);
if (Name == nullptr) {
fprintf(fp, "<b>%s", ShortName);
} else {
ao = ade_get_operator(Name);
// WMC - Do we have an operator?
if (ao < 0) {
fprintf(fp, "<b>%s", Name);
if (ShortName != nullptr) {
int ao2 = ade_get_operator(ShortName);
if (ao2 < 0)
fprintf(fp, "(%s)", ShortName);
else
fprintf(fp, "(%s)", ade_Operators[ao2].dest);
}
} else {
// WMC - Hack
if (ParentIdx != UINT_MAX && getTableEntry(ParentIdx).ParentIdx != UINT_MAX &&
getTableEntry(ParentIdx).Name != nullptr && !stricmp(Name, "__indexer")) {
fprintf(fp, "<b>%s%s", getTableEntry(ParentIdx).Name, ade_Operators[ao].dest);
} else
fprintf(fp, "<b>%s", ade_Operators[ao].dest);
}
}
if (ao < 0) {
fputs("(</b>", fp);
if (overload != nullptr) {
argument_list_parser arg_parser;
if (arg_parser.parse(overload)) {
bool first = true;
bool optional = false;
for (const auto& arg : arg_parser.getArgList()) {
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, " <i>%s</i>", arg.name.c_str());
if (!arg.def_val.empty()) {
fprintf(fp, " = <i>%s</i>", arg.def_val.c_str());
}
}
}
if (optional) {
fputs("]", fp);
}
} else {
fprintf(fp, "<i>%s</i>", overload);
}
}
fputs("<b>)</b>\n", fp);
} else {
fputs("</b>", fp);
if (overload != nullptr) {
fprintf(fp, " <i>%s</i>\n", overload);
}
}
fputs("</dt>\n", fp);
}
//***Description
if(Description != NULL) {
fprintf(fp, "<dd>%s</dd>\n", Description);
}
if (DeprecationVersion.isValid()) {
if (DeprecationMessage != nullptr) {
fprintf(fp, "<dd><b>Deprecated starting with version %d.%d.%d:</b> %s</dd>\n",
DeprecationVersion.major, DeprecationVersion.minor, DeprecationVersion.build,
DeprecationMessage);
} else {
fprintf(fp, "<dd><b>Deprecated starting with version %d.%d.%d.</b></dd>\n",
DeprecationVersion.major, DeprecationVersion.minor, DeprecationVersion.build);
}
}
//***Result: ReturnDescription
if (ReturnDescription != nullptr) {
fprintf(fp, "<dd><b>Returns:</b> %s<br>&nbsp;</dd>\n", ReturnDescription);
} else {
fputs("<dd><b>Returns:</b> Nothing<br>&nbsp;</dd>\n", fp);
}
}
break;
default:
Warning(LOCATION, "Unknown type '%c' passed to ade_table_entry::OutputMeta", Type);
FALLTHROUGH;
case 'b':
case 'd':
case 'f':
case 'i':
case 's':
case 'x':
case 'v': {
//***Type Name(ShortName)
for (const auto& overload : Arguments.overloads()) {
fputs("<dt>\n", fp);
if (!ReturnType.isEmpty()) {
fputs("<i>", fp);
ade_output_type_link(fp, ReturnType);
fputs(" </i>", fp);
}
if (Name == nullptr) {
fprintf(fp, "<b>%s</b>\n", ShortName);
} else {
fprintf(fp, "<b>%s", Name);
if (ShortName != nullptr)
fputs(ShortName, fp);
fputs("</b>\n", fp);
}
if (overload != nullptr)
fprintf(fp, " = <i>%s</i>", overload);
fputs("</dt>\n", fp);
}
//***Description
if(Description != NULL)
fprintf(fp, "<dd>%s</dd>\n", Description);
//***Also settable with: Arguments
if(ReturnDescription != NULL)
fprintf(fp, "<dd><b>Value:</b> %s</b></dd>\n", ReturnDescription);
}
break;
}
}
fputs("<dd><dl>\n", fp);
for(i = 0; i < Num_subentries; i++)
{
auto entry = &getTableEntry(Subentries[i]);
if(ParentIdx == UINT_MAX || stricmp(entry->Name, "__indexer") != 0)
entry->OutputMeta(fp);
}
fputs("</dl></dd>\n", fp);
if(!skip_this)
fputs("<br></dl></dd>\n", fp);
}
std::unique_ptr<DocumentationElement> ade_table_entry::ToDocumentationElement()
{
using namespace scripting;
if (Name == nullptr && ShortName == nullptr) {
Warning(LOCATION, "Data entry with no name or shortname");
return std::unique_ptr<DocumentationElement>();
@@ -854,7 +525,7 @@ std::unique_ptr<DocumentationElement> ade_table_entry::ToDocumentationElement()
std::unique_ptr<DocumentationElementFunction> obj(new DocumentationElementFunction());
auto ao = ade_get_operator(Name);
if (ao >= 0) {
if (ao != nullptr) {
obj->type = ElementType::Operator;
} else {
obj->type = ElementType::Function;
@@ -932,6 +603,14 @@ std::unique_ptr<DocumentationElement> ade_table_entry::ToDocumentationElement()
element->description = Description;
}
if (DeprecationVersion.isValid()) {
element->deprecationVersion = DeprecationVersion;
if (DeprecationMessage != nullptr) {
element->deprecationMessage = DeprecationMessage;
}
}
for (uint32_t i = 0; i < Num_subentries; i++) {
element->children.emplace_back(getTableEntry(Subentries[i]).ToDocumentationElement());
}
@@ -1165,6 +844,16 @@ int ade_friendly_error(lua_State *L)
return LUA_ERRRUN;
}
bool ade_is_internal_type(const char* typeName) {
for (int i = 0; i < Lua_type_names_num; i++) {
if (!stricmp(Lua_type_names[i], typeName)) {
return true;
}
}
return false;
}
//WMC - Gets type of object
const char *ade_get_type_string(lua_State *L, int argnum)
{
@@ -1252,6 +941,17 @@ void load_default_script(lua_State* L, const char* name)
}
}
const string_conv* ade_get_operator(const char *funcname)
{
for(auto iter = std::begin(ade_Operators); iter != std::end(ade_Operators); ++iter)
{
if(!strcmp(funcname, iter->src))
return &(*iter);
}
return nullptr;
}
ade_table_entry& internal::getTableEntry(size_t idx) { return ade_manager::getInstance()->getEntry(idx); }
}
+15 -9
View File
@@ -25,11 +25,12 @@ extern "C" {
* These functions enable the code to communicate with external scripts and expose an API for them to use
*/
// Forward definition
struct DocumentationElement;
namespace scripting {
// Forward definition
struct DocumentationElement;
/**
*
* @param L
@@ -153,7 +154,6 @@ class ade_table_entry {
//*****Functions
size_t AddSubentry(ade_table_entry& n_ate);
int SetTable(lua_State* L, int p_amt_ldx, int p_mtb_ldx);
void OutputMeta(FILE* fp);
std::unique_ptr<DocumentationElement> ToDocumentationElement();
//*****Get
@@ -201,6 +201,11 @@ int ade_friendly_error(lua_State* L);
*/
const char* ade_get_type_string(lua_State* L, int argnum);
/**
* @ingroup ade_api
*/
bool ade_is_internal_type(const char* typeName);
/**
* @brief Converts an object index to something that can be used with ade_set_args.
*
@@ -241,12 +246,13 @@ int ade_set_object_with_breed(lua_State* L, int obj_idx);
*/
void load_default_script(lua_State* L, const char* name);
/**
* @brief Writes HTML to fp which produces a link to the specified type info
* @param fp The stream to write the HTML to
* @param type_info The type to generate the link for
*/
void ade_output_type_link(FILE* fp, const ade_type_info& type_info);
//Struct for converting one string for another. whee!
struct string_conv {
const char *src;
const char *dest;
};
const string_conv* ade_get_operator(const char *funcname);
namespace internal {
+499
View File
@@ -0,0 +1,499 @@
#include "doc_html.h"
namespace scripting {
namespace {
void ade_output_toc(FILE* fp, const std::unique_ptr<DocumentationElement>& 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("<dd>", fp);
if (el->name.empty()) {
fprintf(fp, "<a href=\"#%s\">%s", el->shortName.c_str(), el->shortName.c_str());
} else {
fprintf(fp, "<a href=\"#%s\">%s", el->name.c_str(), el->name.c_str());
if (!el->shortName.empty())
fprintf(fp, " (%s)", el->shortName.c_str());
}
fputs("</a>", fp);
if (!el->description.empty())
fprintf(fp, " - %s\n", el->description.c_str());
fputs("</dd>\n", fp);
}
void OutputToc(FILE* fp, const ScriptingDocumentation& doc)
{
//***TOC: Libraries
fputs("<dt><b>Libraries</b></dt>\n", fp);
for (const auto& el : doc.elements) {
if (el->type != ElementType::Library) {
continue;
}
ade_output_toc(fp, el);
}
//***TOC: Objects
fputs("<dt><b>Types</b></dt>\n", fp);
for (const auto& el : doc.elements) {
if (el->type != ElementType::Class) {
continue;
}
ade_output_toc(fp, el);
}
//***TOC: Enumerations
fputs("<dt><b><a href=\"#Enumerations\">Enumerations</a></b></dt>", fp);
//***End TOC
fputs("</dl><br/><br/>", fp);
}
SCP_string element_name(const std::unique_ptr<DocumentationElement>& 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, "<a href=\"#%s\">%s</a>", 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, " <i>%s</i>", arg.name.c_str());
if (!arg.def_val.empty()) {
fprintf(fp, " = <i>%s</i>", arg.def_val.c_str());
}
}
}
if (optional) {
fputs("]", fp);
}
} else {
fprintf(fp, "<i>%s</i>", overload.simple.c_str());
}
}
void OutputElement(FILE* fp,
const std::unique_ptr<DocumentationElement>& 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("<dd><dl>", fp);
switch (el->type) {
case ElementType::Library:
case ElementType::Class: {
//***Name (ShortName)
if (parent == nullptr) {
fprintf(fp, "<dt id=\"%s\">", element_name(el).c_str());
}
if (el->name.empty()) {
if (el->type == ElementType::Class) {
const auto classEl = static_cast<DocumentationElementClass*>(el.get());
if (classEl->superClass.empty())
fprintf(fp, "<h2>%s</h2>\n", classEl->shortName.c_str());
else {
fprintf(fp,
"<h2>%s:<a href=\"#%s\">%s</a></h2>\n",
classEl->superClass.c_str(),
classEl->superClass.c_str(),
classEl->superClass.c_str());
}
} else {
fprintf(fp, "<h2>%s</h2>\n", el->shortName.c_str());
}
} else {
fprintf(fp, "<h2>%s", el->name.c_str());
if (!el->shortName.empty())
fprintf(fp, " (%s)", el->shortName.c_str());
fputs("</h2>\n", fp);
if (el->type == ElementType::Class) {
const auto classEl = static_cast<DocumentationElementClass*>(el.get());
if (!classEl->superClass.empty()) {
fprintf(fp,
":<a href=\"#%s\">%s</a>",
classEl->superClass.c_str(),
classEl->superClass.c_str());
}
}
}
fputs("</dt>\n", fp);
//***Description
if (!el->description.empty()) {
fprintf(fp, "<dd>%s</dd>\n", el->description.c_str());
}
break;
}
case ElementType::Operator:
case ElementType::Function: {
const auto funcEl = static_cast<DocumentationElementFunction*>(el.get());
//***Name(ShortName)(Arguments)
for (const auto& overload : funcEl->overloads) {
fputs("<dt>", fp);
fputs("<i>", fp);
if (!funcEl->returnType.isEmpty())
ade_output_type_link(fp, funcEl->returnType);
else
fputs("nil", fp);
fputs(" </i>", fp);
const string_conv* op = nullptr;
if (el->name.empty()) {
fprintf(fp, "<b>%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, "<b>%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, "<b>%s%s", parent->name.c_str(), op->dest);
} else
fprintf(fp, "<b>%s", op->dest);
}
}
if (op == nullptr) {
fputs("(</b>", fp);
output_argument_list(fp, overload);
fputs("<b>)</b>\n", fp);
} else {
fputs("</b> ", fp);
output_argument_list(fp, overload);
}
fputs("</dt>\n", fp);
}
//***Description
if (!el->description.empty()) {
fprintf(fp, "<dd>%s</dd>\n", el->description.c_str());
}
if (el->deprecationVersion.isValid()) {
if (!el->deprecationMessage.empty()) {
fprintf(fp,
"<dd><b>Deprecated starting with version %d.%d.%d:</b> %s</dd>\n",
el->deprecationVersion.major,
el->deprecationVersion.minor,
el->deprecationVersion.build,
el->deprecationMessage.c_str());
} else {
fprintf(fp,
"<dd><b>Deprecated starting with version %d.%d.%d.</b></dd>\n",
el->deprecationVersion.major,
el->deprecationVersion.minor,
el->deprecationVersion.build);
}
}
//***Result: ReturnDescription
if (!funcEl->returnDocumentation.empty()) {
fprintf(fp, "<dd><b>Returns:</b> %s</dd>\n", funcEl->returnDocumentation.c_str());
} else {
fputs("<dd><b>Returns:</b> Nothing</dd>\n", fp);
}
break;
}
case ElementType::Property: {
const auto propEl = static_cast<DocumentationElementProperty*>(el.get());
//***Type Name(ShortName)
fputs("<dt>\n", fp);
fputs("<i>", fp);
ade_output_type_link(fp, propEl->getterType);
fputs("</i> ", fp);
if (propEl->name.empty()) {
fprintf(fp, "<b>%s</b>\n", propEl->shortName.c_str());
} else {
fprintf(fp, "<b>%s", propEl->name.c_str());
if (!propEl->shortName.empty())
fputs(propEl->shortName.c_str(), fp);
fputs("</b>\n", fp);
}
if (!propEl->setterType.empty())
fprintf(fp, " = <i>%s</i>", propEl->setterType.c_str());
fputs("</dt>\n", fp);
//***Description
if (!propEl->description.empty())
fprintf(fp, "<dd>%s</dd>\n", propEl->description.c_str());
//***Also settable with: Arguments
if (!propEl->returnDocumentation.empty())
fprintf(fp, "<dd><b>Value:</b> %s</b></dd>\n", propEl->returnDocumentation.c_str());
break;
}
default:
Warning(LOCATION, "Unknown type '%d' passed to ade_table_entry::OutputMeta", static_cast<int>(el->type));
break;
}
}
fputs("<dd><dl>\n", fp);
for (const auto& child : el->children) {
if (parent == nullptr || child->name != "__indexer")
OutputElement(fp, child, el.get(), parent);
}
fputs("</dl></dd>\n", fp);
if (!skip_this)
fputs("<br></dl></dd>\n", fp);
}
void OutputLuaMeta(FILE* fp, const ScriptingDocumentation& doc)
{
OutputToc(fp, doc);
SCP_vector<ade_table_entry*> table_entries;
//***Everything
fputs("<dl>\n", fp);
for (const auto& el : doc.elements) {
OutputElement(fp, el, nullptr, nullptr);
}
fputs("</dl>\n", fp);
}
template <typename Container>
static void output_hook_variable_list(FILE* fp, const Container& vars)
{
fputs("<dl>", fp);
for (const auto& param : vars) {
fputs("<dt>", fp);
ade_output_type_link(fp, param.type);
fprintf(fp, " <i>%s</i></dt>", param.name);
fprintf(fp, "<dd><b>Description:</b> %s</dt>", param.description);
}
fputs("</dl>", 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,
"<html>\n<head>\n\t<title>Script Output - FSO v%s (%s)</title>\n</head>\n",
FS_VERSION_FULL,
doc.name.c_str());
fputs("<body>", fp);
fprintf(fp, "\t<h1>Script Output - FSO v%s (%s)</h1>\n", FS_VERSION_FULL, doc.name.c_str());
// Scripting languages links
fputs("<dl>", fp);
//***Hooks
fputs("<dt><h2>Conditional Hooks</h2></dt>", fp);
fputs("<dd><dl>", fp);
// Conditions
fputs("<dt><b>Conditions</b></dt>", fp);
for (const auto& conditions : doc.conditions) {
fprintf(fp, "<dd>%s</dd>", conditions.c_str());
}
// Actions
fputs("<dt><b>Actions</b></dt>", fp);
fputs("<dd><dl>", fp);
for (const auto& hook : doc.actions) {
fprintf(fp, "<dt><b>%s</b></dt>", hook.name.c_str());
if (!hook.description.empty()) {
fprintf(fp, "<dd><b>Description:</b> %s", hook.description.c_str());
// Only write this for hooks with descriptions since this information is useless for legacy hooks
if (hook.overridable) {
fputs("<br><i>This hook is overridable</i>", fp);
} else {
fputs("<br><i>This hook is <b>not</b> overridable</i>", fp);
}
if (!hook.parameters.empty()) {
fputs("<br><b>Hook Variables:</b>", fp);
output_hook_variable_list(fp, hook.parameters);
}
fputs("</dd>", fp);
}
}
fputs("</dl></dd>", fp);
fputs("<dt><b>Global Hook Variables:</b> (set globally independent of a specific hook)</dt>", fp);
output_hook_variable_list(fp, doc.globalVariables);
fputs("</dl></dd><br />", fp);
// Languages
fputs("<dl>", fp);
//***Version info
fprintf(fp, "<dd>Lua Version: %s</dd>\n", LUA_RELEASE);
fputs("<dd>", fp);
OutputLuaMeta(fp, doc);
fputs("</dd>", fp);
//***Enumerations
fprintf(fp, "<dt id=\"Enumerations\"><h2>Enumerations</h2></dt>");
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, "<dd><b>%s</b></dd>", enumeration.name.c_str());
}
fputs("</dl></body></html>", fp);
fclose(fp);
}
} // namespace scripting
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include "scripting_doc.h"
namespace scripting {
void output_html_doc(const ScriptingDocumentation& doc, const SCP_string& filename);
}
+262
View File
@@ -0,0 +1,262 @@
#include "doc_json.h"
#include "libs/jansson.h"
namespace scripting {
static void json_doc_generate_class(json_t* elObj, const DocumentationElementClass* lib)
{
json_object_set_new(elObj, "superClass", json_string(lib->superClass.c_str()));
}
static json_t* json_doc_generate_return_type(const scripting::ade_type_info& type_info)
{
switch (type_info.getType()) {
case ade_type_info_type::Empty:
return json_string("void");
case ade_type_info_type::Simple:
return json_string(type_info.getIdentifier());
case ade_type_info_type::Tuple: {
json_t* tupleTypes = json_array();
for (const auto& type : type_info.elements()) {
json_array_append_new(tupleTypes, json_doc_generate_return_type(type));
}
return json_pack("{ssso}", "type", "tuple", "elements", tupleTypes);
}
case ade_type_info_type::Array: {
return json_pack("{ssso}",
"type",
"list",
"element",
json_doc_generate_return_type(type_info.elements().front()));
}
case ade_type_info_type::Map: {
return json_pack("{sssoso}",
"type",
"map",
"key",
json_doc_generate_return_type(type_info.elements()[0]),
"value",
json_doc_generate_return_type(type_info.elements()[1]));
}
case ade_type_info_type::Iterator: {
return json_pack("{ssso}",
"type",
"iterator",
"element",
json_doc_generate_return_type(type_info.elements().front()));
}
case ade_type_info_type::Alternative: {
json_t* alternativeTypes = json_array();
for (const auto& type : type_info.elements()) {
json_array_append_new(alternativeTypes, json_doc_generate_return_type(type));
}
return json_pack("{ssso}", "type", "alternative", "elements", alternativeTypes);
}
case ade_type_info_type::Function: {
const auto& elements = type_info.elements();
json_t* parameterTypes = json_array();
if (elements.size() > 1) {
for (auto iter = elements.begin() + 1; iter != elements.end(); ++iter) {
json_array_append_new(parameterTypes,
json_pack("{ssso}", "name", iter->getName().c_str(), "type", json_doc_generate_return_type(*iter)));
}
}
return json_pack("{sssoso}",
"type",
"function",
"returnType",
json_doc_generate_return_type(elements.front()),
"parameters",
parameterTypes);
}
}
UNREACHABLE("Unknown type type!");
return nullptr;
}
static json_t* json_doc_function_signature(const SCP_vector<scripting::argument_def>& args)
{
json_t* arr = json_array();
for (const auto& arg : args) {
json_array_append(arr,
json_pack("{sosssssb}",
"type",
json_doc_generate_return_type(arg.type),
"name",
arg.name.c_str(),
"default",
arg.def_val.c_str(),
"optional",
arg.optional));
}
return arr;
}
static void json_doc_generate_function(json_t* elObj, const DocumentationElementFunction* lib)
{
json_object_set_new(elObj, "returnType", json_doc_generate_return_type(lib->returnType));
json_object_set_new(elObj, "returnDocumentation", json_string(lib->returnDocumentation.c_str()));
if (lib->overloads.size() == 1 && lib->overloads.front().arguments.empty()) {
// Simple overload
json_object_set_new(elObj, "parameters", json_string(lib->overloads.front().simple.c_str()));
} else {
json_t* arr = json_array();
for (const auto& overload : lib->overloads) {
if (!overload.simple.empty()) {
json_array_append_new(arr, json_string(overload.simple.c_str()));
} else {
json_array_append_new(arr, json_doc_function_signature(overload.arguments));
}
}
json_object_set_new(elObj, "parameters", arr);
}
}
static void json_doc_generate_property(json_t* elObj, const DocumentationElementProperty* lib)
{
json_object_set_new(elObj, "getterType", json_doc_generate_return_type(lib->getterType));
json_object_set_new(elObj, "setterType", json_string(lib->setterType.c_str()));
json_object_set_new(elObj, "returnDocumentation", json_string(lib->returnDocumentation.c_str()));
}
static json_t* json_doc_generate_elements(const SCP_vector<std::unique_ptr<DocumentationElement>>& elements);
static json_t* json_doc_generate_element(const std::unique_ptr<DocumentationElement>& element)
{
json_t* elementsObj = json_object();
json_object_set_new(elementsObj, "name", json_string(element->name.c_str()));
json_object_set_new(elementsObj, "shortName", json_string(element->shortName.c_str()));
json_object_set_new(elementsObj, "description", json_string(element->description.c_str()));
switch (element->type) {
case ElementType::Library:
json_object_set_new(elementsObj, "type", json_string("library"));
break;
case ElementType::Class:
json_object_set_new(elementsObj, "type", json_string("class"));
json_doc_generate_class(elementsObj, static_cast<DocumentationElementClass*>(element.get()));
break;
case ElementType::Function:
json_object_set_new(elementsObj, "type", json_string("function"));
json_doc_generate_function(elementsObj, static_cast<DocumentationElementFunction*>(element.get()));
break;
case ElementType::Operator:
json_object_set_new(elementsObj, "type", json_string("operator"));
json_doc_generate_function(elementsObj, static_cast<DocumentationElementFunction*>(element.get()));
break;
case ElementType::Property:
json_object_set_new(elementsObj, "type", json_string("property"));
json_doc_generate_property(elementsObj, static_cast<DocumentationElementProperty*>(element.get()));
break;
default:
json_object_set_new(elementsObj, "type", json_string("unknown"));
break;
}
json_object_set_new(elementsObj, "children", json_doc_generate_elements(element->children));
return elementsObj;
}
static json_t* json_doc_generate_elements(const SCP_vector<std::unique_ptr<DocumentationElement>>& elements)
{
json_t* elementsArray = json_array();
for (const auto& el : elements) {
json_array_append_new(elementsArray, json_doc_generate_element(el));
}
return elementsArray;
}
static json_t* json_doc_generate_param_element(const HookVariableDocumentation& param)
{
return json_pack("{s:s,s:s,s:o}",
"name",
param.name,
"description",
param.description,
"type",
json_doc_generate_return_type(param.type));
}
static json_t* json_doc_generate_action_element(const DocumentationAction& action)
{
json_t* paramArray = json_array();
for (const auto& param : action.parameters) {
json_array_append_new(paramArray, json_doc_generate_param_element(param));
}
return json_pack("{s:s,s:s,s:o,s:b}",
"name",
action.name.c_str(),
"description",
action.description.c_str(),
"hookVars",
paramArray,
"overridable",
action.overridable);
}
void output_json_doc(const ScriptingDocumentation& doc, const SCP_string& filename)
{
std::unique_ptr<json_t> root(json_object());
// Should be incremented every time an incompatible change is made
json_object_set_new(root.get(), "version", json_integer(2));
{
json_t* actionArray = json_array();
for (const auto& action : doc.actions) {
json_array_append_new(actionArray, json_doc_generate_action_element(action));
}
json_object_set_new(root.get(), "actions", actionArray);
}
{
json_t* conditionArray = json_array();
for (const auto& cond : doc.conditions) {
json_array_append_new(conditionArray, json_string(cond.c_str()));
}
json_object_set_new(root.get(), "conditions", conditionArray);
}
{
json_t* enumObject = json_object();
for (const auto& enumVal : doc.enumerations) {
json_object_set_new(enumObject, enumVal.name.c_str(), json_integer(enumVal.value));
}
json_object_set_new(root.get(), "enums", enumObject);
}
{
json_t* globalVariables = json_array();
for (const auto& param : doc.globalVariables) {
json_array_append_new(globalVariables, json_doc_generate_param_element(param));
}
json_object_set_new(root.get(), "globalVars", globalVariables);
}
json_object_set_new(root.get(), "elements", json_doc_generate_elements(doc.elements));
const auto jsonStr = json_dump_string(root.get(), JSON_INDENT(2) | JSON_SORT_KEYS);
std::ofstream outStr(filename, std::ios::binary);
outStr << jsonStr;
}
} // namespace scripting
+9
View File
@@ -0,0 +1,9 @@
#pragma once
#include "scripting_doc.h"
namespace scripting {
void output_json_doc(const ScriptingDocumentation& doc, const SCP_string& filename);
}
+5
View File
@@ -57,6 +57,11 @@ ade_odata_setter<object_h> convert_arg_type(object* objp)
}
} // namespace detail
HookVariableDocumentation::HookVariableDocumentation(const char* name_, ade_type_info type_, const char* description_)
: name(name_), type(std::move(type_)), description(description_)
{
}
HookBase::HookBase(SCP_string hookName,
SCP_string description,
SCP_vector<HookVariableDocumentation> parameters,
+9
View File
@@ -74,6 +74,15 @@ detail::HookParameterInstanceList<Args...> hook_param_list(detail::HookParameter
return detail::HookParameterInstanceList<Args...>(std::move(params)...);
}
struct HookVariableDocumentation {
const char* name = nullptr;
scripting::ade_type_info type;
const char* description = nullptr;
HookVariableDocumentation(const char* name_, scripting::ade_type_info type_, const char* description_);
};
class HookBase {
public:
HookBase(SCP_string hookName,
+2 -109
View File
@@ -1,6 +1,8 @@
#include "scripting/scripting.h"
#include "scripting_doc.h"
#include "scripting/lua/LuaUtil.h"
/**
@@ -171,37 +173,6 @@ void script_state::EndLuaFrame()
scripting::api::graphics_on_frame();
}
void ade_output_toc(FILE *fp, ade_table_entry *ate)
{
Assert(fp != NULL);
Assert(ate != NULL);
//WMC - sanity checking
if(ate->Name == NULL && ate->ShortName == NULL) {
Warning(LOCATION, "Found ade_table_entry with no name or shortname");
return;
}
fputs("<dd>", fp);
if(ate->Name == NULL)
{
fprintf(fp, "<a href=\"#%s\">%s", ate->ShortName, ate->ShortName);
}
else
{
fprintf(fp, "<a href=\"#%s\">%s", ate->Name, ate->Name);
if(ate->ShortName)
fprintf(fp, " (%s)", ate->ShortName);
}
fputs("</a>", fp);
if(ate->Description)
fprintf(fp, " - %s\n", ate->Description);
fputs("</dd>\n", fp);
}
static bool sort_table_entries(const ade_table_entry* left, const ade_table_entry* right) {
const char* leftCmp = left->Name != nullptr ? left->Name : left->ShortName;
const char* rightCmp = right->Name != nullptr ? right->Name : left->ShortName;
@@ -260,81 +231,3 @@ void script_state::OutputLuaDocumentation(ScriptingDocumentation& doc)
doc.enumerations.push_back(e);
}
}
void script_state::OutputLuaMeta(FILE *fp)
{
fputs("<dl>\n", fp);
//***Version info
fprintf(fp, "<dd>Version: %s</dd>\n", LUA_RELEASE);
SCP_vector<ade_table_entry*> table_entries;
//***TOC: Libraries
fputs("<dt><b>Libraries</b></dt>\n", fp);
for (uint32_t i = 0; i < ade_manager::getInstance()->getNumEntries(); i++) {
auto ate = &ade_manager::getInstance()->getEntry(i);
if (ate->ParentIdx == UINT_MAX && ate->Type == 'o' && ate->Instanced) {
table_entries.push_back(ate);
}
}
std::sort(std::begin(table_entries), std::end(table_entries), sort_table_entries);
for (auto entry : table_entries) {
ade_output_toc(fp, entry);
}
table_entries.clear();
//***TOC: Objects
fputs("<dt><b>Types</b></dt>\n", fp);
for (uint32_t i = 0; i < ade_manager::getInstance()->getNumEntries(); i++) {
auto ate = &ade_manager::getInstance()->getEntry(i);
if (ate->ParentIdx == UINT_MAX && ate->Type == 'o' && !ate->Instanced) {
table_entries.push_back(ate);
}
}
std::sort(std::begin(table_entries), std::end(table_entries), sort_table_entries);
for (auto entry : table_entries) {
ade_output_toc(fp, entry);
}
table_entries.clear();
//***TOC: Enumerations
fputs("<dt><b><a href=\"#Enumerations\">Enumerations</a></b></dt>", fp);
//***End TOC
fputs("</dl><br/><br/>", fp);
//***Everything
fputs("<dl>\n", fp);
for (uint32_t i = 0; i < ade_manager::getInstance()->getNumEntries(); i++) {
auto ate = &ade_manager::getInstance()->getEntry(i);
if(ate->ParentIdx == UINT_MAX)
table_entries.push_back(ate);
}
std::sort(std::begin(table_entries), std::end(table_entries), sort_doc_entries);
for (auto entry : table_entries) {
entry->OutputMeta(fp);
}
table_entries.clear();
//***Enumerations
fprintf(fp, "<dt id=\"Enumerations\"><h2>Enumerations</h2></dt>");
for (uint32_t i = 0; i < Num_enumerations; i++) {
// Cyborg17 -- Omit the deprecated flag
if (!stricmp(Enumerations[i].name,
"VM_EXTERNAL_CAMERA_LOCKED") /* || !stricmp(Enumerations[i], "ADD DEPRECATED ENUM HERE"*/) {
continue;
}
// WMC - This is in case we ever want to add descriptions to enums.
// fprintf(fp, "<dd><dl><dt><b>%s</b></dt><dd>%s</dd></dl></dd>", Enumerations[i].name, Enumerations[i].desc);
// WMC - For now, print to the file without the description.
fprintf(fp, "<dd><b>%s</b></dd>", Enumerations[i].name);
}
fputs("</dl>\n", fp);
//***End LUA
fputs("</dl>\n", fp);
}
+7 -360
View File
@@ -13,16 +13,15 @@
#include "gamesequence/gamesequence.h"
#include "hud/hud.h"
#include "io/key.h"
#include "libs/jansson.h"
#include "mission/missioncampaign.h"
#include "parse/parselo.h"
#include "scripting/doc_html.h"
#include "scripting/doc_json.h"
#include "scripting/scripting_doc.h"
#include "ship/ship.h"
#include "weapon/beam.h"
#include "weapon/weapon.h"
#include <cstdarg>
#include <cstdio>
using namespace scripting;
// tehe. Declare the main event
@@ -120,11 +119,6 @@ static HookVariableDocumentation GlobalVariables[] =
int scripting_state_inited = 0;
HookVariableDocumentation::HookVariableDocumentation(const char* name_, ade_type_info type_, const char* description_)
: name(name_), type(std::move(type_)), description(description_)
{
}
//*************************Scripting init and handling*************************
// ditto
@@ -205,260 +199,6 @@ void script_parse_lua_script(const char *filename) {
}
}
static void json_doc_generate_class(json_t* elObj, const DocumentationElementClass* lib)
{
json_object_set_new(elObj, "superClass", json_string(lib->superClass.c_str()));
}
static json_t* json_doc_generate_return_type(const scripting::ade_type_info& type_info)
{
switch (type_info.getType()) {
case ade_type_info_type::Empty:
return json_string("void");
case ade_type_info_type::Simple:
return json_string(type_info.getIdentifier());
case ade_type_info_type::Tuple: {
json_t* tupleTypes = json_array();
for (const auto& type : type_info.elements()) {
json_array_append_new(tupleTypes, json_doc_generate_return_type(type));
}
return json_pack("{ssso}", "type", "tuple", "elements", tupleTypes);
}
case ade_type_info_type::Array: {
return json_pack("{ssso}",
"type",
"list",
"element",
json_doc_generate_return_type(type_info.elements().front()));
}
case ade_type_info_type::Map: {
return json_pack("{sssoso}",
"type",
"map",
"key",
json_doc_generate_return_type(type_info.elements()[0]),
"value",
json_doc_generate_return_type(type_info.elements()[1]));
}
case ade_type_info_type::Iterator: {
return json_pack("{ssso}",
"type",
"iterator",
"element",
json_doc_generate_return_type(type_info.elements().front()));
}
case ade_type_info_type::Alternative: {
json_t* alternativeTypes = json_array();
for (const auto& type : type_info.elements()) {
json_array_append_new(alternativeTypes, json_doc_generate_return_type(type));
}
return json_pack("{ssso}", "type", "alternative", "elements", alternativeTypes);
}
case ade_type_info_type::Function: {
const auto& elements = type_info.elements();
json_t* parameterTypes = json_array();
if (elements.size() > 1) {
for (auto iter = elements.begin() + 1; iter != elements.end(); ++iter) {
json_array_append_new(parameterTypes,
json_pack("{ssso}", "name", iter->getName().c_str(), "type", json_doc_generate_return_type(*iter)));
}
}
return json_pack("{sssoso}",
"type",
"function",
"returnType",
json_doc_generate_return_type(elements.front()),
"parameters",
parameterTypes);
}
}
UNREACHABLE("Unknown type type!");
return nullptr;
}
static json_t* json_doc_function_signature(const SCP_vector<scripting::argument_def>& args) {
json_t* arr = json_array();
for (const auto& arg : args)
{
json_array_append(arr,
json_pack("{sosssssb}",
"type",
json_doc_generate_return_type(arg.type),
"name",
arg.name.c_str(),
"default",
arg.def_val.c_str(),
"optional",
arg.optional));
}
return arr;
}
static void json_doc_generate_function(json_t* elObj, const DocumentationElementFunction* lib)
{
json_object_set_new(elObj, "returnType", json_doc_generate_return_type(lib->returnType));
json_object_set_new(elObj, "returnDocumentation", json_string(lib->returnDocumentation.c_str()));
if (lib->overloads.size() == 1 && lib->overloads.front().arguments.empty()) {
// Simple overload
json_object_set_new(elObj, "parameters", json_string(lib->overloads.front().simple.c_str()));
} else {
json_t* arr = json_array();
for (const auto& overload : lib->overloads) {
if (!overload.simple.empty()) {
json_array_append_new(arr, json_string(overload.simple.c_str()));
} else {
json_array_append_new(arr, json_doc_function_signature(overload.arguments));
}
}
json_object_set_new(elObj, "parameters", arr);
}
}
static void json_doc_generate_property(json_t* elObj, const DocumentationElementProperty* lib)
{
json_object_set_new(elObj, "getterType", json_doc_generate_return_type(lib->getterType));
json_object_set_new(elObj, "setterType", json_string(lib->setterType.c_str()));
json_object_set_new(elObj, "returnDocumentation", json_string(lib->returnDocumentation.c_str()));
}
static json_t* json_doc_generate_elements(const SCP_vector<std::unique_ptr<DocumentationElement>>& elements);
static json_t* json_doc_generate_element(const std::unique_ptr<DocumentationElement>& element)
{
json_t* elementsObj = json_object();
json_object_set_new(elementsObj, "name", json_string(element->name.c_str()));
json_object_set_new(elementsObj, "shortName", json_string(element->shortName.c_str()));
json_object_set_new(elementsObj, "description", json_string(element->description.c_str()));
switch (element->type) {
case ElementType::Library:
json_object_set_new(elementsObj, "type", json_string("library"));
break;
case ElementType::Class:
json_object_set_new(elementsObj, "type", json_string("class"));
json_doc_generate_class(elementsObj, static_cast<DocumentationElementClass*>(element.get()));
break;
case ElementType::Function:
json_object_set_new(elementsObj, "type", json_string("function"));
json_doc_generate_function(elementsObj, static_cast<DocumentationElementFunction*>(element.get()));
break;
case ElementType::Operator:
json_object_set_new(elementsObj, "type", json_string("operator"));
json_doc_generate_function(elementsObj, static_cast<DocumentationElementFunction*>(element.get()));
break;
case ElementType::Property:
json_object_set_new(elementsObj, "type", json_string("property"));
json_doc_generate_property(elementsObj, static_cast<DocumentationElementProperty*>(element.get()));
break;
default:
json_object_set_new(elementsObj, "type", json_string("unknown"));
break;
}
json_object_set_new(elementsObj, "children", json_doc_generate_elements(element->children));
return elementsObj;
}
static json_t* json_doc_generate_elements(const SCP_vector<std::unique_ptr<DocumentationElement>>& elements)
{
json_t* elementsArray = json_array();
for (const auto& el : elements) {
json_array_append_new(elementsArray, json_doc_generate_element(el));
}
return elementsArray;
}
static json_t* json_doc_generate_param_element(const HookVariableDocumentation& param)
{
return json_pack("{s:s,s:s,s:o}",
"name",
param.name,
"description",
param.description,
"type",
json_doc_generate_return_type(param.type));
}
static json_t* json_doc_generate_action_element(const DocumentationAction& action)
{
json_t* paramArray = json_array();
for (const auto& param : action.parameters) {
json_array_append_new(paramArray, json_doc_generate_param_element(param));
}
return json_pack("{s:s,s:s,s:o,s:b}",
"name",
action.name.c_str(),
"description",
action.description.c_str(),
"hookVars",
paramArray,
"overridable",
action.overridable);
}
static void documentation_to_json(const ScriptingDocumentation& doc)
{
std::unique_ptr<json_t> root(json_object());
// Should be incremented every time an incompatible change is made
json_object_set_new(root.get(), "version", json_integer(2));
{
json_t* actionArray = json_array();
for (const auto& action : doc.actions) {
json_array_append_new(actionArray, json_doc_generate_action_element(action));
}
json_object_set_new(root.get(), "actions", actionArray);
}
{
json_t* conditionArray = json_array();
for (const auto& cond : doc.conditions) {
json_array_append_new(conditionArray, json_string(cond.c_str()));
}
json_object_set_new(root.get(), "conditions", conditionArray);
}
{
json_t* enumObject = json_object();
for (const auto& enumVal : doc.enumerations) {
json_object_set_new(enumObject, enumVal.name.c_str(), json_integer(enumVal.value));
}
json_object_set_new(root.get(), "enums", enumObject);
}
{
json_t* globalVariables = json_array();
for (const auto& param : doc.globalVariables) {
json_array_append_new(globalVariables, json_doc_generate_param_element(param));
}
json_object_set_new(root.get(), "globalVars", globalVariables);
}
json_object_set_new(root.get(), "elements", json_doc_generate_elements(doc.elements));
const auto jsonStr = json_dump_string(root.get(), JSON_INDENT(2) | JSON_SORT_KEYS);
std::ofstream outStr("scripting.json");
outStr << jsonStr;
}
// Initializes the (global) scripting system, as well as any subsystems.
// script_close is handled by destructors
void script_init()
@@ -473,13 +213,12 @@ void script_init()
if (Output_scripting_meta) {
mprintf(("SCRIPTING: Outputting scripting metadata...\n"));
Script_system.OutputMeta("scripting.html");
scripting::output_html_doc(doc, "scripting.html");
}
if (Output_scripting_json) {
mprintf(("SCRIPTING: Outputting scripting metadata in JSON format...\n"));
documentation_to_json(doc);
scripting::output_json_doc(doc, "scripting.json");
}
exit(1);
}
mprintf(("SCRIPTING: Beginning main hook parse sequence....\n"));
@@ -1106,6 +845,8 @@ ScriptingDocumentation script_state::OutputDocumentation()
{
ScriptingDocumentation doc;
doc.name = StateName;
// Conditions
doc.conditions.reserve(static_cast<size_t>(Num_script_conditions));
for (int32_t i = 0; i < Num_script_conditions; i++) {
@@ -1134,100 +875,6 @@ ScriptingDocumentation script_state::OutputDocumentation()
return doc;
}
template <typename Container>
static void output_hook_variable_list(FILE* fp, const Container& vars)
{
fputs("<dl>", fp);
for (const auto& param : vars) {
fputs("<dt>", fp);
ade_output_type_link(fp, param.type);
fprintf(fp, " <i>%s</i></dt>", param.name);
fprintf(fp, "<dd><b>Description:</b> %s</dt>", param.description);
}
fputs("</dl>", fp);
}
int script_state::OutputMeta(const char* filename)
{
FILE* fp = fopen(filename, "w");
int i;
if (fp == nullptr) {
return 0;
}
fprintf(fp, "<html>\n<head>\n\t<title>Script Output - FSO v%s (%s)</title>\n</head>\n", FS_VERSION_FULL, StateName);
fputs("<body>", fp);
fprintf(fp, "\t<h1>Script Output - FSO v%s (%s)</h1>\n", FS_VERSION_FULL, StateName);
// Scripting languages links
fputs("<dl>", fp);
//***Hooks
fputs("<dt><h2>Conditional Hooks</h2></dt>", fp);
fputs("<dd><dl>", fp);
// Conditions
fputs("<dt><b>Conditions</b></dt>", fp);
for (i = 0; i < Num_script_conditions; i++) {
fprintf(fp, "<dd>%s</dd>", Script_conditions[i].name);
}
// Actions
fputs("<dt><b>Actions</b></dt>", fp);
fputs("<dd><dl>", fp);
auto sortedHooks = scripting::getHooks();
std::sort(sortedHooks.begin(),
sortedHooks.end(),
[](const scripting::HookBase* left, const scripting::HookBase* right) {
return left->getHookName() < right->getHookName();
});
for (const auto& hook : sortedHooks) {
fprintf(fp, "<dt><b>%s</b></dt>", hook->getHookName().c_str());
if (!hook->getDescription().empty()) {
fprintf(fp, "<dd><b>Description:</b> %s", hook->getDescription().c_str());
// Only write this for hooks with descriptions since this information is useless for legacy hooks
if (hook->isOverridable()) {
fputs("<br><i>This hook is overridable</i>", fp);
} else {
fputs("<br><i>This hook is <b>not</b> overridable</i>", fp);
}
if (!hook->getParameters().empty()) {
fputs("<br><b>Hook Variables:</b>", fp);
output_hook_variable_list(fp, hook->getParameters());
}
fputs("</dd>", fp);
}
}
fputs("</dl></dd>", fp);
fputs("<dt><b>Global Hook Variables:</b> (set globally independent of a specific hook)</dt>", fp);
output_hook_variable_list(fp, GlobalVariables);
fputs("</dl></dd><br />", fp);
//***Scripting langs
fputs("<dt><h2>Scripting languages</h2></dt>", fp);
if (Langs & SC_LUA) {
fputs("<dd><a href=\"#Lua\">Lua</a></dd>", fp);
}
fputs("</dl>", fp);
// Languages
fputs("<dl>", fp);
if (Langs & SC_LUA) {
fputs("<dt><H2><a name=\"#Lua\">Lua</a></H2></dt>", fp);
fputs("<dd>", fp);
OutputLuaMeta(fp);
fputs("</dd>", fp);
}
fputs("</dl></body></html>", fp);
fclose(fp);
return 1;
}
void script_state::ParseChunkSub(script_function& script_func, const char* debug_str)
{
using namespace luacpp;
+7 -91
View File
@@ -3,20 +3,22 @@
#include "globalincs/globals.h"
#include "globalincs/pstypes.h"
#include "graphics/2d.h"
#include "scripting/ade_args.h"
#include "scripting/doc_parser.h"
#include "scripting/lua/LuaFunction.h"
#include "utils/event.h"
#include <cstdio>
//**********Scripting languages that are possible
#define SC_LUA (1<<0)
//*************************Scripting structs*************************
#define SCRIPT_END_LIST NULL
namespace scripting {
struct ScriptingDocumentation;
}
struct image_desc
{
char fname[MAX_FILENAME_LEN];
@@ -148,90 +150,6 @@ public:
bool Run(class script_state* sys, int action);
};
enum class ElementType {
Unknown,
Library,
Class,
Function,
Operator,
Property,
};
struct HookVariableDocumentation {
const char* name = nullptr;
scripting::ade_type_info type;
const char* description = nullptr;
HookVariableDocumentation(const char* name_, scripting::ade_type_info type_, const char* description_);
};
struct DocumentationElement {
virtual ~DocumentationElement() = default;
ElementType type = ElementType::Unknown;
SCP_string name;
SCP_string shortName;
SCP_string description;
SCP_vector<std::unique_ptr<DocumentationElement>> children;
};
struct DocumentationElementClass : public DocumentationElement {
~DocumentationElementClass() override = default;
SCP_string superClass;
};
struct DocumentationElementProperty : public DocumentationElement {
~DocumentationElementProperty() override = default;
scripting::ade_type_info getterType;
SCP_string setterType;
SCP_string returnDocumentation;
};
struct DocumentationElementFunction : public DocumentationElement {
~DocumentationElementFunction() override = default;
scripting::ade_type_info returnType;
struct argument_list {
SCP_string simple;
SCP_vector<scripting::argument_def> arguments;
};
SCP_vector<argument_list> overloads;
SCP_string returnDocumentation;
};
struct DocumentationEnum {
SCP_string name;
int value;
};
struct DocumentationAction {
SCP_string name;
SCP_string description;
SCP_vector<HookVariableDocumentation> parameters;
bool overridable;
};
struct ScriptingDocumentation {
SCP_vector<SCP_string> conditions;
SCP_vector<HookVariableDocumentation> globalVariables;
SCP_vector<DocumentationAction> actions;
SCP_vector<std::unique_ptr<DocumentationElement>> elements;
SCP_vector<DocumentationEnum> enumerations;
};
//**********Main script_state function
class script_state
{
@@ -254,8 +172,7 @@ private:
void SetLuaSession(struct lua_State *L);
void OutputLuaMeta(FILE *fp);
void OutputLuaDocumentation(ScriptingDocumentation& doc);
void OutputLuaDocumentation(scripting::ScriptingDocumentation& doc);
//Lua private helper functions
bool OpenHookVarTable();
@@ -288,8 +205,7 @@ public:
int CreateLuaState();
//***Get data
ScriptingDocumentation OutputDocumentation();
int OutputMeta(const char *filename);
scripting::ScriptingDocumentation OutputDocumentation();
//***Moves data
//void MoveData(script_state &in);
+91
View File
@@ -0,0 +1,91 @@
#pragma once
#include "globalincs/pstypes.h"
#include "scripting/ade_doc.h"
#include "scripting/doc_parser.h"
#include "scripting/hook_api.h"
namespace scripting {
enum class ElementType {
Unknown,
Library,
Class,
Function,
Operator,
Property,
};
struct DocumentationElement {
virtual ~DocumentationElement() = default;
ElementType type = ElementType::Unknown;
SCP_string name;
SCP_string shortName;
SCP_string description;
gameversion::version deprecationVersion;
SCP_string deprecationMessage;
SCP_vector<std::unique_ptr<DocumentationElement>> children;
};
struct DocumentationElementClass : public DocumentationElement {
~DocumentationElementClass() override = default;
SCP_string superClass;
};
struct DocumentationElementProperty : public DocumentationElement {
~DocumentationElementProperty() override = default;
scripting::ade_type_info getterType;
SCP_string setterType;
SCP_string returnDocumentation;
};
struct DocumentationElementFunction : public DocumentationElement {
~DocumentationElementFunction() override = default;
scripting::ade_type_info returnType;
struct argument_list {
SCP_string simple;
SCP_vector<scripting::argument_def> arguments;
};
SCP_vector<argument_list> overloads;
SCP_string returnDocumentation;
};
struct DocumentationEnum {
SCP_string name;
int value;
};
struct DocumentationAction {
SCP_string name;
SCP_string description;
SCP_vector<HookVariableDocumentation> parameters;
bool overridable;
};
struct ScriptingDocumentation {
SCP_string name;
SCP_vector<SCP_string> conditions;
SCP_vector<HookVariableDocumentation> globalVariables;
SCP_vector<DocumentationAction> actions;
SCP_vector<std::unique_ptr<DocumentationElement>> elements;
SCP_vector<DocumentationEnum> enumerations;
};
}
+5
View File
@@ -1066,6 +1066,10 @@ add_file_folder("Scripting"
scripting/ade_args.h
scripting/ade_doc.cpp
scripting/ade_doc.h
scripting/doc_html.cpp
scripting/doc_html.h
scripting/doc_json.cpp
scripting/doc_json.h
scripting/doc_parser.cpp
scripting/doc_parser.h
scripting/hook_api.cpp
@@ -1073,6 +1077,7 @@ add_file_folder("Scripting"
scripting/lua.cpp
scripting/scripting.cpp
scripting/scripting.h
scripting/scripting_doc.h
)
add_file_folder("Scripting\\\\Api"