2013-09-09 16:28:02 -04:00
-- main.lua
-- Implements the plugin entrypoint (in this case the entire plugin)
2013-07-30 10:02:20 -04:00
2013-07-30 11:26:06 -04:00
2013-09-11 10:57:57 -04:00
-- Global variables:
2014-03-19 08:57:37 -04:00
local g_Plugin = nil
local g_PluginFolder = " "
local g_Stats = { }
local g_TrackedPages = { }
2013-09-11 10:57:57 -04:00
2013-07-30 11:26:06 -04:00
2014-03-19 08:57:37 -04:00
local function LoadAPIFiles ( a_Folder , a_DstTable )
2014-03-14 11:07:22 -04:00
assert ( type ( a_Folder ) == " string " )
assert ( type ( a_DstTable ) == " table " )
2015-08-11 16:48:55 -04:00
2013-11-23 15:26:24 -05:00
local Folder = g_PluginFolder .. a_Folder ;
2014-03-19 08:57:37 -04:00
for _ , fnam in ipairs ( cFile : GetFolderContents ( Folder ) ) do
2013-11-23 15:26:24 -05:00
local FileName = Folder .. fnam ;
-- We only want .lua files from the folder:
if ( cFile : IsFile ( FileName ) and fnam : match ( " .*%.lua$ " ) ) then
2016-10-25 12:49:42 -04:00
local TablesFn = assert ( loadfile ( FileName ) )
local Tables = TablesFn ( )
if ( type ( Tables ) ~= " table " ) then
error ( " Cannot load API descriptions from " .. FileName .. " , returned object is not a table ( " .. type ( Tables ) .. " ). " )
break
end
for k , cls in pairs ( Tables ) do
if ( a_DstTable [ k ] ) then
-- The class is documented in two files, warn and store into a file (so that CIs can mark build as failure):
LOGWARNING ( string.format (
2021-06-22 18:09:06 -04:00
" Warning: class %s is documented at two places, the documentation in file %s will overwrite the previously loaded one! " ,
2016-10-25 12:49:42 -04:00
k , FileName
) )
local f = io.open ( " DuplicateDocs.txt " , " a " )
f : write ( k , " \t " , FileName )
f : close ( )
2013-11-23 15:26:24 -05:00
end
2016-10-25 12:49:42 -04:00
a_DstTable [ k ] = cls
end
2013-11-23 15:26:24 -05:00
end -- if (is lua file)
end -- for fnam - Folder[]
end
2016-05-31 17:57:53 -04:00
--- Returns the API currently detected from the global environment
2014-03-19 08:57:37 -04:00
local function CreateAPITables ( )
2013-09-09 16:28:02 -04:00
--[[
We want an API table of the following shape :
local API = {
2013-09-11 10:57:57 -04:00
{
Name = " cCuboid " ,
2013-09-09 16:28:02 -04:00
Functions = {
2013-09-11 10:57:57 -04:00
{ Name = " Sort " } ,
{ Name = " IsInside " }
2013-09-09 16:28:02 -04:00
} ,
Constants = {
2013-10-20 04:33:40 -04:00
} ,
Variables = {
} ,
2013-09-13 04:51:01 -04:00
Descendants = { } , -- Will be filled by ReadDescriptions(), array of class APIs (references to other member in the tree)
2015-03-19 16:29:23 -04:00
} ,
2013-09-11 10:57:57 -04:00
{
Name = " cBlockArea " ,
2013-09-09 16:28:02 -04:00
Functions = {
2013-09-11 10:57:57 -04:00
{ Name = " Clear " } ,
{ Name = " CopyFrom " } ,
2013-09-09 16:28:02 -04:00
...
2013-10-20 04:33:40 -04:00
} ,
2013-09-09 16:28:02 -04:00
Constants = {
2013-09-11 10:57:57 -04:00
{ Name = " baTypes " , Value = 0 } ,
{ Name = " baMetas " , Value = 1 } ,
2013-09-09 16:28:02 -04:00
...
2013-10-20 04:33:40 -04:00
} ,
Variables = {
} ,
2013-09-09 16:28:02 -04:00
...
2015-03-19 16:29:23 -04:00
} ,
2015-08-11 16:48:55 -04:00
2015-03-19 16:29:23 -04:00
cCuboid = { } -- Each array item also has the map item by its name
2013-09-09 16:28:02 -04:00
} ;
local Globals = {
Functions = {
...
} ,
Constants = {
...
}
} ;
--]]
2013-10-19 16:07:06 -04:00
local Globals = { Functions = { } , Constants = { } , Variables = { } , Descendants = { } } ;
2013-09-09 16:28:02 -04:00
local API = { } ;
2015-08-11 16:48:55 -04:00
2013-09-13 08:05:18 -04:00
local function Add ( a_APIContainer , a_ObjName , a_ObjValue )
if ( type ( a_ObjValue ) == " function " ) then
table.insert ( a_APIContainer.Functions , { Name = a_ObjName } ) ;
2013-09-13 04:51:01 -04:00
elseif (
2013-09-13 08:05:18 -04:00
( type ( a_ObjValue ) == " number " ) or
( type ( a_ObjValue ) == " string " )
2013-09-13 04:51:01 -04:00
) then
2013-09-13 08:05:18 -04:00
table.insert ( a_APIContainer.Constants , { Name = a_ObjName , Value = a_ObjValue } ) ;
2013-09-09 16:28:02 -04:00
end
end
2015-08-11 16:48:55 -04:00
2013-09-11 10:57:57 -04:00
local function ParseClass ( a_ClassName , a_ClassObj )
2013-10-19 16:07:06 -04:00
local res = { Name = a_ClassName , Functions = { } , Constants = { } , Variables = { } , Descendants = { } } ;
-- Add functions and constants:
2013-09-09 16:28:02 -04:00
for i , v in pairs ( a_ClassObj ) do
Add ( res , i , v ) ;
end
2015-08-11 16:48:55 -04:00
2013-10-19 16:07:06 -04:00
-- Member variables:
2021-05-03 16:07:09 -04:00
local GetField = a_ClassObj [ " .get " ] ;
2013-10-22 16:07:39 -04:00
local SetField = a_ClassObj [ " .set " ] or { } ;
2021-05-03 16:07:09 -04:00
if ( ( GetField ~= nil ) and ( type ( GetField ) == " table " ) ) then
for k , v in pairs ( GetField ) do
if ( ( SetField [ k ] == nil ) and ( ( type ( v ) ~= " table " ) or ( v [ " __newindex " ] == nil ) ) ) then
-- It is a read-only variable or array, add it as a constant:
2013-10-22 16:07:39 -04:00
table.insert ( res.Constants , { Name = k , Value = " " } ) ;
else
2021-05-03 16:07:09 -04:00
-- It is a read-write variable or array, add it as a variable:
2013-10-22 16:07:39 -04:00
table.insert ( res.Variables , { Name = k } ) ;
end
2013-10-19 16:07:06 -04:00
end
end
2013-09-09 16:28:02 -04:00
return res ;
end
2015-08-11 16:48:55 -04:00
2013-09-09 16:28:02 -04:00
for i , v in pairs ( _G ) do
2013-09-13 10:38:39 -04:00
if (
( v ~= _G ) and -- don't want the global namespace
( v ~= _G.packages ) and -- don't want any packages
2016-05-31 17:57:53 -04:00
( v ~= _G [ " .get " ] )
2013-09-13 10:38:39 -04:00
) then
if ( type ( v ) == " table " ) then
2015-03-19 16:29:23 -04:00
local cls = ParseClass ( i , v )
table.insert ( API , cls ) ;
API [ cls.Name ] = cls
2013-09-13 10:38:39 -04:00
else
Add ( Globals , i , v ) ;
end
2013-09-09 16:28:02 -04:00
end
end
2015-08-11 16:48:55 -04:00
2016-09-09 05:19:22 -04:00
-- Remove the built-in Lua libraries:
API.debug = nil
API.io = nil
API.string = nil
API.table = nil
2013-09-10 16:02:18 -04:00
return API , Globals ;
end
2015-06-04 06:23:38 -04:00
--- Returns the timestamp in HTML format
-- The timestamp will be inserted to all generated HTML files
local function GetHtmlTimestamp ( )
2017-08-14 23:32:28 -04:00
return string.format ( " <div id='timestamp'>Generated by <a href='https://github.com/cuberite/cuberite/tree/master/Server/Plugins/APIDump'>APIDump</a> on %s, Build ID %s, Commit %s</div> " ,
2015-06-05 04:34:13 -04:00
os.date ( " %Y-%m-%d %H:%M:%S " ) ,
cRoot : GetBuildID ( ) , cRoot : GetBuildCommitID ( )
)
2015-06-04 06:23:38 -04:00
end
2016-05-31 17:57:53 -04:00
--- Writes links to articles in a bullet format into the output HTML file
-- f is the output file stream
-- a_APIDesc is the API description as read from APIDesc.lua
local function WriteArticles ( f , a_APIDesc )
2013-12-27 09:01:49 -05:00
f : write ( [ [
< a name = " articles " >< h2 > Articles </ h2 ></ a >
< p > The following articles provide various extra information on plugin development </ p >
< ul >
] ] ) ;
2016-05-31 17:57:53 -04:00
for _ , extra in ipairs ( a_APIDesc.ExtraPages ) do
2013-12-27 09:01:49 -05:00
local SrcFileName = g_PluginFolder .. " / " .. extra.FileName ;
2015-09-26 16:54:18 -04:00
if ( cFile : IsFile ( SrcFileName ) ) then
2013-12-27 09:01:49 -05:00
local DstFileName = " API/ " .. extra.FileName ;
2015-09-26 16:54:18 -04:00
cFile : Delete ( DstFileName ) ;
2013-12-27 09:01:49 -05:00
cFile : Copy ( SrcFileName , DstFileName ) ;
f : write ( " <li><a href= \" " .. extra.FileName .. " \" > " .. extra.Title .. " </a></li> \n " ) ;
else
f : write ( " <li> " .. extra.Title .. " <i>(file is missing)</i></li> \n " ) ;
end
end
f : write ( " </ul><hr /> " ) ;
end
2014-03-19 08:57:37 -04:00
-- Make a link out of anything with the special linkifying syntax {{link|title}}
2017-08-03 09:40:40 -04:00
local function LinkifyString ( a_String , a_Referrer , a_API )
2014-03-19 08:57:37 -04:00
assert ( a_Referrer ~= nil ) ;
assert ( a_Referrer ~= " " ) ;
2015-08-11 16:48:55 -04:00
2017-08-03 09:40:40 -04:00
-- If the string is a known class, return a direct link to it:
if ( a_API [ a_String ] ) then
return " <a href= \" " .. a_String .. " .html \" > " .. a_String .. " </a> "
end
2014-03-19 08:57:37 -04:00
--- Adds a page to the list of tracked pages (to be checked for existence at the end)
local function AddTrackedPage ( a_PageName )
local Pg = ( g_TrackedPages [ a_PageName ] or { } ) ;
table.insert ( Pg , a_Referrer ) ;
g_TrackedPages [ a_PageName ] = Pg ;
2013-12-27 09:08:27 -05:00
end
2015-08-11 16:48:55 -04:00
2014-03-19 08:57:37 -04:00
--- Creates the HTML for the specified link and title
local function CreateLink ( Link , Title )
2017-04-07 08:31:44 -04:00
if ( ( Link : sub ( 1 , 7 ) == " http:// " ) or ( Link : sub ( 1 , 8 ) == " https:// " ) ) then
2014-03-19 08:57:37 -04:00
-- The link is a full absolute URL, do not modify, do not track:
return " <a href= \" " .. Link .. " \" > " .. Title .. " </a> " ;
end
local idxHash = Link : find ( " # " ) ;
if ( idxHash ~= nil ) then
-- The link contains an anchor:
if ( idxHash == 1 ) then
-- Anchor in the current page, no need to track:
return " <a href= \" " .. Link .. " \" > " .. Title .. " </a> " ;
end
-- Anchor in another page:
local PageName = Link : sub ( 1 , idxHash - 1 ) ;
AddTrackedPage ( PageName ) ;
return " <a href= \" " .. PageName .. " .html# " .. Link : sub ( idxHash + 1 ) .. " \" > " .. Title .. " </a> " ;
end
-- Link without anchor:
AddTrackedPage ( Link ) ;
return " <a href= \" " .. Link .. " .html \" > " .. Title .. " </a> " ;
end
2015-08-11 16:48:55 -04:00
2014-03-19 08:57:37 -04:00
-- Linkify the strings using the CreateLink() function:
local txt = a_String : gsub ( " {{([^|}]*)|([^}]*)}} " , CreateLink ) -- {{link|title}}
txt = txt : gsub ( " {{([^|}]*)}} " , -- {{LinkAndTitle}}
function ( LinkAndTitle )
local idxHash = LinkAndTitle : find ( " # " ) ;
if ( idxHash ~= nil ) then
-- The LinkAndTitle contains a hash, remove the hashed part from the title:
return CreateLink ( LinkAndTitle , LinkAndTitle : sub ( 1 , idxHash - 1 ) ) ;
end
return CreateLink ( LinkAndTitle , LinkAndTitle ) ;
end
) ;
return txt ;
end
2017-08-03 09:40:40 -04:00
local function WriteHtmlHook ( a_Hook , a_HookNav , a_API )
2014-03-19 08:57:37 -04:00
local fnam = " API/ " .. a_Hook.DefaultFnName .. " .html " ;
local f , error = io.open ( fnam , " w " ) ;
if ( f == nil ) then
LOG ( " Cannot write \" " .. fnam .. " \" : \" " .. error .. " \" . " ) ;
return ;
end
local HookName = a_Hook.DefaultFnName ;
2015-08-11 16:48:55 -04:00
2014-03-19 08:57:37 -04:00
f : write ( [ [ < ! DOCTYPE html >< html >
< head >
2015-08-11 16:48:55 -04:00
< title > Cuberite API - ] ] , HookName , [ [ Hook </ title >
2016-12-19 11:08:19 -05:00
< link rel = " canonical " href = " https://api.cuberite.org/]], HookName, [[.html " >
2014-03-19 08:57:37 -04:00
< link rel = " stylesheet " type = " text/css " href = " main.css " />
< link rel = " stylesheet " type = " text/css " href = " prettify.css " />
< script src = " prettify.js " ></ script >
< script src = " lang-lua.js " ></ script >
</ head >
< body >
< div id = " content " >
< header >
< h1 > ] ] , a_Hook.Name , [ [ </ h1 >
2013-12-27 09:08:27 -05:00
< hr />
2014-03-19 08:57:37 -04:00
</ header >
< table >< tr >< td style = " vertical-align: top; " >
Index : < br />
< a href = ' index.html#articles ' > Articles </ a >< br />
< a href = ' index.html#classes ' > Classes </ a >< br />
< a href = ' index.html#hooks ' > Hooks </ a >< br />
< br />
Quick navigation : < br />
2013-12-27 09:08:27 -05:00
] ] ) ;
2014-03-19 08:57:37 -04:00
f : write ( a_HookNav ) ;
f : write ( [ [
</ td >< td style = " vertical-align: top; " >< p >
] ] ) ;
2017-08-03 09:40:40 -04:00
f : write ( LinkifyString ( a_Hook.Desc , HookName , a_API ) ) ;
2014-03-19 08:57:37 -04:00
f : write ( " </p> \n <hr /><h1>Callback function</h1> \n <p>The default name for the callback function is " ) ;
f : write ( a_Hook.DefaultFnName , " . It has the following signature: \n " ) ;
2017-05-08 23:50:55 -04:00
f : write ( " <pre class= \" prettyprint lang-lua \" >function My " , HookName , " ( " ) ;
2014-03-19 08:57:37 -04:00
if ( a_Hook.Params == nil ) then
a_Hook.Params = { } ;
end
for i , param in ipairs ( a_Hook.Params ) do
if ( i > 1 ) then
f : write ( " , " ) ;
end
f : write ( param.Name ) ;
end
f : write ( " )</pre> \n <hr /><h1>Parameters:</h1> \n <table><tr><th>Name</th><th>Type</th><th>Notes</th></tr> \n " ) ;
for _ , param in ipairs ( a_Hook.Params ) do
2017-08-03 09:40:40 -04:00
f : write ( " <tr><td> " , param.Name , " </td><td> " , LinkifyString ( param.Type , HookName , a_API ) , " </td><td> " , LinkifyString ( param.Notes , HookName , a_API ) , " </td></tr> \n " ) ;
2014-03-19 08:57:37 -04:00
end
2017-08-03 09:40:40 -04:00
f : write ( " </table> \n <p> " .. LinkifyString ( a_Hook.Returns or " " , HookName , a_API ) .. " </p> \n \n " ) ;
2014-03-19 08:57:37 -04:00
f : write ( [[<hr /><h1>Code examples</h1><h2>Registering the callback</h2>]] ) ;
f : write ( " <pre class= \" prettyprint lang-lua \" > \n " ) ;
f : write ( [[cPluginManager:AddHook(cPluginManager.]] .. a_Hook.Name .. " , My " .. a_Hook.DefaultFnName .. [[);]] ) ;
f : write ( " </pre> \n \n " ) ;
local Examples = a_Hook.CodeExamples or { } ;
for _ , example in ipairs ( Examples ) do
f : write ( " <h2> " , ( example.Title or " <i>missing Title</i> " ) , " </h2> \n " ) ;
f : write ( " <p> " , ( example.Desc or " <i>missing Desc</i> " ) , " </p> \n " ) ;
f : write ( " <pre class= \" prettyprint lang-lua \" > " , ( example.Code or " <i>missing Code</i> " ) , " \n </pre> \n \n " ) ;
end
2015-06-04 06:23:38 -04:00
f : write ( [[</td></tr></table></div><script>prettyPrint();</script>]] )
f : write ( GetHtmlTimestamp ( ) )
f : write ( [[</body></html>]] )
2014-03-19 08:57:37 -04:00
f : close ( ) ;
2013-12-27 09:08:27 -05:00
end
2016-05-31 17:57:53 -04:00
--- Writes all hooks into HTML output file as links in a sorted bullet list, as well as the individual hook HTML files
-- f is the output HTML index file
-- a_Hooks is an array of hook descriptions
-- a_UndocumentedHooks is a table that will be filled with the names of hooks that are not documented
-- a_HookNav is the HTML code for the menu on the left that is constant for all hook pages
2017-08-03 09:40:40 -04:00
local function WriteHooks ( f , a_Hooks , a_UndocumentedHooks , a_HookNav , a_API )
2013-12-27 09:08:27 -05:00
f : write ( [ [
< a name = " hooks " >< h2 > Hooks </ h2 ></ a >
< p >
A plugin can register to be called whenever an " interesting event " occurs . It does so by calling
< a href = " cPluginManager.html " > cPluginManager </ a > ' s AddHook() function and implementing a callback
function to handle the event . </ p >
< p >
A plugin can decide whether it will let the event pass through to the rest of the plugins , or hide it
from them . This is determined by the return value from the hook callback function . If the function
returns false or no value , the event is propagated further . If the function returns true , the processing
2015-08-11 16:48:55 -04:00
is stopped , no other plugin receives the notification ( and possibly Cuberite disables the default
2013-12-27 09:08:27 -05:00
behavior for the event ) . See each hook ' s details to see the exact behavior.</p>
< table >
< tr >
< th > Hook name </ th >
< th > Called when </ th >
</ tr >
] ] ) ;
2014-03-19 08:57:37 -04:00
for _ , hook in ipairs ( a_Hooks ) do
2013-12-27 09:08:27 -05:00
if ( hook.DefaultFnName == nil ) then
-- The hook is not documented yet
f : write ( " <tr> \n <td> " .. hook.Name .. " </td> \n <td><i>(No documentation yet)</i></td> \n </tr> \n " ) ;
table.insert ( a_UndocumentedHooks , hook.Name ) ;
else
2017-08-03 09:40:40 -04:00
f : write ( " <tr> \n <td><a href= \" " .. hook.DefaultFnName .. " .html \" > " .. hook.Name .. " </a></td> \n <td> " .. LinkifyString ( hook.CalledWhen , hook.Name , a_API ) .. " </td> \n </tr> \n " ) ;
WriteHtmlHook ( hook , a_HookNav , a_API ) ;
2013-12-27 09:08:27 -05:00
end
end
f : write ( [ [
</ table >
< hr />
] ] ) ;
end
2016-05-31 17:57:53 -04:00
--- Fills the API in a_API table with descriptions from a_Desc
-- a_API is the API detected from current global environment
-- a_Desc is the description loaded from APIDesc.lua and Classes files
local function ReadDescriptions ( a_API , a_Desc )
2013-09-28 18:10:42 -04:00
-- Returns true if the class of the specified name is to be ignored
local function IsClassIgnored ( a_ClsName )
2016-05-31 17:57:53 -04:00
if ( a_Desc.IgnoreClasses == nil ) then
2013-09-28 18:10:42 -04:00
return false ;
end
2016-05-31 17:57:53 -04:00
for _ , name in ipairs ( a_Desc.IgnoreClasses ) do
2013-09-28 18:10:42 -04:00
if ( a_ClsName : match ( name ) ) then
return true ;
end
end
return false ;
end
2015-08-11 16:48:55 -04:00
2013-11-15 16:39:02 -05:00
-- Returns true if the function is to be ignored
local function IsFunctionIgnored ( a_ClassName , a_FnName )
2016-05-31 17:57:53 -04:00
if ( a_Desc.IgnoreFunctions == nil ) then
2013-09-14 16:15:58 -04:00
return false ;
end
2016-05-31 17:57:53 -04:00
if ( ( ( a_Desc.Classes [ a_ClassName ] or { } ) . Functions or { } ) [ a_FnName ] ~= nil ) then
2013-11-15 16:39:02 -05:00
-- The function is documented, don't ignore
return false ;
end
local FnName = a_ClassName .. " . " .. a_FnName ;
2016-05-31 17:57:53 -04:00
for _ , name in ipairs ( a_Desc.IgnoreFunctions ) do
2013-11-15 16:39:02 -05:00
if ( FnName : match ( name ) ) then
2013-09-13 08:05:18 -04:00
return true ;
end
end
return false ;
end
2015-08-11 16:48:55 -04:00
2013-09-14 16:15:58 -04:00
-- Returns true if the constant (specified by its fully qualified name) is to be ignored
local function IsConstantIgnored ( a_CnName )
2016-05-31 17:57:53 -04:00
if ( a_Desc.IgnoreConstants == nil ) then
2013-09-14 16:15:58 -04:00
return false ;
end ;
2016-05-31 17:57:53 -04:00
for _ , name in ipairs ( a_Desc.IgnoreConstants ) do
2013-09-14 16:15:58 -04:00
if ( a_CnName : match ( name ) ) then
return true ;
end
end
return false ;
end
2015-08-11 16:48:55 -04:00
2013-10-19 16:07:06 -04:00
-- Returns true if the member variable (specified by its fully qualified name) is to be ignored
local function IsVariableIgnored ( a_VarName )
2016-05-31 17:57:53 -04:00
if ( a_Desc.IgnoreVariables == nil ) then
2013-10-19 16:07:06 -04:00
return false ;
end ;
2016-05-31 17:57:53 -04:00
for _ , name in ipairs ( a_Desc.IgnoreVariables ) do
2013-10-19 16:07:06 -04:00
if ( a_VarName : match ( name ) ) then
return true ;
end
end
return false ;
end
2015-08-11 16:48:55 -04:00
2013-09-28 18:10:42 -04:00
-- Remove ignored classes from a_API:
local APICopy = { } ;
2014-03-19 08:57:37 -04:00
for _ , cls in ipairs ( a_API ) do
2013-09-28 18:10:42 -04:00
if not ( IsClassIgnored ( cls.Name ) ) then
table.insert ( APICopy , cls ) ;
end
end
for i = 1 , # a_API do
a_API [ i ] = APICopy [ i ] ;
end ;
-- Process the documentation for each class:
2014-03-19 08:57:37 -04:00
for _ , cls in ipairs ( a_API ) do
2013-11-22 15:46:06 -05:00
-- Initialize default values for each class:
cls.ConstantGroups = { } ;
cls.NumConstantsInGroups = 0 ;
cls.NumConstantsInGroupsForDescendants = 0 ;
2015-08-11 16:48:55 -04:00
2013-09-14 02:38:39 -04:00
-- Rename special functions:
2014-03-19 08:57:37 -04:00
for _ , fn in ipairs ( cls.Functions ) do
2013-09-14 02:38:39 -04:00
if ( fn.Name == " .call " ) then
fn.DocID = " constructor " ;
fn.Name = " () <i>(constructor)</i> " ;
elseif ( fn.Name == " .add " ) then
fn.DocID = " operator_plus " ;
fn.Name = " <i>operator +</i> " ;
elseif ( fn.Name == " .div " ) then
fn.DocID = " operator_div " ;
fn.Name = " <i>operator /</i> " ;
elseif ( fn.Name == " .mul " ) then
fn.DocID = " operator_mul " ;
fn.Name = " <i>operator *</i> " ;
elseif ( fn.Name == " .sub " ) then
fn.DocID = " operator_sub " ;
fn.Name = " <i>operator -</i> " ;
2013-09-30 14:45:13 -04:00
elseif ( fn.Name == " .eq " ) then
2013-10-02 02:49:15 -04:00
fn.DocID = " operator_eq " ;
2013-09-30 14:45:13 -04:00
fn.Name = " <i>operator ==</i> " ;
2013-09-14 02:38:39 -04:00
end
end
2015-08-11 16:48:55 -04:00
2016-05-31 17:57:53 -04:00
local APIDesc = a_Desc.Classes [ cls.Name ] ;
2013-09-11 10:57:57 -04:00
if ( APIDesc ~= nil ) then
2013-09-14 16:34:19 -04:00
APIDesc.IsExported = true ;
2013-09-11 10:57:57 -04:00
cls.Desc = APIDesc.Desc ;
2013-09-14 11:28:22 -04:00
cls.AdditionalInfo = APIDesc.AdditionalInfo ;
2015-08-11 16:48:55 -04:00
2013-09-13 04:51:01 -04:00
-- Process inheritance:
if ( APIDesc.Inherits ~= nil ) then
2014-03-19 08:57:37 -04:00
for _ , icls in ipairs ( a_API ) do
2013-09-13 04:51:01 -04:00
if ( icls.Name == APIDesc.Inherits ) then
table.insert ( icls.Descendants , cls ) ;
cls.Inherits = icls ;
end
end
end
2013-09-14 10:52:15 -04:00
2013-09-14 16:15:58 -04:00
cls.UndocumentedFunctions = { } ; -- This will contain names of all the functions that are not documented
cls.UndocumentedConstants = { } ; -- This will contain names of all the constants that are not documented
2013-10-19 16:07:06 -04:00
cls.UndocumentedVariables = { } ; -- This will contain names of all the variables that are not documented
2015-08-11 16:48:55 -04:00
2013-09-14 10:52:15 -04:00
local DoxyFunctions = { } ; -- This will contain all the API functions together with their documentation
2015-08-11 16:48:55 -04:00
2016-09-12 17:21:15 -04:00
local function AddFunction ( a_Name , a_Params , a_Returns , a_IsStatic , a_Notes )
table.insert ( DoxyFunctions , { Name = a_Name , Params = a_Params , Returns = a_Returns , IsStatic = a_IsStatic , Notes = a_Notes } ) ;
2013-09-14 10:52:15 -04:00
end
2015-08-11 16:48:55 -04:00
2013-09-11 10:57:57 -04:00
if ( APIDesc.Functions ~= nil ) then
-- Assign function descriptions:
2014-03-19 08:57:37 -04:00
for _ , func in ipairs ( cls.Functions ) do
2013-09-14 02:38:39 -04:00
local FnName = func.DocID or func.Name ;
2013-09-13 10:04:32 -04:00
local FnDesc = APIDesc.Functions [ FnName ] ;
2013-09-14 10:52:15 -04:00
if ( FnDesc == nil ) then
-- No description for this API function
AddFunction ( func.Name ) ;
2013-11-15 16:39:02 -05:00
if not ( IsFunctionIgnored ( cls.Name , FnName ) ) then
2013-09-14 16:15:58 -04:00
table.insert ( cls.UndocumentedFunctions , FnName ) ;
end
2013-09-14 10:52:15 -04:00
else
-- Description is available
if ( FnDesc [ 1 ] == nil ) then
-- Single function definition
2016-09-12 17:21:15 -04:00
AddFunction ( func.Name , FnDesc.Params , FnDesc.Returns , FnDesc.IsStatic , FnDesc.Notes ) ;
2013-09-14 10:52:15 -04:00
else
-- Multiple function overloads
2014-03-19 08:57:37 -04:00
for _ , desc in ipairs ( FnDesc ) do
2016-09-12 17:21:15 -04:00
AddFunction ( func.Name , desc.Params , desc.Returns , desc.IsStatic , desc.Notes ) ;
2013-09-14 10:52:15 -04:00
end -- for k, desc - FnDesc[]
end
2013-09-11 10:57:57 -04:00
FnDesc.IsExported = true ;
end
end -- for j, func
2015-08-11 16:48:55 -04:00
2013-09-14 10:52:15 -04:00
-- Replace functions with their described and overload-expanded versions:
cls.Functions = DoxyFunctions ;
2013-10-20 04:33:40 -04:00
else -- if (APIDesc.Functions ~= nil)
2014-03-19 08:57:37 -04:00
for _ , func in ipairs ( cls.Functions ) do
2013-10-28 08:07:30 -04:00
local FnName = func.DocID or func.Name ;
2013-11-15 16:39:02 -05:00
if not ( IsFunctionIgnored ( cls.Name , FnName ) ) then
2013-10-20 04:33:40 -04:00
table.insert ( cls.UndocumentedFunctions , FnName ) ;
end
end
2013-09-11 10:57:57 -04:00
end -- if (APIDesc.Functions ~= nil)
2015-08-11 16:48:55 -04:00
2013-09-11 10:57:57 -04:00
if ( APIDesc.Constants ~= nil ) then
-- Assign constant descriptions:
2014-03-19 08:57:37 -04:00
for _ , cons in ipairs ( cls.Constants ) do
2013-09-11 10:57:57 -04:00
local CnDesc = APIDesc.Constants [ cons.Name ] ;
2013-09-14 10:52:15 -04:00
if ( CnDesc == nil ) then
-- Not documented
2013-09-14 16:15:58 -04:00
if not ( IsConstantIgnored ( cls.Name .. " . " .. cons.Name ) ) then
table.insert ( cls.UndocumentedConstants , cons.Name ) ;
end
2013-09-14 10:52:15 -04:00
else
2013-09-11 10:57:57 -04:00
cons.Notes = CnDesc.Notes ;
CnDesc.IsExported = true ;
end
end -- for j, cons
2013-10-20 04:33:40 -04:00
else -- if (APIDesc.Constants ~= nil)
2014-03-19 08:57:37 -04:00
for _ , cons in ipairs ( cls.Constants ) do
2013-10-20 04:33:40 -04:00
if not ( IsConstantIgnored ( cls.Name .. " . " .. cons.Name ) ) then
table.insert ( cls.UndocumentedConstants , cons.Name ) ;
end
end
end -- else if (APIDesc.Constants ~= nil)
2015-08-11 16:48:55 -04:00
2013-10-19 16:07:06 -04:00
-- Assign member variables' descriptions:
if ( APIDesc.Variables ~= nil ) then
2014-03-19 08:57:37 -04:00
for _ , var in ipairs ( cls.Variables ) do
2013-10-19 16:07:06 -04:00
local VarDesc = APIDesc.Variables [ var.Name ] ;
if ( VarDesc == nil ) then
-- Not documented
if not ( IsVariableIgnored ( cls.Name .. " . " .. var.Name ) ) then
table.insert ( cls.UndocumentedVariables , var.Name ) ;
end
else
-- Copy all documentation:
for k , v in pairs ( VarDesc ) do
var [ k ] = v
end
end
end -- for j, var
2013-10-20 04:33:40 -04:00
else -- if (APIDesc.Variables ~= nil)
2014-03-19 08:57:37 -04:00
for _ , var in ipairs ( cls.Variables ) do
2013-10-20 04:33:40 -04:00
if not ( IsVariableIgnored ( cls.Name .. " . " .. var.Name ) ) then
table.insert ( cls.UndocumentedVariables , var.Name ) ;
end
end
end -- else if (APIDesc.Variables ~= nil)
2015-08-11 16:48:55 -04:00
2013-11-22 15:46:06 -05:00
if ( APIDesc.ConstantGroups ~= nil ) then
-- Create links between the constants and the groups:
local NumInGroups = 0 ;
local NumInDescendantGroups = 0 ;
for j , group in pairs ( APIDesc.ConstantGroups ) do
group.Name = j ;
group.Constants = { } ;
2013-11-23 13:40:35 -05:00
if ( type ( group.Include ) == " string " ) then
2013-11-22 15:46:06 -05:00
group.Include = { group.Include } ;
end
local NumInGroup = 0 ;
2014-03-19 08:57:37 -04:00
for _ , incl in ipairs ( group.Include or { } ) do
for _ , cons in ipairs ( cls.Constants ) do
2013-11-22 15:46:06 -05:00
if ( ( cons.Group == nil ) and cons.Name : match ( incl ) ) then
cons.Group = group ;
table.insert ( group.Constants , cons ) ;
NumInGroup = NumInGroup + 1 ;
end
end -- for cidx - cls.Constants[]
end -- for idx - group.Include[]
NumInGroups = NumInGroups + NumInGroup ;
if ( group.ShowInDescendants ) then
NumInDescendantGroups = NumInDescendantGroups + NumInGroup ;
end
2015-08-11 16:48:55 -04:00
2013-11-22 15:46:06 -05:00
-- Sort the constants:
table.sort ( group.Constants ,
function ( c1 , c2 )
return ( c1.Name < c2.Name ) ;
end
) ;
end -- for j - APIDesc.ConstantGroups[]
cls.ConstantGroups = APIDesc.ConstantGroups ;
cls.NumConstantsInGroups = NumInGroups ;
cls.NumConstantsInGroupsForDescendants = NumInDescendantGroups ;
2015-08-11 16:48:55 -04:00
2013-11-22 15:46:06 -05:00
-- Remove grouped constants from the normal list:
local NewConstants = { } ;
2014-03-19 08:57:37 -04:00
for _ , cons in ipairs ( cls.Constants ) do
2013-11-22 15:46:06 -05:00
if ( cons.Group == nil ) then
table.insert ( NewConstants , cons ) ;
end
end
cls.Constants = NewConstants ;
end -- if (ConstantGroups ~= nil)
2015-08-11 16:48:55 -04:00
2013-10-18 15:29:38 -04:00
else -- if (APIDesc ~= nil)
2015-08-11 16:48:55 -04:00
2013-09-14 17:47:37 -04:00
-- Class is not documented at all, add all its members to Undocumented lists:
cls.UndocumentedFunctions = { } ;
cls.UndocumentedConstants = { } ;
2013-10-19 16:07:06 -04:00
cls.UndocumentedVariables = { } ;
cls.Variables = cls.Variables or { } ;
2013-10-20 05:12:33 -04:00
g_Stats.NumUndocumentedClasses = g_Stats.NumUndocumentedClasses + 1 ;
2014-03-19 08:57:37 -04:00
for _ , func in ipairs ( cls.Functions ) do
2013-09-14 17:47:37 -04:00
local FnName = func.DocID or func.Name ;
2013-11-15 16:39:02 -05:00
if not ( IsFunctionIgnored ( cls.Name , FnName ) ) then
2013-09-14 17:47:37 -04:00
table.insert ( cls.UndocumentedFunctions , FnName ) ;
end
end -- for j, func - cls.Functions[]
2014-03-19 08:57:37 -04:00
for _ , cons in ipairs ( cls.Constants ) do
2013-09-14 17:47:37 -04:00
if not ( IsConstantIgnored ( cls.Name .. " . " .. cons.Name ) ) then
table.insert ( cls.UndocumentedConstants , cons.Name ) ;
end
end -- for j, cons - cls.Constants[]
2014-03-19 08:57:37 -04:00
for _ , var in ipairs ( cls.Variables ) do
2013-10-19 16:07:06 -04:00
if not ( IsConstantIgnored ( cls.Name .. " . " .. var.Name ) ) then
table.insert ( cls.UndocumentedVariables , var.Name ) ;
end
end -- for j, var - cls.Variables[]
2013-09-14 17:47:37 -04:00
end -- else if (APIDesc ~= nil)
2015-08-11 16:48:55 -04:00
2013-09-14 02:27:31 -04:00
-- Remove ignored functions:
local NewFunctions = { } ;
2014-03-19 08:57:37 -04:00
for _ , fn in ipairs ( cls.Functions ) do
2013-11-15 16:39:02 -05:00
if ( not ( IsFunctionIgnored ( cls.Name , fn.Name ) ) ) then
2013-09-14 02:27:31 -04:00
table.insert ( NewFunctions , fn ) ;
end
end -- for j, fn
cls.Functions = NewFunctions ;
2013-09-14 10:20:54 -04:00
-- Sort the functions (they may have been renamed):
table.sort ( cls.Functions ,
function ( f1 , f2 )
return ( f1.Name < f2.Name ) ;
end
) ;
2015-08-11 16:48:55 -04:00
2013-12-16 16:13:31 -05:00
-- Remove ignored constants:
local NewConstants = { } ;
2014-03-19 08:57:37 -04:00
for _ , cn in ipairs ( cls.Constants ) do
2013-12-16 16:13:31 -05:00
if ( not ( IsFunctionIgnored ( cls.Name , cn.Name ) ) ) then
table.insert ( NewConstants , cn ) ;
end
end -- for j, cn
cls.Constants = NewConstants ;
2015-08-11 16:48:55 -04:00
2013-09-14 10:20:54 -04:00
-- Sort the constants:
table.sort ( cls.Constants ,
function ( c1 , c2 )
return ( c1.Name < c2.Name ) ;
end
) ;
2015-08-11 16:48:55 -04:00
2013-12-16 16:13:31 -05:00
-- Remove ignored member variables:
2013-10-19 16:21:38 -04:00
local NewVariables = { } ;
2014-03-19 08:57:37 -04:00
for _ , var in ipairs ( cls.Variables ) do
2013-10-19 16:21:38 -04:00
if ( not ( IsVariableIgnored ( cls.Name .. " . " .. var.Name ) ) ) then
table.insert ( NewVariables , var ) ;
end
end -- for j, var
cls.Variables = NewVariables ;
2015-08-11 16:48:55 -04:00
2013-10-18 15:29:38 -04:00
-- Sort the member variables:
table.sort ( cls.Variables ,
function ( v1 , v2 )
return ( v1.Name < v2.Name ) ;
end
) ;
2013-09-13 04:51:01 -04:00
end -- for i, cls
2015-08-11 16:48:55 -04:00
2013-09-13 04:51:01 -04:00
-- Sort the descendants lists:
2014-03-19 08:57:37 -04:00
for _ , cls in ipairs ( a_API ) do
2013-09-13 04:51:01 -04:00
table.sort ( cls.Descendants ,
function ( c1 , c2 )
return ( c1.Name < c2.Name ) ;
end
) ;
end -- for i, cls
2013-09-11 10:57:57 -04:00
end
2016-05-31 17:57:53 -04:00
--- Fills the hooks in a_Hooks with their descriptions from a_Descs
-- a_Hooks is an array of hooks detected from current global environment
-- a_Descs is the description read from APIDesc.lua and Hooks files
local function ReadHooks ( a_Hooks , a_Descs )
2013-10-09 09:10:25 -04:00
--[[
a_Hooks = {
{ Name = " HOOK_1 " } ,
{ Name = " HOOK_2 " } ,
...
} ;
We want to add hook descriptions to each hook in this array
--]]
2014-03-19 08:57:37 -04:00
for _ , hook in ipairs ( a_Hooks ) do
2016-05-31 17:57:53 -04:00
local HookDesc = a_Descs.Hooks [ hook.Name ] ;
2013-10-09 09:10:25 -04:00
if ( HookDesc ~= nil ) then
for key , val in pairs ( HookDesc ) do
hook [ key ] = val ;
end
end
end -- for i, hook - a_Hooks[]
2013-10-20 05:12:33 -04:00
g_Stats.NumTotalHooks = # a_Hooks ;
2013-10-09 09:10:25 -04:00
end
2016-09-09 05:19:22 -04:00
--- Returns a HTML string describing the (parameter) type, linking to the type's documentation, if available
-- a_Type is the string containing the type (such as "cPlugin" or "number"), or nil
-- a_API is the complete API description (used for searching the classnames)
local function LinkifyType ( a_Type , a_API )
-- Check params:
assert ( type ( a_Type ) == " string " )
assert ( type ( a_API ) == " table " )
-- If the type is a known class, return a direct link to it:
if ( a_API [ a_Type ] ) then
return " <a href= \" " .. a_Type .. " .html \" > " .. a_Type .. " </a> "
end
2016-09-12 17:21:15 -04:00
-- If the type has a hash sign, it's a child enum of a class:
local idxColon = a_Type : find ( " # " )
2016-09-09 05:19:22 -04:00
if ( idxColon ) then
local classType = a_Type : sub ( 1 , idxColon - 1 )
if ( a_API [ classType ] ) then
2016-09-12 17:21:15 -04:00
local enumType = a_Type : sub ( idxColon + 1 )
return " <a href= \" " .. classType .. " .html# " .. enumType .. " \" > " .. enumType .. " </a> "
2016-09-09 05:19:22 -04:00
end
end
2016-09-12 17:21:15 -04:00
-- If the type is a ConstantGroup within the Globals, it's a global enum:
if ( ( a_API.Globals . ConstantGroups or { } ) [ a_Type ] ) then
return " <a href= \" Globals.html# " .. a_Type .. " \" > " .. a_Type .. " </a> "
end
2016-09-09 05:19:22 -04:00
-- Unknown or built-in type, output just text:
return a_Type
end
--- Returns an HTML string describing all function parameters (or return values)
-- a_FnParams is an array-table or string description of the parameters
-- a_ClassName is the name of the class for which the function is being documented (for Linkification)
-- a_API is the complete API description (for cross-type linkification)
local function CreateFunctionParamsDescription ( a_FnParams , a_ClassName , a_API )
local pt = type ( a_FnParams )
assert ( ( pt == " string " ) or ( pt == " table " ) )
assert ( type ( a_ClassName ) == " string " )
assert ( type ( a_API ) == " table " )
-- If the params description is a string (old format), just linkify it:
if ( pt == " string " ) then
2017-08-03 09:40:40 -04:00
return LinkifyString ( a_FnParams , a_ClassName , a_API )
2016-09-09 05:19:22 -04:00
end
-- If the params description is an empty table, give no description at all:
if not ( a_FnParams [ 1 ] ) then
return " "
end
-- The params description is a table, output the full desc:
local res = { " <table border=0 cellspacing=0> " }
local idx = 2
for _ , param in ipairs ( a_FnParams ) do
res [ idx ] = " <tr><td> "
res [ idx + 1 ] = param.Name or " "
res [ idx + 2 ] = " </td><td><i> "
res [ idx + 3 ] = LinkifyType ( param.Type , a_API )
res [ idx + 4 ] = " </i></td></tr> "
idx = idx + 5
end
res [ idx ] = " </tr></table> "
return table.concat ( res )
end
--- Writes an HTML file containing the class API description for the given class
-- a_ClassAPI is the API description of the class to output
-- a_ClassMenu is the HTML string containing the code for the menu sidebar
-- a_API is the complete API (for cross-type linkification)
local function WriteHtmlClass ( a_ClassAPI , a_ClassMenu , a_API )
-- Check params:
assert ( type ( a_ClassAPI ) == " table " )
assert ( type ( a_ClassMenu ) == " string " )
assert ( type ( a_API ) == " table " )
2013-09-11 10:57:57 -04:00
local cf , err = io.open ( " API/ " .. a_ClassAPI.Name .. " .html " , " w " ) ;
2013-09-10 16:02:18 -04:00
if ( cf == nil ) then
2014-03-19 08:57:37 -04:00
LOGINFO ( " Cannot write HTML API for class " .. a_ClassAPI.Name .. " : " .. err )
2013-09-10 16:02:18 -04:00
return ;
end
2015-08-11 16:48:55 -04:00
2013-09-13 04:22:04 -04:00
-- Writes a table containing all functions in the specified list, with an optional "inherited from" header when a_InheritedName is valid
local function WriteFunctions ( a_Functions , a_InheritedName )
2016-09-09 05:19:22 -04:00
if not ( a_Functions [ 1 ] ) then
-- No functions to write
2013-09-13 04:22:04 -04:00
return ;
end
2013-09-14 10:20:54 -04:00
2016-09-09 05:19:22 -04:00
if ( a_InheritedName ) then
2013-11-24 04:03:02 -05:00
cf : write ( " <h2>Functions inherited from " , a_InheritedName , " </h2> \n " ) ;
2013-09-13 04:22:04 -04:00
end
2013-11-24 04:03:02 -05:00
cf : write ( " <table> \n <tr><th>Name</th><th>Parameters</th><th>Return value</th><th>Notes</th></tr> \n " ) ;
2016-09-06 15:03:18 -04:00
-- Store all function names, to create unique anchor names for all functions
local TableOverloadedFunctions = { }
2014-03-19 08:57:37 -04:00
for _ , func in ipairs ( a_Functions ) do
2016-07-06 10:11:15 -04:00
local StaticClause = " "
if ( func.IsStatic ) then
StaticClause = " (STATIC) "
end
2016-09-06 15:03:18 -04:00
-- Increase number by one
TableOverloadedFunctions [ func.Name ] = ( TableOverloadedFunctions [ func.Name ] or 0 ) + 1
-- Add the anchor names as a title
cf : write ( " <tr><td id= \" " , func.Name , " _ " , TableOverloadedFunctions [ func.Name ] , " \" title= \" " , func.Name , " _ " , TableOverloadedFunctions [ func.Name ] , " \" > " , func.Name , " </td> \n " ) ;
2016-09-09 05:19:22 -04:00
cf : write ( " <td> " , CreateFunctionParamsDescription ( func.Params or { } , a_InheritedName or a_ClassAPI.Name , a_API ) , " </td> \n " ) ;
2016-09-12 17:21:15 -04:00
cf : write ( " <td> " , CreateFunctionParamsDescription ( func.Returns or { } , a_InheritedName or a_ClassAPI.Name , a_API ) , " </td> \n " ) ;
2017-08-03 09:40:40 -04:00
cf : write ( " <td> " , StaticClause .. LinkifyString ( func.Notes or " <i>(undocumented)</i> " , ( a_InheritedName or a_ClassAPI.Name ) , a_API ) , " </td></tr> \n " ) ;
2013-09-13 04:22:04 -04:00
end
2013-11-24 04:03:02 -05:00
cf : write ( " </table> \n " ) ;
2013-09-13 04:22:04 -04:00
end
2015-08-11 16:48:55 -04:00
2013-11-22 15:46:06 -05:00
local function WriteConstantTable ( a_Constants , a_Source )
cf : write ( " <table> \n <tr><th>Name</th><th>Value</th><th>Notes</th></tr> \n " ) ;
2014-03-19 08:57:37 -04:00
for _ , cons in ipairs ( a_Constants ) do
2016-09-06 15:03:18 -04:00
cf : write ( " <tr><td id= \" " , cons.Name , " \" > " , cons.Name , " </td> \n " ) ;
2013-11-22 15:46:06 -05:00
cf : write ( " <td> " , cons.Value , " </td> \n " ) ;
2017-08-03 09:40:40 -04:00
cf : write ( " <td> " , LinkifyString ( cons.Notes or " " , a_Source , a_API ) , " </td></tr> \n " ) ;
2013-11-22 15:46:06 -05:00
end
cf : write ( " </table> \n \n " ) ;
end
2015-08-11 16:48:55 -04:00
2013-11-22 15:46:06 -05:00
local function WriteConstants ( a_Constants , a_ConstantGroups , a_NumConstantGroups , a_InheritedName )
if ( ( # a_Constants == 0 ) and ( a_NumConstantGroups == 0 ) ) then
2013-10-18 14:49:30 -04:00
return ;
end
2015-08-11 16:48:55 -04:00
2013-11-23 13:40:35 -05:00
local Source = a_ClassAPI.Name
2013-10-18 14:49:30 -04:00
if ( a_InheritedName ~= nil ) then
2013-11-22 15:46:06 -05:00
cf : write ( " <h2>Constants inherited from " , a_InheritedName , " </h2> \n " ) ;
2013-11-23 13:40:35 -05:00
Source = a_InheritedName ;
2013-10-18 14:49:30 -04:00
end
2015-08-11 16:48:55 -04:00
2013-11-22 15:46:06 -05:00
if ( # a_Constants > 0 ) then
2013-11-23 13:40:35 -05:00
WriteConstantTable ( a_Constants , Source ) ;
2013-11-22 15:46:06 -05:00
end
2015-08-11 16:48:55 -04:00
2014-03-19 08:57:37 -04:00
for _ , group in pairs ( a_ConstantGroups ) do
2013-11-22 15:46:06 -05:00
if ( ( a_InheritedName == nil ) or group.ShowInDescendants ) then
cf : write ( " <a name=' " , group.Name , " '><p> " ) ;
2017-08-03 09:40:40 -04:00
cf : write ( LinkifyString ( group.TextBefore or " " , Source , a_API ) ) ;
2013-11-22 15:46:06 -05:00
WriteConstantTable ( group.Constants , a_InheritedName or a_ClassAPI.Name ) ;
2017-08-03 09:40:40 -04:00
cf : write ( LinkifyString ( group.TextAfter or " " , Source , a_API ) , " </a></p><hr/> " ) ;
2013-11-22 15:46:06 -05:00
end
2013-10-18 14:49:30 -04:00
end
end
2015-08-11 16:48:55 -04:00
2013-10-18 15:29:38 -04:00
local function WriteVariables ( a_Variables , a_InheritedName )
if ( # a_Variables == 0 ) then
return ;
end
2015-08-11 16:48:55 -04:00
2013-10-18 15:29:38 -04:00
if ( a_InheritedName ~= nil ) then
2013-11-24 04:03:02 -05:00
cf : write ( " <h2>Member variables inherited from " , a_InheritedName , " </h2> \n " ) ;
2013-10-18 15:29:38 -04:00
end
2015-08-11 16:48:55 -04:00
2013-11-24 04:03:02 -05:00
cf : write ( " <table><tr><th>Name</th><th>Type</th><th>Notes</th></tr> \n " ) ;
2014-03-19 08:57:37 -04:00
for _ , var in ipairs ( a_Variables ) do
2016-09-06 15:03:18 -04:00
cf : write ( " <tr><td id= \" " , var.Name , " \" > " , var.Name , " </td> \n " ) ;
2017-08-03 09:40:40 -04:00
cf : write ( " <td> " , LinkifyString ( var.Type or " <i>(undocumented)</i> " , a_InheritedName or a_ClassAPI.Name , a_API ) , " </td> \n " ) ;
cf : write ( " <td> " , LinkifyString ( var.Notes or " " , a_InheritedName or a_ClassAPI.Name , a_API ) , " </td> \n </tr> \n " ) ;
2013-10-18 15:29:38 -04:00
end
2013-11-24 04:03:02 -05:00
cf : write ( " </table> \n \n " ) ;
2013-10-18 15:29:38 -04:00
end
2015-08-11 16:48:55 -04:00
2013-09-13 04:51:01 -04:00
local function WriteDescendants ( a_Descendants )
if ( # a_Descendants == 0 ) then
return ;
end
cf : write ( " <ul> " ) ;
2014-03-19 08:57:37 -04:00
for _ , desc in ipairs ( a_Descendants ) do
2013-11-19 03:56:36 -05:00
cf : write ( " <li><a href= \" " , desc.Name , " .html \" > " , desc.Name , " </a> " ) ;
2013-09-13 04:51:01 -04:00
WriteDescendants ( desc.Descendants ) ;
cf : write ( " </li> \n " ) ;
end
cf : write ( " </ul> \n " ) ;
end
2015-08-11 16:48:55 -04:00
2013-10-18 06:30:06 -04:00
local ClassName = a_ClassAPI.Name ;
2013-09-13 04:22:04 -04:00
-- Build an array of inherited classes chain:
local InheritanceChain = { } ;
2013-09-13 04:51:01 -04:00
local CurrInheritance = a_ClassAPI.Inherits ;
2013-09-13 04:22:04 -04:00
while ( CurrInheritance ~= nil ) do
table.insert ( InheritanceChain , CurrInheritance ) ;
2013-09-13 04:51:01 -04:00
CurrInheritance = CurrInheritance.Inherits ;
2013-09-13 04:22:04 -04:00
end
2015-08-11 16:48:55 -04:00
2013-11-24 04:03:02 -05:00
cf : write ( [ [ < ! DOCTYPE html >< html >
< head >
2015-08-11 16:48:55 -04:00
< title > Cuberite API - ] ] , a_ClassAPI.Name , [ [ Class </ title >
2016-12-19 11:08:19 -05:00
< link rel = " canonical " href = " https://api.cuberite.org/]], a_ClassAPI.Name, [[.html " >
2013-10-16 02:04:06 -04:00
< link rel = " stylesheet " type = " text/css " href = " main.css " />
2013-10-18 15:02:43 -04:00
< link rel = " stylesheet " type = " text/css " href = " prettify.css " />
< script src = " prettify.js " ></ script >
2013-10-18 14:21:26 -04:00
< script src = " lang-lua.js " ></ script >
2013-11-24 04:03:02 -05:00
</ head >
< body >
2013-10-16 02:04:06 -04:00
< div id = " content " >
2013-11-24 04:03:02 -05:00
< header >
< h1 > ] ] , a_ClassAPI.Name , [ [ </ h1 >
< hr />
</ header >
2013-12-27 09:48:01 -05:00
< table >< tr >< td style = " vertical-align: top; " >
Index : < br />
< a href = ' index.html#articles ' > Articles </ a >< br />
< a href = ' index.html#classes ' > Classes </ a >< br />
< a href = ' index.html#hooks ' > Hooks </ a >< br />
< br />
Quick navigation : < br />
2013-12-27 09:26:41 -05:00
] ] ) ;
cf : write ( a_ClassMenu ) ;
cf : write ( [ [
</ td >< td style = " vertical-align: top; " >< h1 > Contents </ h1 >
2013-11-24 04:03:02 -05:00
< p >< ul >
] ] ) ;
2015-08-11 16:48:55 -04:00
2013-09-13 04:51:01 -04:00
local HasInheritance = ( ( # a_ClassAPI.Descendants > 0 ) or ( a_ClassAPI.Inherits ~= nil ) ) ;
2015-08-11 16:48:55 -04:00
2013-11-22 15:46:06 -05:00
local HasConstants = ( # a_ClassAPI.Constants > 0 ) or ( a_ClassAPI.NumConstantsInGroups > 0 ) ;
2013-10-18 14:49:30 -04:00
local HasFunctions = ( # a_ClassAPI.Functions > 0 ) ;
2013-10-18 15:29:38 -04:00
local HasVariables = ( # a_ClassAPI.Variables > 0 ) ;
2014-03-19 08:57:37 -04:00
for _ , cls in ipairs ( InheritanceChain ) do
2013-11-22 15:46:06 -05:00
HasConstants = HasConstants or ( # cls.Constants > 0 ) or ( cls.NumConstantsInGroupsForDescendants > 0 ) ;
2013-10-22 15:53:35 -04:00
HasFunctions = HasFunctions or ( # cls.Functions > 0 ) ;
HasVariables = HasVariables or ( # cls.Variables > 0 ) ;
2013-10-18 14:49:30 -04:00
end
2015-08-11 16:48:55 -04:00
2013-09-10 16:02:18 -04:00
-- Write the table of contents:
2013-09-13 04:51:01 -04:00
if ( HasInheritance ) then
2013-11-24 04:03:02 -05:00
cf : write ( " <li><a href= \" #inherits \" >Inheritance</a></li> \n " ) ;
2013-09-10 16:02:18 -04:00
end
2013-10-18 14:49:30 -04:00
if ( HasConstants ) then
2013-11-24 04:03:02 -05:00
cf : write ( " <li><a href= \" #constants \" >Constants</a></li> \n " ) ;
2013-10-18 14:49:30 -04:00
end
2013-10-18 15:29:38 -04:00
if ( HasVariables ) then
2013-11-24 04:03:02 -05:00
cf : write ( " <li><a href= \" #variables \" >Member variables</a></li> \n " ) ;
2013-10-18 15:29:38 -04:00
end
2013-10-18 14:49:30 -04:00
if ( HasFunctions ) then
2013-11-24 04:03:02 -05:00
cf : write ( " <li><a href= \" #functions \" >Functions</a></li> \n " ) ;
2013-10-18 14:49:30 -04:00
end
2013-09-14 11:28:22 -04:00
if ( a_ClassAPI.AdditionalInfo ~= nil ) then
for i , additional in ipairs ( a_ClassAPI.AdditionalInfo ) do
2013-11-24 04:03:02 -05:00
cf : write ( " <li><a href= \" #additionalinfo_ " , i , " \" > " , ( additional.Header or " <i>(No header)</i> " ) , " </a></li> \n " ) ;
2013-09-14 11:28:22 -04:00
end
end
2013-11-24 04:03:02 -05:00
cf : write ( " </ul></p> \n " ) ;
2015-08-11 16:48:55 -04:00
2013-09-10 16:02:18 -04:00
-- Write the class description:
2013-11-24 04:03:02 -05:00
cf : write ( " <hr /><a name= \" desc \" ><h1> " , ClassName , " class</h1></a> \n " ) ;
2013-09-11 10:57:57 -04:00
if ( a_ClassAPI.Desc ~= nil ) then
2013-11-24 04:03:02 -05:00
cf : write ( " <p> " ) ;
2017-08-03 09:40:40 -04:00
cf : write ( LinkifyString ( a_ClassAPI.Desc , ClassName , a_API ) ) ;
2013-11-24 04:03:02 -05:00
cf : write ( " </p> \n \n " ) ;
2013-09-10 16:02:18 -04:00
end ;
2015-08-11 16:48:55 -04:00
2013-09-13 04:22:04 -04:00
-- Write the inheritance, if available:
2013-09-13 04:51:01 -04:00
if ( HasInheritance ) then
2013-11-24 04:03:02 -05:00
cf : write ( " <hr /><a name= \" inherits \" ><h1>Inheritance</h1></a> \n " ) ;
2013-09-13 04:51:01 -04:00
if ( # InheritanceChain > 0 ) then
2013-11-24 04:03:02 -05:00
cf : write ( " <p>This class inherits from the following parent classes:<ul> \n " ) ;
2014-03-19 08:57:37 -04:00
for _ , cls in ipairs ( InheritanceChain ) do
2013-11-24 04:03:02 -05:00
cf : write ( " <li><a href= \" " , cls.Name , " .html \" > " , cls.Name , " </a></li> \n " ) ;
2013-09-13 04:51:01 -04:00
end
2013-11-24 04:03:02 -05:00
cf : write ( " </ul></p> \n " ) ;
2013-09-13 04:51:01 -04:00
end
if ( # a_ClassAPI.Descendants > 0 ) then
2013-11-24 04:03:02 -05:00
cf : write ( " <p>This class has the following descendants: \n " ) ;
2013-09-13 04:51:01 -04:00
WriteDescendants ( a_ClassAPI.Descendants ) ;
2013-11-24 04:03:02 -05:00
cf : write ( " </p> \n \n " ) ;
2013-09-10 16:02:18 -04:00
end
end
2015-08-11 16:48:55 -04:00
2013-09-13 04:22:04 -04:00
-- Write the constants:
2013-10-18 14:49:30 -04:00
if ( HasConstants ) then
2013-11-24 04:03:02 -05:00
cf : write ( " <a name= \" constants \" ><hr /><h1>Constants</h1></a> \n " ) ;
2013-11-22 15:46:06 -05:00
WriteConstants ( a_ClassAPI.Constants , a_ClassAPI.ConstantGroups , a_ClassAPI.NumConstantsInGroups , nil ) ;
g_Stats.NumTotalConstants = g_Stats.NumTotalConstants + # a_ClassAPI.Constants + ( a_ClassAPI.NumConstantsInGroups or 0 ) ;
2014-03-19 08:57:37 -04:00
for _ , cls in ipairs ( InheritanceChain ) do
2013-11-22 15:46:06 -05:00
WriteConstants ( cls.Constants , cls.ConstantGroups , cls.NumConstantsInGroupsForDescendants , cls.Name ) ;
2013-10-18 14:49:30 -04:00
end ;
end ;
2015-08-11 16:48:55 -04:00
2013-10-18 15:29:38 -04:00
-- Write the member variables:
if ( HasVariables ) then
2013-11-24 04:03:02 -05:00
cf : write ( " <a name= \" variables \" ><hr /><h1>Member variables</h1></a> \n " ) ;
2013-10-18 15:29:38 -04:00
WriteVariables ( a_ClassAPI.Variables , nil ) ;
2013-10-20 05:12:33 -04:00
g_Stats.NumTotalVariables = g_Stats.NumTotalVariables + # a_ClassAPI.Variables ;
2014-03-19 08:57:37 -04:00
for _ , cls in ipairs ( InheritanceChain ) do
2013-10-18 15:29:38 -04:00
WriteVariables ( cls.Variables , cls.Name ) ;
end ;
end
2015-08-11 16:48:55 -04:00
2014-03-19 08:57:37 -04:00
-- Write the functions, including the inherited ones:
if ( HasFunctions ) then
cf : write ( " <a name= \" functions \" ><hr /><h1>Functions</h1></a> \n " ) ;
WriteFunctions ( a_ClassAPI.Functions , nil ) ;
g_Stats.NumTotalFunctions = g_Stats.NumTotalFunctions + # a_ClassAPI.Functions ;
for _ , cls in ipairs ( InheritanceChain ) do
WriteFunctions ( cls.Functions , cls.Name ) ;
2013-10-09 09:10:25 -04:00
end
end
2015-08-11 16:48:55 -04:00
2014-03-19 08:57:37 -04:00
-- Write the additional infos:
if ( a_ClassAPI.AdditionalInfo ~= nil ) then
for i , additional in ipairs ( a_ClassAPI.AdditionalInfo ) do
cf : write ( " <a name= \" additionalinfo_ " , i , " \" ><h1> " , additional.Header , " </h1></a> \n " ) ;
2017-08-03 09:40:40 -04:00
cf : write ( LinkifyString ( additional.Contents , ClassName , a_API ) ) ;
2014-03-19 08:57:37 -04:00
end
2013-10-16 02:04:06 -04:00
end
2014-03-19 08:57:37 -04:00
2015-06-04 06:23:38 -04:00
cf : write ( [[</td></tr></table></div><script>prettyPrint();</script>]] )
cf : write ( GetHtmlTimestamp ( ) )
cf : write ( [[</body></html>]] )
cf : close ( )
2014-03-19 08:57:37 -04:00
end
2016-05-31 17:57:53 -04:00
--- Writes all classes into HTML output file as links in a sorted bullet list, as well as the individual class HTML files
-- f is the output file
-- a_API is the API detected from current environment enriched with descriptions
-- a_ClassMenu is the HTML code for the menu on the left that is constant for all class pages
2014-03-19 08:57:37 -04:00
local function WriteClasses ( f , a_API , a_ClassMenu )
f : write ( [ [
< a name = " classes " >< h2 > Class index </ h2 ></ a >
2015-08-11 16:48:55 -04:00
< p > The following classes are available in the Cuberite Lua scripting language :
2014-03-19 08:57:37 -04:00
< ul >
] ] ) ;
for _ , cls in ipairs ( a_API ) do
f : write ( " <li><a href= \" " , cls.Name , " .html \" > " , cls.Name , " </a></li> \n " ) ;
2016-09-09 05:19:22 -04:00
WriteHtmlClass ( cls , a_ClassMenu , a_API ) ;
2013-10-12 12:11:14 -04:00
end
2014-03-19 08:57:37 -04:00
f : write ( [ [
</ ul ></ p >
< hr />
] ] ) ;
2013-10-09 09:10:25 -04:00
end
2013-10-18 06:30:06 -04:00
2013-10-18 14:32:36 -04:00
--- Writes a list of undocumented objects into a file
2014-03-19 08:57:37 -04:00
local function ListUndocumentedObjects ( API , UndocumentedHooks )
2015-05-17 16:33:43 -04:00
local f = io.open ( " API/_undocumented.lua " , " w " ) ;
2013-10-18 14:32:36 -04:00
if ( f ~= nil ) then
f : write ( " \n -- This is the list of undocumented API objects, automatically generated by APIDump \n \n " ) ;
2016-05-31 17:57:53 -04:00
f : write ( " return \n { \n \t Classes = \n \t { \n " ) ;
2014-03-19 08:57:37 -04:00
for _ , cls in ipairs ( API ) do
2013-10-18 14:32:36 -04:00
local HasFunctions = ( ( cls.UndocumentedFunctions ~= nil ) and ( # cls.UndocumentedFunctions > 0 ) ) ;
local HasConstants = ( ( cls.UndocumentedConstants ~= nil ) and ( # cls.UndocumentedConstants > 0 ) ) ;
2013-10-19 16:44:28 -04:00
local HasVariables = ( ( cls.UndocumentedVariables ~= nil ) and ( # cls.UndocumentedVariables > 0 ) ) ;
2013-10-20 05:12:33 -04:00
g_Stats.NumUndocumentedFunctions = g_Stats.NumUndocumentedFunctions + # cls.UndocumentedFunctions ;
g_Stats.NumUndocumentedConstants = g_Stats.NumUndocumentedConstants + # cls.UndocumentedConstants ;
g_Stats.NumUndocumentedVariables = g_Stats.NumUndocumentedVariables + # cls.UndocumentedVariables ;
2013-10-19 16:44:28 -04:00
if ( HasFunctions or HasConstants or HasVariables ) then
2013-10-18 14:32:36 -04:00
f : write ( " \t \t " .. cls.Name .. " = \n \t \t { \n " ) ;
if ( ( cls.Desc == nil ) or ( cls.Desc == " " ) ) then
2015-09-27 07:57:07 -04:00
f : write ( " \t \t \t Desc = \" \" , \n " ) ;
2013-10-18 14:32:36 -04:00
end
end
2015-08-11 16:48:55 -04:00
2013-10-18 14:32:36 -04:00
if ( HasFunctions ) then
f : write ( " \t \t \t Functions = \n \t \t \t { \n " ) ;
table.sort ( cls.UndocumentedFunctions ) ;
2014-03-19 08:57:37 -04:00
for _ , fn in ipairs ( cls.UndocumentedFunctions ) do
2013-10-18 14:32:36 -04:00
f : write ( " \t \t \t \t " .. fn .. " = { Params = \" \" , Return = \" \" , Notes = \" \" }, \n " ) ;
2013-10-19 16:44:28 -04:00
end -- for j, fn - cls.UndocumentedFunctions[]
2013-10-18 14:32:36 -04:00
f : write ( " \t \t \t }, \n \n " ) ;
end
2015-08-11 16:48:55 -04:00
2013-10-18 14:32:36 -04:00
if ( HasConstants ) then
f : write ( " \t \t \t Constants = \n \t \t \t { \n " ) ;
table.sort ( cls.UndocumentedConstants ) ;
2014-03-19 08:57:37 -04:00
for _ , cn in ipairs ( cls.UndocumentedConstants ) do
2013-10-18 14:32:36 -04:00
f : write ( " \t \t \t \t " .. cn .. " = { Notes = \" \" }, \n " ) ;
2013-10-19 16:44:28 -04:00
end -- for j, fn - cls.UndocumentedConstants[]
2013-10-18 14:32:36 -04:00
f : write ( " \t \t \t }, \n \n " ) ;
end
2015-08-11 16:48:55 -04:00
2013-10-19 16:44:28 -04:00
if ( HasVariables ) then
f : write ( " \t \t \t Variables = \n \t \t \t { \n " ) ;
table.sort ( cls.UndocumentedVariables ) ;
2014-03-19 08:57:37 -04:00
for _ , vn in ipairs ( cls.UndocumentedVariables ) do
2013-10-19 16:44:28 -04:00
f : write ( " \t \t \t \t " .. vn .. " = { Type = \" \" , Notes = \" \" }, \n " ) ;
end -- for j, fn - cls.UndocumentedVariables[]
f : write ( " \t \t \t }, \n \n " ) ;
end
2015-08-11 16:48:55 -04:00
2013-10-19 16:44:28 -04:00
if ( HasFunctions or HasConstants or HasVariables ) then
2013-10-18 14:32:36 -04:00
f : write ( " \t \t }, \n \n " ) ;
end
end -- for i, cls - API[]
f : write ( " \t }, \n " ) ;
2015-08-11 16:48:55 -04:00
2013-10-18 14:32:36 -04:00
if ( # UndocumentedHooks > 0 ) then
f : write ( " \n \t Hooks = \n \t { \n " ) ;
for i , hook in ipairs ( UndocumentedHooks ) do
if ( i > 1 ) then
f : write ( " \n " ) ;
end
f : write ( " \t \t " .. hook .. " = \n \t \t { \n " ) ;
f : write ( " \t \t \t CalledWhen = \" \" , \n " ) ;
f : write ( " \t \t \t DefaultFnName = \" On \" , -- also used as pagename \n " ) ;
f : write ( " \t \t \t Desc = [[ \n \t \t \t \t \n \t \t \t ]], \n " ) ;
f : write ( " \t \t \t Params = \n \t \t \t { \n " ) ;
f : write ( " \t \t \t \t { Name = \" \" , Type = \" \" , Notes = \" \" }, \n " ) ;
f : write ( " \t \t \t \t { Name = \" \" , Type = \" \" , Notes = \" \" }, \n " ) ;
f : write ( " \t \t \t \t { Name = \" \" , Type = \" \" , Notes = \" \" }, \n " ) ;
f : write ( " \t \t \t \t { Name = \" \" , Type = \" \" , Notes = \" \" }, \n " ) ;
f : write ( " \t \t \t \t { Name = \" \" , Type = \" \" , Notes = \" \" }, \n " ) ;
f : write ( " \t \t \t \t { Name = \" \" , Type = \" \" , Notes = \" \" }, \n " ) ;
f : write ( " \t \t \t \t { Name = \" \" , Type = \" \" , Notes = \" \" }, \n " ) ;
f : write ( " \t \t \t \t { Name = \" \" , Type = \" \" , Notes = \" \" }, \n " ) ;
f : write ( " \t \t \t }, \n " ) ;
f : write ( " \t \t \t Returns = [[ \n \t \t \t \t \n \t \t \t ]], \n " ) ;
f : write ( " \t \t }, -- " .. hook .. " \n " ) ;
end
2013-10-19 16:44:28 -04:00
f : write ( " \t }, \n " ) ;
2013-10-18 14:32:36 -04:00
end
2013-10-19 16:44:28 -04:00
f : write ( " } \n \n \n \n " ) ;
2013-10-18 14:32:36 -04:00
f : close ( ) ;
end
2013-10-20 05:12:33 -04:00
g_Stats.NumUndocumentedHooks = # UndocumentedHooks ;
2013-10-18 14:32:36 -04:00
end
--- Lists the API objects that are documented but not available in the API:
2016-05-31 17:57:53 -04:00
local function ListUnexportedObjects ( a_APIDesc )
2013-10-18 14:32:36 -04:00
f = io.open ( " API/_unexported-documented.txt " , " w " ) ;
if ( f ~= nil ) then
2016-05-31 17:57:53 -04:00
for clsname , cls in pairs ( a_APIDesc.Classes ) do
2013-10-18 14:32:36 -04:00
if not ( cls.IsExported ) then
-- The whole class is not exported
f : write ( " class \t " .. clsname .. " \n " ) ;
else
if ( cls.Functions ~= nil ) then
for fnname , fnapi in pairs ( cls.Functions ) do
if not ( fnapi.IsExported ) then
f : write ( " func \t " .. clsname .. " . " .. fnname .. " \n " ) ;
end
end -- for j, fn - cls.Functions[]
end
if ( cls.Constants ~= nil ) then
for cnname , cnapi in pairs ( cls.Constants ) do
if not ( cnapi.IsExported ) then
f : write ( " const \t " .. clsname .. " . " .. cnname .. " \n " ) ;
end
end -- for j, fn - cls.Functions[]
end
end
2016-05-31 17:57:53 -04:00
end -- for i, cls - a_APIDesc.Classes[]
2013-10-18 14:32:36 -04:00
f : close ( ) ;
end
end
2014-03-19 08:57:37 -04:00
local function ListMissingPages ( )
2013-10-18 06:30:06 -04:00
local MissingPages = { } ;
2013-10-20 05:12:33 -04:00
local NumLinks = 0 ;
2013-10-18 06:30:06 -04:00
for PageName , Referrers in pairs ( g_TrackedPages ) do
2013-10-20 05:12:33 -04:00
NumLinks = NumLinks + 1 ;
2015-09-26 16:54:18 -04:00
if not ( cFile : IsFile ( " API/ " .. PageName .. " .html " ) ) then
2013-10-18 06:30:06 -04:00
table.insert ( MissingPages , { Name = PageName , Refs = Referrers } ) ;
end
end ;
2013-10-20 05:12:33 -04:00
g_Stats.NumTrackedLinks = NumLinks ;
2013-10-18 06:30:06 -04:00
g_TrackedPages = { } ;
2015-08-11 16:48:55 -04:00
2013-10-18 06:30:06 -04:00
if ( # MissingPages == 0 ) then
-- No missing pages, congratulations!
return ;
end
2015-08-11 16:48:55 -04:00
2013-10-18 06:30:06 -04:00
-- Sort the pages by name:
table.sort ( MissingPages ,
function ( Page1 , Page2 )
return ( Page1.Name < Page2.Name ) ;
end
) ;
2015-08-11 16:48:55 -04:00
2013-10-18 06:30:06 -04:00
-- Output the pages:
local f , err = io.open ( " API/_missingPages.txt " , " w " ) ;
if ( f == nil ) then
LOGWARNING ( " Cannot open _missingPages.txt for writing: ' " .. err .. " '. There are " .. # MissingPages .. " pages missing. " ) ;
return ;
end
2014-03-19 08:57:37 -04:00
for _ , pg in ipairs ( MissingPages ) do
2013-10-18 06:30:06 -04:00
f : write ( pg.Name .. " : \n " ) ;
-- Sort and output the referrers:
table.sort ( pg.Refs ) ;
f : write ( " \t " .. table.concat ( pg.Refs , " \n \t " ) ) ;
f : write ( " \n \n " ) ;
end
f : close ( ) ;
2013-10-20 05:12:33 -04:00
g_Stats.NumInvalidLinks = # MissingPages ;
end
--- Writes the documentation statistics (in g_Stats) into the given HTML file
2014-03-19 08:57:37 -04:00
local function WriteStats ( f )
2013-10-20 08:56:09 -04:00
local function ExportMeter ( a_Percent )
local Color ;
2013-11-22 15:46:06 -05:00
if ( a_Percent > 99 ) then
2013-10-20 08:56:09 -04:00
Color = " green " ;
elseif ( a_Percent > 50 ) then
Color = " orange " ;
else
Color = " red " ;
end
2015-08-11 16:48:55 -04:00
2013-10-20 08:56:09 -04:00
local meter = {
" \n " ,
" <div style= \" background-color: black; padding: 1px; width: 100px \" > \n " ,
" <div style= \" background-color: " ,
Color ,
" ; width: " ,
a_Percent ,
" %; height: 16px \" ></div></div> \n </td><td> " ,
string.format ( " %.2f " , a_Percent ) ,
" % " ,
} ;
return table.concat ( meter , " " ) ;
end
2015-08-11 16:48:55 -04:00
2013-10-20 05:12:33 -04:00
f : write ( [ [
< hr />< a name = " docstats " >< h2 > Documentation statistics </ h2 ></ a >
2013-10-20 08:56:09 -04:00
< table >< tr >< th > Object </ th >< th > Total </ th >< th > Documented </ th >< th > Undocumented </ th >< th colspan = " 2 " > Documented %</ th ></ tr >
2013-10-20 05:12:33 -04:00
] ] ) ;
f : write ( " <tr><td>Classes</td><td> " , g_Stats.NumTotalClasses ) ;
f : write ( " </td><td> " , g_Stats.NumTotalClasses - g_Stats.NumUndocumentedClasses ) ;
f : write ( " </td><td> " , g_Stats.NumUndocumentedClasses ) ;
2013-10-20 08:56:09 -04:00
f : write ( " </td><td> " , ExportMeter ( 100 * ( g_Stats.NumTotalClasses - g_Stats.NumUndocumentedClasses ) / g_Stats.NumTotalClasses ) ) ;
f : write ( " </td></tr> \n " ) ;
2013-10-20 05:12:33 -04:00
f : write ( " <tr><td>Functions</td><td> " , g_Stats.NumTotalFunctions ) ;
f : write ( " </td><td> " , g_Stats.NumTotalFunctions - g_Stats.NumUndocumentedFunctions ) ;
f : write ( " </td><td> " , g_Stats.NumUndocumentedFunctions ) ;
2013-10-20 08:56:09 -04:00
f : write ( " </td><td> " , ExportMeter ( 100 * ( g_Stats.NumTotalFunctions - g_Stats.NumUndocumentedFunctions ) / g_Stats.NumTotalFunctions ) ) ;
f : write ( " </td></tr> \n " ) ;
2013-10-20 05:12:33 -04:00
f : write ( " <tr><td>Member variables</td><td> " , g_Stats.NumTotalVariables ) ;
f : write ( " </td><td> " , g_Stats.NumTotalVariables - g_Stats.NumUndocumentedVariables ) ;
f : write ( " </td><td> " , g_Stats.NumUndocumentedVariables ) ;
2013-10-20 08:56:09 -04:00
f : write ( " </td><td> " , ExportMeter ( 100 * ( g_Stats.NumTotalVariables - g_Stats.NumUndocumentedVariables ) / g_Stats.NumTotalVariables ) ) ;
f : write ( " </td></tr> \n " ) ;
2013-10-20 05:12:33 -04:00
f : write ( " <tr><td>Constants</td><td> " , g_Stats.NumTotalConstants ) ;
f : write ( " </td><td> " , g_Stats.NumTotalConstants - g_Stats.NumUndocumentedConstants ) ;
f : write ( " </td><td> " , g_Stats.NumUndocumentedConstants ) ;
2013-10-20 08:56:09 -04:00
f : write ( " </td><td> " , ExportMeter ( 100 * ( g_Stats.NumTotalConstants - g_Stats.NumUndocumentedConstants ) / g_Stats.NumTotalConstants ) ) ;
f : write ( " </td></tr> \n " ) ;
2015-08-11 16:48:55 -04:00
2013-10-20 08:56:09 -04:00
f : write ( " <tr><td>Hooks</td><td> " , g_Stats.NumTotalHooks ) ;
f : write ( " </td><td> " , g_Stats.NumTotalHooks - g_Stats.NumUndocumentedHooks ) ;
f : write ( " </td><td> " , g_Stats.NumUndocumentedHooks ) ;
f : write ( " </td><td> " , ExportMeter ( 100 * ( g_Stats.NumTotalHooks - g_Stats.NumUndocumentedHooks ) / g_Stats.NumTotalHooks ) ) ;
f : write ( " </td></tr> \n " ) ;
2015-08-11 16:48:55 -04:00
2013-10-20 05:12:33 -04:00
f : write ( [ [
</ table >
< p > There are ] ] , g_Stats.NumTrackedLinks , " internal links, " , g_Stats.NumInvalidLinks , " of them are invalid.</p> "
) ;
2013-10-18 06:30:06 -04:00
end
2014-03-14 11:07:22 -04:00
2016-05-31 17:57:53 -04:00
local function DumpAPIHtml ( a_API , a_Descs )
2014-03-19 08:57:37 -04:00
LOG ( " Dumping all available functions and constants to API subfolder... " ) ;
2015-08-11 16:48:55 -04:00
2014-03-19 08:57:37 -04:00
-- Create the output folder
if not ( cFile : IsFolder ( " API " ) ) then
cFile : CreateFolder ( " API " ) ;
end
LOG ( " Copying static files.. " ) ;
cFile : CreateFolder ( " API/Static " ) ;
local localFolder = g_Plugin : GetLocalFolder ( ) ;
for _ , fnam in ipairs ( cFile : GetFolderContents ( localFolder .. " /Static " ) ) do
cFile : Delete ( " API/Static/ " .. fnam ) ;
cFile : Copy ( localFolder .. " /Static/ " .. fnam , " API/Static/ " .. fnam ) ;
end
-- Extract hook constants:
local Hooks = { } ;
local UndocumentedHooks = { } ;
for name , obj in pairs ( cPluginManager ) do
if (
( type ( obj ) == " number " ) and
name : match ( " HOOK_.* " ) and
( name ~= " HOOK_MAX " ) and
( name ~= " HOOK_NUM_HOOKS " )
) then
table.insert ( Hooks , { Name = name } ) ;
end
end
table.sort ( Hooks ,
function ( Hook1 , Hook2 )
return ( Hook1.Name < Hook2.Name ) ;
end
) ;
2016-05-31 17:57:53 -04:00
ReadHooks ( Hooks , a_Descs ) ;
2015-08-11 16:48:55 -04:00
2014-03-19 08:57:37 -04:00
-- Create a "class index" file, write each class as a link to that file,
-- then dump class contents into class-specific file
LOG ( " Writing HTML files... " ) ;
local f , err = io.open ( " API/index.html " , " w " ) ;
if ( f == nil ) then
LOGINFO ( " Cannot output HTML API: " .. err ) ;
return ;
end
-- Create a class navigation menu that will be inserted into each class file for faster navigation (#403)
local ClassMenuTab = { } ;
for _ , cls in ipairs ( a_API ) do
table.insert ( ClassMenuTab , " <a href=' " ) ;
table.insert ( ClassMenuTab , cls.Name ) ;
table.insert ( ClassMenuTab , " .html'> " ) ;
table.insert ( ClassMenuTab , cls.Name ) ;
table.insert ( ClassMenuTab , " </a><br /> " ) ;
end
local ClassMenu = table.concat ( ClassMenuTab , " " ) ;
2015-08-11 16:48:55 -04:00
2014-03-19 08:57:37 -04:00
-- Create a hook navigation menu that will be inserted into each hook file for faster navigation(#403)
local HookNavTab = { } ;
for _ , hook in ipairs ( Hooks ) do
table.insert ( HookNavTab , " <a href=' " ) ;
table.insert ( HookNavTab , hook.DefaultFnName ) ;
table.insert ( HookNavTab , " .html'> " ) ;
table.insert ( HookNavTab , ( hook.Name : gsub ( " ^HOOK_ " , " " ) ) ) ; -- remove the "HOOK_" part of the name
table.insert ( HookNavTab , " </a><br /> " ) ;
end
local HookNav = table.concat ( HookNavTab , " " ) ;
2015-08-11 16:48:55 -04:00
2014-03-19 08:57:37 -04:00
-- Write the HTML file:
f : write ( [ [ < ! DOCTYPE html >
< html >
< head >
2015-08-11 16:48:55 -04:00
< title > Cuberite API - Index </ title >
2016-12-19 11:08:19 -05:00
< link rel = " canonical " href = " https://api.cuberite.org/ " >
2014-03-19 08:57:37 -04:00
< link rel = " stylesheet " type = " text/css " href = " main.css " />
</ head >
< body >
< div id = " content " >
< header >
2015-08-11 16:48:55 -04:00
< h1 > Cuberite API - Index </ h1 >
2014-03-19 08:57:37 -04:00
< hr />
</ header >
< p > The API reference is divided into the following sections : </ p >
< ul >
< li >< a href = " #articles " > Articles </ a ></ li >
< li >< a href = " #classes " > Class index </ a ></ li >
< li >< a href = " #hooks " > Hooks </ a ></ li >
< li >< a href = " #docstats " > Documentation statistics </ a ></ li >
</ ul >
< hr />
] ] ) ;
2015-08-11 16:48:55 -04:00
2016-05-31 17:57:53 -04:00
WriteArticles ( f , a_Descs ) ;
2014-03-19 08:57:37 -04:00
WriteClasses ( f , a_API , ClassMenu ) ;
2017-08-03 09:40:40 -04:00
WriteHooks ( f , Hooks , UndocumentedHooks , HookNav , a_API ) ;
2015-08-11 16:48:55 -04:00
2014-03-19 08:57:37 -04:00
-- Copy the static files to the output folder:
local StaticFiles =
{
" main.css " ,
" prettify.js " ,
" prettify.css " ,
" lang-lua.js " ,
} ;
for _ , fnam in ipairs ( StaticFiles ) do
cFile : Delete ( " API/ " .. fnam ) ;
cFile : Copy ( g_Plugin : GetLocalFolder ( ) .. " / " .. fnam , " API/ " .. fnam ) ;
end
2015-08-11 16:48:55 -04:00
2014-03-19 08:57:37 -04:00
-- List the documentation problems:
LOG ( " Listing leftovers... " ) ;
ListUndocumentedObjects ( a_API , UndocumentedHooks ) ;
2016-05-31 17:57:53 -04:00
ListUnexportedObjects ( a_Descs ) ;
2014-03-19 08:57:37 -04:00
ListMissingPages ( ) ;
WriteStats ( f ) ;
2015-08-11 16:48:55 -04:00
2015-06-04 06:23:38 -04:00
f : write ( [[</ul></div>]] )
f : write ( GetHtmlTimestamp ( ) )
f : write ( [[</body></html>]] )
f : close ( )
2015-08-11 16:48:55 -04:00
2014-03-19 08:57:37 -04:00
LOG ( " API subfolder written " ) ;
end
2014-03-19 17:42:56 -04:00
--- Returns the string with extra tabs and CR/LFs removed
local function CleanUpDescription ( a_Desc )
-- Get rid of indent and newlines, normalize whitespace:
local res = a_Desc : gsub ( " [ \n \t ] " , " " )
res = a_Desc : gsub ( " %s%s+ " , " " )
2015-08-11 16:48:55 -04:00
2014-03-19 17:42:56 -04:00
-- Replace paragraph marks with newlines:
res = res : gsub ( " <p> " , " \n " )
res = res : gsub ( " </p> " , " " )
2015-08-11 16:48:55 -04:00
2014-03-19 17:42:56 -04:00
-- Replace list items with dashes:
res = res : gsub ( " </?ul> " , " " )
res = res : gsub ( " <li> " , " \n - " )
res = res : gsub ( " </li> " , " " )
2015-08-11 16:48:55 -04:00
2014-03-19 17:42:56 -04:00
return res
end
--- Writes a list of methods into the specified file in ZBS format
local function WriteZBSMethods ( f , a_Methods )
for _ , func in ipairs ( a_Methods or { } ) do
f : write ( " \t \t \t [ \" " , func.Name , " \" ] = \n " )
f : write ( " \t \t \t { \n " )
f : write ( " \t \t \t \t type = \" method \" , \n " )
2016-09-12 17:21:15 -04:00
-- No way to indicate multiple signatures to ZBS, so don't output any params at all
2014-03-19 17:42:56 -04:00
if ( ( func.Notes ~= nil ) and ( func.Notes ~= " " ) ) then
f : write ( " \t \t \t \t description = [[ " , CleanUpDescription ( func.Notes or " " ) , " ]], \n " )
end
f : write ( " \t \t \t }, \n " )
end
end
--- Writes a list of constants into the specified file in ZBS format
local function WriteZBSConstants ( f , a_Constants )
for _ , cons in ipairs ( a_Constants or { } ) do
f : write ( " \t \t \t [ \" " , cons.Name , " \" ] = \n " )
f : write ( " \t \t \t { \n " )
f : write ( " \t \t \t \t type = \" value \" , \n " )
if ( ( cons.Desc ~= nil ) and ( cons.Desc ~= " " ) ) then
f : write ( " \t \t \t \t description = [[ " , CleanUpDescription ( cons.Desc or " " ) , " ]], \n " )
end
f : write ( " \t \t \t }, \n " )
end
end
2015-08-11 16:48:55 -04:00
--- Writes one Cuberite class definition into the specified file in ZBS format
2014-03-19 17:42:56 -04:00
local function WriteZBSClass ( f , a_Class )
assert ( type ( a_Class ) == " table " )
2015-08-11 16:48:55 -04:00
2014-03-19 17:42:56 -04:00
-- Write class header:
f : write ( " \t " , a_Class.Name , " = \n \t { \n " )
f : write ( " \t \t type = \" class \" , \n " )
f : write ( " \t \t description = [[ " , CleanUpDescription ( a_Class.Desc or " " ) , " ]], \n " )
f : write ( " \t \t childs = \n " )
f : write ( " \t \t { \n " )
2015-08-11 16:48:55 -04:00
2014-03-19 17:42:56 -04:00
-- Export methods and constants:
WriteZBSMethods ( f , a_Class.Functions )
WriteZBSConstants ( f , a_Class.Constants )
2015-08-11 16:48:55 -04:00
2014-03-19 17:42:56 -04:00
-- Finish the class definition:
f : write ( " \t \t }, \n " )
f : write ( " \t }, \n \n " )
end
--- Dumps the entire API table into a file in the ZBS format
local function DumpAPIZBS ( a_API )
LOG ( " Dumping ZBS API description... " )
2015-09-13 17:49:04 -04:00
local f , err = io.open ( " cuberite_api.lua " , " w " )
2014-03-19 17:42:56 -04:00
if ( f == nil ) then
2015-09-13 17:49:04 -04:00
LOG ( " Cannot open cuberite_api.lua for writing, ZBS API will not be dumped. " .. err )
2014-03-19 17:42:56 -04:00
return
end
2015-08-11 16:48:55 -04:00
2014-03-19 17:42:56 -04:00
-- Write the file header:
2015-08-11 16:48:55 -04:00
f : write ( " -- This is a Cuberite API file automatically generated by the APIDump plugin \n " )
2014-03-19 17:42:56 -04:00
f : write ( " -- Note that any manual changes will be overwritten by the next dump \n \n " )
f : write ( " return { \n " )
2015-08-11 16:48:55 -04:00
2014-03-19 17:42:56 -04:00
-- Export each class except Globals, store those aside:
local Globals
for _ , cls in ipairs ( a_API ) do
if ( cls.Name ~= " Globals " ) then
WriteZBSClass ( f , cls )
else
Globals = cls
end
end
2015-08-11 16:48:55 -04:00
2014-03-19 17:42:56 -04:00
-- Export the globals:
if ( Globals ) then
WriteZBSMethods ( f , Globals.Functions )
WriteZBSConstants ( f , Globals.Constants )
end
2015-08-11 16:48:55 -04:00
2014-03-19 17:42:56 -04:00
-- Finish the file:
f : write ( " } \n " )
f : close ( )
LOG ( " ZBS API dumped... " )
end
2016-11-20 16:11:23 -05:00
local function DumpLuaCheck ( a_API )
LOG ( " Creating file .luacheckrc... " )
local file = io.open ( " .luacheckrc " , " w " )
2016-12-15 08:13:09 -05:00
2016-11-20 16:11:23 -05:00
file : write ( [ [
-- This file is the config file for the tool named Luacheck
2016-12-19 11:08:19 -05:00
-- Documentation: https://luacheck.readthedocs.io/en/stable/index.html
2016-11-20 16:11:23 -05:00
-- Ignore unused function and loop arguments
unused_args = false
-- Allow self defined globals
allow_defined = true
-- Ignore this functions
ignore =
{
" Initialize " , -- Plugin
" OnDisable " , -- Plugin
" RegisterPluginInfoCommands " , -- InfoReg.lua
" RegisterPluginInfoConsoleCommands " , -- InfoReg.lua
" g_PluginInfo " , -- Info.lua
}
-- Ignore files / directories
exclude_files =
{
" tests/ " -- CuberitePluginChecker
}
-- All globals from cuberite (classes, enums, functions)
globals =
{
] ] )
2016-12-15 08:13:09 -05:00
2016-11-20 16:11:23 -05:00
-- Export all global symbols
for _ , cls in ipairs ( a_API ) do
if cls.Name == " Globals " then
-- Global functions
for _ , func in ipairs ( cls.Functions ) do
file : write ( " \t \" " , func.Name , " \" , \n " )
end
2016-12-15 08:13:09 -05:00
2016-11-20 16:11:23 -05:00
-- Global constants
for _ , const in ipairs ( cls.Constants ) do
file : write ( " \t \" " , const.Name , " \" , \n " )
end
2016-12-15 08:13:09 -05:00
2016-11-20 16:11:23 -05:00
-- Global constants from all groups
for _ , group in pairs ( cls.ConstantGroups ) do
for _ , const in pairs ( group.Constants ) do
file : write ( " \t \" " , const.Name , " \" , \n " )
end
end
else
file : write ( " \t \" " , cls.Name , " \" , \n " )
end
end
2016-12-15 08:13:09 -05:00
2017-01-27 07:05:05 -05:00
file : write ( " } \n \n " )
-- Add merge code
file : write ( [ [
-- ## Main ##
-- Load plugins's luacheck
2017-01-27 09:35:36 -05:00
local FilePluginLuacheck = loadfile ( " .luacheckrc_plugin " )
if FilePluginLuacheck ~= nil then
local PluginLuacheck = { }
setfenv ( FilePluginLuacheck , PluginLuacheck )
FilePluginLuacheck ( )
for Option , Value in pairs ( PluginLuacheck ) do
if ( type ( Value ) == " table " ) and not ( getfenv ( 1 ) [ Option ] == nil ) then
-- Merge tables together
for _ , Entry in ipairs ( Value ) do
table.insert ( getfenv ( 1 ) [ Option ] , Entry )
end
else
-- Add a option, table or overwrite a option
getfenv ( 1 ) [ Option ] = Value
2017-01-27 07:05:05 -05:00
end
end
end
] ] )
2016-11-20 16:11:23 -05:00
file : close ( )
2016-12-15 08:13:09 -05:00
2016-11-20 16:11:23 -05:00
LOG ( " Config file .luacheckrc created... " )
end
2015-03-19 16:29:23 -04:00
--- Returns true if a_Descendant is declared to be a (possibly indirect) descendant of a_Base
local function IsDeclaredDescendant ( a_DescendantName , a_BaseName , a_API )
-- Check params:
assert ( type ( a_DescendantName ) == " string " )
assert ( type ( a_BaseName ) == " string " )
assert ( type ( a_API ) == " table " )
if not ( a_API [ a_BaseName ] ) then
return false
end
assert ( type ( a_API [ a_BaseName ] ) == " table " , " Not a class name: " .. a_BaseName )
assert ( type ( a_API [ a_BaseName ] . Descendants ) == " table " )
2015-08-11 16:48:55 -04:00
2015-03-19 16:29:23 -04:00
-- Check direct inheritance:
for _ , desc in ipairs ( a_API [ a_BaseName ] . Descendants ) do
if ( desc.Name == a_DescendantName ) then
return true
end
end -- for desc - a_BaseName's descendants
2015-08-11 16:48:55 -04:00
2015-03-19 16:29:23 -04:00
-- Check indirect inheritance:
for _ , desc in ipairs ( a_API [ a_BaseName ] . Descendants ) do
if ( IsDeclaredDescendant ( a_DescendantName , desc.Name , a_API ) ) then
return true
end
end -- for desc - a_BaseName's descendants
2015-08-11 16:48:55 -04:00
2015-03-19 16:29:23 -04:00
return false
end
--- Checks the specified class' inheritance
-- Reports any problems as new items in the a_Report table
local function CheckClassInheritance ( a_Class , a_API , a_Report )
-- Check params:
assert ( type ( a_Class ) == " table " )
assert ( type ( a_API ) == " table " )
assert ( type ( a_Report ) == " table " )
2015-08-11 16:48:55 -04:00
2015-03-19 16:29:23 -04:00
-- Check that the declared descendants are really descendants:
local registry = debug.getregistry ( )
for _ , desc in ipairs ( a_Class.Descendants or { } ) do
local isParent = false
local parents = registry [ " tolua_super " ] [ _G [ desc.Name ] ]
if not ( parents [ a_Class.Name ] ) then
table.insert ( a_Report , desc.Name .. " is not a descendant of " .. a_Class.Name )
end
end -- for desc - a_Class.Descendants[]
2015-08-11 16:48:55 -04:00
2015-03-19 16:29:23 -04:00
-- Check that all inheritance is listed for the class:
local parents = registry [ " tolua_super " ] [ _G [ a_Class.Name ] ] -- map of "classname" -> true for each class that a_Class inherits
for clsName , isParent in pairs ( parents or { } ) do
if ( ( clsName ~= " " ) and not ( clsName : match ( " const .* " ) ) ) then
if not ( IsDeclaredDescendant ( a_Class.Name , clsName , a_API ) ) then
table.insert ( a_Report , a_Class.Name .. " inherits from " .. clsName .. " but this isn't documented " )
end
end
end
end
--- Checks each class's declared inheritance versus the actual inheritance
local function CheckAPIDescendants ( a_API )
-- Check each class:
local report = { }
for _ , cls in ipairs ( a_API ) do
if ( cls.Name ~= " Globals " ) then
CheckClassInheritance ( cls , a_API , report )
end
end
2015-08-11 16:48:55 -04:00
2015-03-19 16:29:23 -04:00
-- If there's anything to report, output it to a file:
if ( report [ 1 ] ~= nil ) then
LOG ( " There are inheritance errors in the API description: " )
for _ , msg in ipairs ( report ) do
LOG ( " " .. msg )
end
local f , err = io.open ( " API/_inheritance_errors.txt " , " w " )
if ( f == nil ) then
LOG ( " Cannot report inheritance problems to a file: " .. tostring ( err ) )
return
end
f : write ( table.concat ( report , " \n " ) )
f : close ( )
end
end
2015-09-27 07:57:07 -04:00
--- Prepares the API and Globals tables containing the documentation
2016-05-31 17:57:53 -04:00
-- Returns the API and Globals desc table, containing the Classes and Hooks subtables with descriptions,
-- and the apiDesc table containing the descriptions only in their original format.
2015-09-27 07:57:07 -04:00
local function PrepareApi ( )
2014-03-19 08:57:37 -04:00
-- Load the API descriptions from the Classes and Hooks subfolders:
-- This needs to be done each time the command is invoked because the export modifies the tables' contents
2016-05-31 17:57:53 -04:00
local apiDesc = dofile ( g_PluginFolder .. " /APIDesc.lua " )
2016-09-09 05:19:22 -04:00
apiDesc.Classes = apiDesc.Classes or { }
apiDesc.Hooks = apiDesc.Hooks or { }
LoadAPIFiles ( " /Classes/ " , apiDesc.Classes )
LoadAPIFiles ( " /Hooks/ " , apiDesc.Hooks )
2014-03-19 08:57:37 -04:00
-- Reset the stats:
g_TrackedPages = { } ; -- List of tracked pages, to be checked later whether they exist. Each item is an array of referring pagenames.
g_Stats = -- Statistics about the documentation
{
NumTotalClasses = 0 ,
NumUndocumentedClasses = 0 ,
NumTotalFunctions = 0 ,
NumUndocumentedFunctions = 0 ,
NumTotalConstants = 0 ,
NumUndocumentedConstants = 0 ,
NumTotalVariables = 0 ,
NumUndocumentedVariables = 0 ,
NumTotalHooks = 0 ,
NumUndocumentedHooks = 0 ,
NumTrackedLinks = 0 ,
NumInvalidLinks = 0 ,
}
2015-08-11 16:48:55 -04:00
2014-03-19 08:57:37 -04:00
-- Create the API tables:
local API , Globals = CreateAPITables ( ) ;
2015-08-11 16:48:55 -04:00
2014-03-19 08:57:37 -04:00
-- Sort the classes by name:
table.sort ( API ,
function ( c1 , c2 )
return ( string.lower ( c1.Name ) < string.lower ( c2.Name ) ) ;
end
) ;
g_Stats.NumTotalClasses = # API ;
2015-08-11 16:48:55 -04:00
2014-03-19 08:57:37 -04:00
-- Add Globals into the API:
Globals.Name = " Globals " ;
table.insert ( API , Globals ) ;
2016-09-12 17:21:15 -04:00
API.Globals = Globals
2015-08-11 16:48:55 -04:00
2014-03-19 17:42:56 -04:00
-- Read in the descriptions:
LOG ( " Reading descriptions... " ) ;
2016-05-31 17:57:53 -04:00
ReadDescriptions ( API , apiDesc ) ;
2014-03-19 17:42:56 -04:00
2016-05-31 17:57:53 -04:00
return API , Globals , apiDesc
2015-09-27 07:57:07 -04:00
end
local function DumpApi ( )
LOG ( " Dumping the API... " )
-- Match the currently exported API with the available documentation:
2016-05-31 17:57:53 -04:00
local API , Globals , descs = PrepareApi ( )
2015-09-27 07:57:07 -04:00
2015-03-19 16:29:23 -04:00
-- Check that the API lists the inheritance properly, report any problems to a file:
CheckAPIDescendants ( API )
2015-08-11 16:48:55 -04:00
2014-03-19 17:42:56 -04:00
-- Dump all available API objects in HTML format into a subfolder:
2016-05-31 17:57:53 -04:00
DumpAPIHtml ( API , descs ) ;
2015-08-11 16:48:55 -04:00
2014-03-19 17:42:56 -04:00
-- Dump all available API objects in format used by ZeroBraneStudio API descriptions:
DumpAPIZBS ( API )
2016-12-15 08:13:09 -05:00
2016-11-20 16:11:23 -05:00
-- Export the API in a format used by LuaCheck
DumpLuaCheck ( API )
2015-08-11 16:48:55 -04:00
2014-03-19 08:57:37 -04:00
LOG ( " APIDump finished " ) ;
return true
end
2016-10-03 10:24:47 -04:00
--- Checks the currently undocumented symbols against an "official" undocumented symbol list
-- Returns an array-table of strings representing the newly-undocumented symbol names
2016-12-15 08:13:09 -05:00
-- If no newly undocumented symbols are found, returns no value.
-- If an error occurs, returns true and error message.
2016-10-03 10:24:47 -04:00
local function CheckNewUndocumentedSymbols ( )
2015-09-27 07:57:07 -04:00
-- Download the official API stats on undocumented stuff:
-- (We need a blocking downloader, which is impossible with the current cNetwork API)
2016-12-19 11:08:19 -05:00
assert ( os.execute ( " wget -q -O official_undocumented.lua https://api.cuberite.org/_undocumented.lua " ) )
2015-09-27 07:57:07 -04:00
local OfficialStats = cFile : ReadWholeFile ( " official_undocumented.lua " )
if ( OfficialStats == " " ) then
return true , " Cannot load official stats "
end
2015-12-21 13:07:13 -05:00
2015-09-27 07:57:07 -04:00
-- Load the API stats as a Lua file, process into usable dictionary:
2016-05-31 17:57:53 -04:00
-- The _undocumented.lua file format has changed from "g_APIDesc = {}" to "return {}"
-- To support both versions, we execute the function in a sandbox and check both its return value and the sandbox globals
2015-09-27 07:57:07 -04:00
local Loaded , Msg = loadstring ( OfficialStats )
if not ( Loaded ) then
return true , " Cannot load official stats: " .. ( Msg or " <unknown error> " )
end
2016-05-31 17:57:53 -04:00
local sandbox = { }
setfenv ( Loaded , sandbox )
local IsSuccess , OfficialUndocumented = pcall ( Loaded )
2015-09-27 07:57:07 -04:00
if not ( IsSuccess ) then
2016-05-31 17:57:53 -04:00
return true , " Cannot parse official stats: " .. tostring ( OfficialUndocumented or " <unknown error> " )
2015-09-27 07:57:07 -04:00
end
local Parsed = { }
2016-05-31 17:57:53 -04:00
for clsK , clsV in pairs ( ( sandbox.g_APIDesc or OfficialUndocumented ) . Classes ) do -- Check return value OR sandbox global, whichever is present
2015-09-27 07:57:07 -04:00
local cls =
{
Desc = not ( clsV.Desc ) , -- set to true if the Desc was not documented in the official docs
Functions = { } ,
Constants = { }
}
for funK , _ in pairs ( clsV.Functions or { } ) do
cls.Functions [ funK ] = true
end
for conK , _ in pairs ( clsV.Constants or { } ) do
cls.Constants [ conK ] = true
end
Parsed [ clsK ] = cls
end
2015-12-21 13:07:13 -05:00
2015-09-27 07:57:07 -04:00
-- Get the current API's undocumented stats:
local API = PrepareApi ( )
2015-12-21 13:07:13 -05:00
2015-09-27 07:57:07 -04:00
-- Compare the two sets of undocumented stats, list anything extra in current:
local res = { }
local ins = table.insert
for _ , cls in ipairs ( API ) do
local ParsedOfficial = Parsed [ cls.Name ] or { }
if ( not ( cls.Desc ) and ParsedOfficial.Desc ) then
ins ( res , cls.Name .. " .Desc " )
end
local ParsedOfficialFns = ParsedOfficial.Functions or { }
for _ , funK in ipairs ( cls.UndocumentedFunctions or { } ) do
if not ( ParsedOfficialFns [ funK ] ) then
ins ( res , cls.Name .. " . " .. funK .. " (function) " )
end
end
local ParsedOfficialCons = ParsedOfficial.Constants or { }
for _ , conK in ipairs ( cls.UndocumentedConstants or { } ) do
if not ( ParsedOfficialCons [ conK ] ) then
ins ( res , cls.Name .. " . " .. conK .. " (constant) " )
end
end
end
table.sort ( res )
2015-12-21 13:07:13 -05:00
2015-09-27 07:57:07 -04:00
-- Bail out if no items found:
if not ( res [ 1 ] ) then
2016-10-03 10:24:47 -04:00
return
2015-09-27 07:57:07 -04:00
end
-- Save any found items to a file:
local f = io.open ( " NewlyUndocumented.lua " , " w " )
f : write ( table.concat ( res , " \n " ) )
2015-10-11 15:25:40 -04:00
f : write ( " \n " )
2015-09-27 07:57:07 -04:00
f : close ( )
2015-12-21 13:07:13 -05:00
2016-10-03 10:24:47 -04:00
return res
end
--- Checks the API description for unknown types listed in Params or Returns
-- Returns an array-table of { Location = "cClass:function(), param #1", Type = "UnknownType" }
-- Returns nil if no unknown types are found
local function CheckBadTypes ( )
-- Load the API and preprocess known types:
local api = PrepareApi ( )
local knownTypes =
{
string = true ,
number = true ,
boolean = true ,
any = true ,
self = true ,
table = true ,
[ " function " ] = true ,
[ " ... " ] = true ,
[ " SQLite DB object " ] = true ,
[ " <unknown> " ] = true , -- Allow "<unknown>" types, for now, until the API is properly documented
}
for _ , clsDesc in ipairs ( api ) do
knownTypes [ clsDesc.Name ] = true -- The class is a known type
for grpName , _ in pairs ( clsDesc.ConstantGroups or { } ) do -- All class' enums are known types (with namespacing)
knownTypes [ clsDesc.Name .. " # " .. grpName ] = true
end
if ( clsDesc.Name == " Globals " ) then
for grpName , _ in pairs ( clsDesc.ConstantGroups or { } ) do -- All Globals' enums are known types without namespacing, too
knownTypes [ grpName ] = true
end
end
end -- for cls - classes
-- Check types:
local res = { }
for _ , clsDesc in ipairs ( api ) do
for _ , fnDesc in ipairs ( clsDesc.Functions or { } ) do
local fnName = fnDesc.Name
local fn = fnDesc [ 1 ] and fnDesc or { fnDesc } -- Unify the format, fn is an array of function signatures
for idxS , signature in ipairs ( fn ) do
for idxP , param in ipairs ( signature.Params or { } ) do
if not ( knownTypes [ param.Type ] ) then
table.insert ( res , {
Location = string.format ( " %s:%s(), signature #%d, param #%d " , clsDesc.Name , fnName , idxS , idxP ) ,
Type = param.Type ,
} )
end
end -- for param
if ( type ( signature.Returns ) == " table " ) then
for idxR , ret in ipairs ( signature.Returns ) do
if not ( knownTypes [ ret.Type ] ) then
table.insert ( res , {
Location = string.format ( " %s:%s(), signature #%d, return #%d " , clsDesc.Name , fnName , idxS , idxR ) ,
Type = ret.Type ,
} )
end
end -- for ret
elseif not ( signature.Returns ) then
else
table.insert ( res , {
Location = string.format ( " %s:%s(), signature #%d, return string " , clsDesc.Name , fnName , idxS ) ,
Type = tostring ( signature.Returns ) ,
} )
end
end -- for signature
end -- for fn - functions
end -- for cls - classes
-- If no problems found, bail out:
if not ( res [ 1 ] ) then
return
end
-- Write the problems into a file:
local f = io.open ( " UnknownTypes.lua " , " w " )
f : write ( " return \n { \n " )
for _ , item in ipairs ( res ) do
f : write ( " \t { " , string.format ( " { Location = %q, Type = %q " , item.Location , item.Type ) , " }, \n " )
end
f : write ( " } \n " )
f : close ( )
return res
end
local function HandleWebAdminDump ( a_Request )
if ( a_Request.PostParams [ " Dump " ] ~= nil ) then
DumpApi ( )
end
return
[ [
< p > Pressing the button will generate the API dump on the server . Note that this can take some time . </ p >
< form method = " POST " >< input type = " submit " name = " Dump " value = " Dump the API " /></ form >
] ]
end
local function HandleCmdApi ( a_Split )
DumpApi ( )
return true
end
local function HandleCmdApiShow ( a_Split , a_EntireCmd )
os.execute ( " API " .. cFile : GetPathSeparator ( ) .. " index.html " )
return true , " Launching the browser to show the API docs... "
end
local function HandleCmdApiCheck ( a_Split , a_EntireCmd )
-- Check the Params and Returns types:
LOG ( " Checking API for bad types... " )
local badTypes = CheckBadTypes ( )
if ( badTypes ) then
-- Serialize into descriptions:
local descs = { }
for idx , t in ipairs ( badTypes ) do
descs [ idx ] = string.format ( " Location %q, type %q " , t.Location , t.Type )
end
return true , " Found bad types: \n " .. table.concat ( descs , " \n " )
end
-- Check for new symbols that are not documented:
LOG ( " Checking API for newly undocumented symbols... " )
2016-12-15 08:13:09 -05:00
local newUndocumented , msg = CheckNewUndocumentedSymbols ( )
2016-10-03 10:24:47 -04:00
if ( newUndocumented ) then
2016-12-15 08:13:09 -05:00
if ( newUndocumented == true ) then
2016-12-15 14:22:19 -05:00
LOGERROR ( " Cannot check for new undocumented symbols: " .. ( msg or " <no message> " ) )
return true
2016-12-15 08:13:09 -05:00
else
2016-12-15 14:22:19 -05:00
LOGERROR ( " Found new undocumented symbols: \n " .. table.concat ( newUndocumented , " \n " ) )
return true
2016-12-15 08:13:09 -05:00
end
2016-10-03 10:24:47 -04:00
end
2016-10-25 12:49:42 -04:00
-- The check completed successfully, remove the "test failed" flag from the filesystem:
cFile : DeleteFile ( " apiCheckFailed.flag " )
2016-10-03 10:24:47 -04:00
return true , " API check completed successfully "
2015-09-27 07:57:07 -04:00
end
2014-03-19 08:57:37 -04:00
function Initialize ( Plugin )
g_Plugin = Plugin ;
g_PluginFolder = Plugin : GetLocalFolder ( ) ;
2015-08-11 16:48:55 -04:00
2021-06-22 18:09:06 -04:00
LOG ( " Initialising v. " .. Plugin : GetVersion ( ) )
2015-08-11 16:48:55 -04:00
2014-03-19 17:51:02 -04:00
-- Bind a console command to dump the API:
2015-09-27 07:57:07 -04:00
cPluginManager : BindConsoleCommand ( " api " , HandleCmdApi , " Dumps the Lua API docs into the API/ subfolder " )
cPluginManager : BindConsoleCommand ( " apicheck " , HandleCmdApiCheck , " Checks the Lua API documentation stats against the official stats " )
cPluginManager : BindConsoleCommand ( " apishow " , HandleCmdApiShow , " Runs the default browser to show the API docs " )
2014-03-19 17:51:02 -04:00
-- Add a WebAdmin tab that has a Dump button
2014-03-19 08:57:37 -04:00
g_Plugin : AddWebTab ( " APIDump " , HandleWebAdminDump )
2015-08-11 16:48:55 -04:00
2014-03-19 08:57:37 -04:00
return true
end
2016-11-20 16:11:23 -05:00