mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-07 08:15:59 -10:00
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:
1 parent
bf8416d0cf
commit
4a6628e16f
13 files changed
+954
-903
No files matched your search
+34
-334
@@ -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> </dd>\n", ReturnDescription);
|
||||
} else {
|
||||
fputs("<dd><b>Returns:</b> Nothing<br> </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
@@ -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 {
|
||||
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "scripting_doc.h"
|
||||
|
||||
namespace scripting {
|
||||
|
||||
void output_html_doc(const ScriptingDocumentation& doc, const SCP_string& filename);
|
||||
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "scripting_doc.h"
|
||||
|
||||
namespace scripting {
|
||||
|
||||
void output_json_doc(const ScriptingDocumentation& doc, const SCP_string& filename);
|
||||
|
||||
}
|
||||
@@ -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,
|
||||
|
||||
@@ -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
@@ -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);
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
}
|
||||
@@ -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"
|
||||
|
||||
Reference in new issue
Block a user