summaryrefslogblamecommitdiff
path: root/gendoc.lua
blob: 8feb7cfaaca4f39de6df6f3b5e2eacbe3fbdb888 (plain) (tree)
1
2
3
4
5
6
7
8
9
                                
 
                                        
 

                                    

                                              
                                  
 



                                       
 
         
 
                               
 














                                                                          

           
                                  
 
                                            


                            
 

                            
                       
                                       
                                                 
                                         
                       
                                                                                        


                                                             


                                                     
                                                                                                 
                    
 


                                           
 



                                            









                                                     
 

                                                     










                                     

                                                                         





                                                                  




                                       









                                         

                                                                    


                                                             




                                                   





                                               




                                                   


                                            

                                                                               















                                                      
                            
                        
                           






























                                                                            
                                                    






















                                                                         

                                                                                






                                     



                                                             




                                                                
                                                                                   
            

                                                                                                 


















                                                            
                                                    












                                         

                                                                          











                                   



                                                             


                         


                

                                                 

              
                 





                                    
            
                             
           
 



                             
 
                        

       



                                                      
 

                            
 
             

   








                                                      


       




                                               
                                           


                                   
 
                                     

                                                                        





                                 
                                                                    
                    


                             
           
 
                    
                                                    
                    
 
                         





                                                           

           

   
 








                                                   
 


                             

                                                                          







                                
 
                                     

                                                                        
                                 
 
                          
                                                                    
                    
                             
           


                         


       


                                           

                                                      
                                           

                               
                                                                          
                 
 

                                        
 




                                                   

                                   
 
                                                                
                    
 



                                                   
       
 
                                                               
 



                                       


                                                         
       
 



                                                          


                                                     





                                
                                  
                                
                           
           


       


                                            
                                   


                                 
                                                                
           
                                                                           




                                          











                                                
                           

                           
                                      




                                      
                                      





                                      

                                                        



                                     
                                                                   
                   

                                                                                  




















                                      







                                                                                  
                                         
                                                                 
                                                                                
             


                                 




                                                       
                     
 
                                                                             

                               
                                                       



                                        
 
                                            
 
                             
                                                  

                
                               
                                                            
                
 

                                               
                                  


                              
 
                            
                



              
                                   




                                                       
                     
 
                                   
 

                                    
 


                                                                                
 
                           
                                   
                                              
                                                               
           


                                                    


                                            

       







                                                                                                   

                          
                                  
            
                                       
           
                                                            


                              




                                                       



                          
                                  
            

                                       
                                                            



                                                       
           

       
                






                                            
                                           

                             
                                                    




                               
                                                                                           

                 
                                                                                           









                                                
                                     
                                                                               






















                                                                                                 
                                                                                

           
                                                                               

                             
                                                                               

                              
                                                                             

                            
                                                                                

                               

   

                                  
                                



                                                                                  


                                  
                                





                                                                            


                                
                                





                                                                            




                                   
                               





                                                                       
       

   


























                                                                       






                                                                              
                                                        
                                                                             


                                                               
                                                                         






                                                                                





                                          


                         


                                                   


                                                         
                                                


                                      
           



                                                              
                          
                            
            
                                       







                                         


                                          
                                        
                                        
                   
               
                  
           






                                               
                                                           


                                    
           

























                                                         
           


       
           
local getopt = require("getopt")

local serializer = require("serializer")

local template = require("template")

local eolian = require("eolian")
local eoutils = require("docgen.eolian_utils")
local docm = require("docgen.doc")

local stats = require("docgen.stats")
local dutil = require("docgen.util")
local writer = require("docgen.writer")
local keyref = require("docgen.keyref")

local eos

local printgen = function() end

local render_template = function(tpath, ns, title, ctx)
    local nctx = {
        doc = docm, eos = eos, eolian = eolian,
        eoutils = eoutils, title = docm.title_str_get(title), page_ns = ns
    }
    if ctx then
        nctx = setmetatable(nctx, { __index = ctx })
    end
    local f = writer.Writer(ns)
    tpath = "templates/" .. tpath .. ".txt"
    print("rendering template: " .. tpath .. " (" .. title .. ")")
    f:write_raw(template.compile(tpath)(nctx, true))
    f:finish()
end

-- builders

local build_method, build_property

local build_reftable = function(f, title, t)
    if not t or #t == 0 then
        return
    end

    local nt = {}
    for i, v in ipairs(t) do
        nt[#nt + 1] = {
            writer.Buffer():write_link(
                eoutils.obj_nspaces_get(v, true),
                v:name_get(), v:is_beta()
            ):finish(),
            docm.brief_str_get(eos, v:documentation_get()) or "No description supplied."
        }
    end
    table.sort(nt, function(v1, v2) return v1[1] < v2[1] end)
    f:write_table({ title, "Brief description" }, nt)
end

local build_ref_group = function(f, ns, classes, ifaces, mixins, aliases, structs, enums, consts)
    f:write_h(ns, 2)

    build_reftable(f, "Classes", classes)
    build_reftable(f, "Interfaces", ifaces)
    build_reftable(f, "Mixins", mixins)

    build_reftable(f, "Aliases", aliases)
    build_reftable(f, "Structures", structs)
    build_reftable(f, "Enums", enums)
    build_reftable(f, "Constants", consts)

    f:write_nl()
end

local build_ref = function()
    local f = writer.Writer("start", "EFL Reference")
    printgen("Generating reference...")

    f:write_editable({ "reference" }, "general")
    f:write_nl()
 
    for _, grp in ipairs(docm.ref_groups_get(eos)) do
        build_ref_group(f, unpack(grp))
    end

    f:finish()
end

local build_inherits
build_inherits = function(cl, t, lvl)
    t = t or {}
    lvl = lvl or 0
    local lbuf = writer.Buffer()
    if lvl > 0 then
        lbuf:write_link(eoutils.obj_nspaces_get(cl, true), cl:name_get(),
            cl:is_beta())
        lbuf:write_raw(" ")
        lbuf:write_i("(" .. eoutils.class_type_str_get(cl) .. ")")
 
        t[#t + 1] = { lvl - 1, lbuf:finish() }
    end

    local pcl = cl:parent_get()
    if pcl then
        build_inherits(pcl, t, lvl + 1)
    end
    for acl in cl:extensions_get() do
        build_inherits(acl, t, lvl + 1)
    end
    return t
end

local build_inherit_summary
build_inherit_summary = function(cl, buf)
    buf = buf or writer.Buffer()
    buf:write_raw(" => ")

    buf:write_link(eoutils.obj_nspaces_get(cl, true), cl:name_get(),
        cl:is_beta())
    buf:write_raw(" ")
    buf:write_i("(" .. eoutils.class_type_str_get(cl) .. ")")

    local inherits = cl:extensions_get():to_array()
    local pcl = cl:parent_get()
    if pcl then
        table.insert(inherits, 1, pcl)
    end
    if #inherits ~= 0 then
        build_inherit_summary(inherits[1], buf)
    end
    return buf
end

local propt_to_type = {
    [eolian.function_type.PROPERTY] = "(get, set)",
    [eolian.function_type.PROP_GET] = "(get)",
    [eolian.function_type.PROP_SET] = "(set)",
}

local write_function = function(f, func, cl)
    local llbuf = writer.Buffer()
    llbuf:write_link(eoutils.func_nspaces_get(func, cl, true), func:name_get(),
        cl:is_beta() or func:is_beta())
    f:write_b(llbuf:finish())

    local pt = propt_to_type[func:type_get()]
    if pt then
        f:write_raw(" ")
        local llbuf = writer.Buffer()
        llbuf:write_b(pt)
        f:write_i(llbuf:finish())
    end
end

local write_scope = function(f, func)
    local ftt = {
        [eolian.object_scope.PROTECTED] = "protected",
        [eolian.object_scope.PRIVATE] = "private"
    }
    if func:is_static() then
        f:write_raw(" ")
        f:write_m("static")
    end
    if func:type_get() == eolian.function_type.PROPERTY then
        local ft1, ft2 = ftt[func:scope_get(eolian.function_type.PROP_GET)],
                         ftt[func:scope_get(eolian.function_type.PROP_SET)]
        if ft1 and ft1 == ft2 then
            f:write_raw(" ")
            f:write_m(ft1)
        elseif ft1 or ft2 then
            local s = ""
            if ft1 then
                s = s .. ft1 .. " get" .. (ft2 and ", " or "")
            end
            if ft2 then
                s = s .. ft2 .. " set"
            end
            f:write_raw(" ")
            f:write_m(s)
        end
    else
        local ft = ftt[func:scope_get(func:type_get())]
        if ft then
            f:write_raw(" ")
            f:write_m(ft)
        end
    end
end

local write_functable = function(f, tcl, tbl)
    if #tbl == 0 then
        return
    end
    local nt = eoutils.sorted_funclist_get(tcl, tbl)

    local wrote = false
    for i, wt in ipairs(nt) do
        local cl = wt[0]
        local func = wt[1]
        local impl = wt[2]

        local ocl = impl:class_get()
        local func = impl:function_get()
        local over = eoutils.impl_is_overridden(impl, cl)

        -- function
        write_function(f, func, cl)
        -- scope
        write_scope(f, func)

        -- overrides
        if over then
            -- TODO: possibly also mention which part of a property was
            -- overridden and where, get/set override point might differ!
            -- but we get latest doc every time so it's ok for now
            local llbuf = writer.Buffer()
            llbuf:write_raw(" [Overridden from ")
            llbuf:write_link(eoutils.obj_nspaces_get(ocl, true), ocl:name_get(),
                ocl:is_beta())
            llbuf:write_raw("]")
            f:write_i(llbuf:finish())
        end

        -- description
        f:write_br(true)
        f:write_raw("> ")
        local desc = docm.impl_description_get(eos, impl, cl)
        if desc then
            f:write_raw(desc)
        end
 
        -- code snippets
        f:write_nl()
        local codes = {}
        if func:type_get() ~= eolian.function_type.PROPERTY then
            codes[#codes + 1] = eoutils.function_serialize_c(func, func:type_get())
        else
            codes[#codes + 1] = eoutils.function_serialize_c(func, eolian.function_type.PROP_GET)
            codes[#codes + 1] = eoutils.function_serialize_c(func, eolian.function_type.PROP_SET)
        end
        f:write_code(table.concat(codes, "\n"), "c")
        f:write_br(true)

        if cl == tcl then
            if impl:is_prop_get() or impl:is_prop_set() then
                build_property(impl, cl)
            else
                build_method(impl, cl)
            end
        end
    end
    f:write_nl()
end

local write_inherit_functable = function(f, tcl, tbl)
    if #tbl == 0 then
        return
    end
    local nt = eoutils.sorted_funclist_get(tcl, tbl)

    local prevcl = tcl
    for i, wt in ipairs(nt) do
        local cl = wt[0]
        local func = wt[1]
        local impl = wt[2]

        local func = impl:function_get()

        -- class grouping for inheritance
        if cl ~= prevcl then
            prevcl = cl
            f:write_raw("^ ")
            f:write_link(eoutils.obj_nspaces_get(cl, true), cl:name_get(),
                cl:is_beta())
            f:write_raw(" ^^^")
            f:write_nl()
        end

        -- scope
        f:write_raw("| ")
        write_scope(f, func)
        f:write_raw(" | ")
        -- function
        write_function(f, func, cl)
        f:write_raw(" | ")
        -- description
        local desc = docm.impl_description_get(eos, impl, cl)
        if desc then
            f:write_raw(desc)
        end
        f:write_raw(" |")
        f:write_nl()
    end
    f:write_nl()
end

local build_evtable = function(f, tcl, tbl, newm)
    if #tbl == 0 then
        return
    end
    local nt = {}
    for i, evt in ipairs(tbl) do
        local lbuf = writer.Buffer()
        local evn
        local cl, ev
        if not newm then
            cl, ev = evt[1], evt[2]
        else
            cl, ev = tcl, evt
        end

        local wt = {}
        wt[0] = cl
        wt[1] = ev
        wt[2] = ev:name_get()

	nt[#nt + 1] = wt
    end

    table.sort(nt, function(v1, v2)
        if v1[0] ~= v2[0] then
            return v1[0]:name_get() < v2[0]:name_get()
        end

        return v1[2] < v2[2]
    end)

    return nt
end

local write_event_scope = function(f, ev)
    local ett = {
        [eolian.object_scope.PROTECTED] = "protected",
        [eolian.object_scope.PRIVATE] = "private"
    }
    local ets = ett[ev:scope_get()]
    if ets then
        f:write_raw(" ")
        f:write_m(ets)
    end
end

local write_evtable = function(f, tcl, tbl)
    if #tbl == 0 then
        return
    end
    local nt = build_evtable(f, tcl, tbl, true)
    local ev_m = require("templates.event")
    for i, wt in ipairs(nt) do
        local evn
        local cl, ev = wt[0], wt[1]

        local llbuf = writer.Buffer()
        llbuf:write_link(eoutils.event_nspaces_get(ev, cl, true), wt[2],
            cl:is_beta() or ev:is_beta())
        f:write_b(llbuf:finish())

        -- scope
        write_event_scope(f, ev)
 
        -- description
        local bdoc = docm.brief_str_get(eos, ev:documentation_get())
        if bdoc then
            f:write_br(true)
            f:write_raw("> ")
            f:write_raw(bdoc)
        end

        f:write_nl()
        f:write_code(ev_m.c_signature_get(ev), "c");
        f:write_br()

        if cl == tcl then
            render_template(
                "event", eoutils.event_nspaces_get(ev, cl),
                cl:name_get() .. ": " .. ev:name_get(), {
                    ev_obj = ev, ev_m = ev_m
                }
            )
        end
    end
end


local write_inherit_evtable = function(f, tcl, tbl)
    if #tbl == 0 then
        return
    end
    local nt = build_evtable(f, tcl, tbl, false)
    local prevcl
    for i, wt in ipairs(nt) do
        local evn
        local cl, ev = wt[0], wt[1]

        if cl ~= prevcl then
            prevcl = cl
            f:write_raw("^ ")
            f:write_link(eoutils.obj_nspaces_get(cl, true), cl:name_get(),
                cl:is_beta())
            f:write_raw(" ^^^")
            f:write_nl()
        end
 
        f:write_raw("| ")
        -- scope
        write_event_scope(f, ev)
        f:write_raw(" | ")

        local llbuf = writer.Buffer()
        llbuf:write_link(eoutils.event_nspaces_get(ev, cl, true), wt[2],
            cl:is_beta() or ev:is_beta())
        f:write_b(llbuf:finish())

        f:write_raw(" | ")
        local bdoc = docm.brief_str_get(eos, ev:documentation_get())
        if bdoc then
            f:write_raw(bdoc)
        end

        f:write_raw(" |")
        f:write_nl()
    end
end

local build_class = function(cl)
    local cln = eoutils.obj_nspaces_get(cl)
    local fulln = cl:name_get()
    local f = writer.Writer(cln, fulln, fulln ..
        " (" .. eoutils.class_type_str_get(cl) .. ")")
    printgen("Generating class: " .. fulln)
 
    f:write_h("Description", 2)
    f:write_raw(docm.full_str_get(eos, cl:documentation_get(), nil, true))
    f:write_nl(2)

    f:write_editable(cln, "description")
    f:write_nl()

    local inherits = cl:extensions_get():to_array()
    local pcl = cl:parent_get()
    if pcl then
        table.insert(inherits, 1, pcl)
    end
    if #inherits ~= 0 then
        f:write_h("Inheritance", 2)

        f:write_raw(build_inherit_summary(inherits[1]):finish())
        f:write_nl()

        f:write_folded("Full hierarchy", function()
            f:write_list(build_inherits(cl))
        end)
        f:write_nl()
    end

    local meths, omeths, ievs = eoutils.callables_get(cl, true)

    f:write_h("Members", 2)
    write_functable(f, cl, meths, true)
    if #omeths ~= 0 then
        f:write_h("Inherited", 3)
        f:write_folded("Inherited methods", function()
            write_inherit_functable(f, cl, omeths, false)
        end)
    end

    f:write_h("Events", 2)
    write_evtable(f, cl, cl:events_get():to_array(), true)
    if #ievs ~= 0 then
        f:write_h("Inherited", 3)
        f:write_folded("Inherited events", function()
            write_inherit_evtable(f, cl, ievs, false)
        end)
    end

    f:finish()
end

local build_classes = function()
    for cl in eos:classes_get() do
        if not cl:is_beta() then
            build_class(cl)
        end
    end
end

local build_parlist = function(f, pl, nodir)
    local params = {}
    for i, p in ipairs(pl) do
        local buf = writer.Buffer()
        buf:write_b(p:name_get())
        if not nodir then
            buf:write_raw(" ")
            buf:write_i("(", eoutils.param_get_dir_name(p), ")")
        end
        buf:write_raw(" - ", docm.full_str_get(eos, p:documentation_get()))
        params[#params + 1] = buf:finish()
    end
    f:write_list(params)
end

local build_vallist = function(f, pg, ps, title)
    if #pg == #ps then
        local same = true
        for i = 1, #pg do
            if pg[i] ~= ps[i] then
                same = false
                break
            end
        end
        if same then ps = {} end
    end
    if #pg > 0 or #ps > 0 then
        f:write_h(title, 2)
        if #pg > 0 then
            if #ps > 0 then
                f:write_h("Getter", 3)
            end
            build_parlist(f, pg, true)
        end
        if #ps > 0 then
            if #pg > 0 then
                f:write_h("Setter", 3)
            end
            build_parlist(f, ps, true)
        end
    end
end

local write_inherited_from = function(f, impl, cl, prop)
    if not eoutils.impl_is_overridden(impl, cl) then
        return
    end
    local buf = writer.Buffer()
    buf:write_raw("Overridden from ")
    local pimpl, pcl = eoutils.parent_impl_get(impl:name_get(), cl)
    buf:write_link(
        eoutils.func_nspaces_get(impl:function_get(), pcl, true), impl:name_get(),
        pcl:is_beta() or impl:function_get():is_beta()
    )
    if prop then
        buf:write_raw(" ")
        local lbuf = writer.Buffer()
        lbuf:write_raw("(")
        if impl:is_prop_get() then
            lbuf:write_raw("get")
            if impl:is_prop_set() then
                lbuf:write_raw(", ")
            end
        end
        if impl:is_prop_set() then
            lbuf:write_raw("set")
        end
        lbuf:write_raw(")")
        buf:write_b(lbuf:finish())
    end
    buf:write_raw(".")
    f:write_i(buf:finish())
end

local write_back = function(f, cl)
    local buf = writer.Buffer()
    buf:write_raw("Belongs to ")
    buf:write_link(eoutils.obj_nspaces_get(cl, true), cl:name_get(), cl:is_beta())
    buf:write_raw(".")
    f:write_i(buf:finish())
end

local write_ilist = function(f, impl, cl)
    f:write_raw(template.compile("templates/include/impls.txt")({
        doc = docm, eoutils = eoutils, fn_obj = impl:function_get(), cl_obj = cl
    }, true))
end

build_method = function(impl, cl)
    local fn = impl:function_get()
    local mns = eoutils.func_nspaces_get(fn, cl)
    local methn = cl:name_get() .. "." .. fn:name_get()
    local f = writer.Writer(mns, methn)
    printgen("Generating method: " .. methn)
    write_back(f, cl)

    local doc = eoutils.parent_doc_get(impl, cl, eolian.function_type.METHOD)

    f:write_h("Description", 2)
    f:write_raw(docm.full_str_get(eos, doc, nil, true))
    f:write_nl()

    f:write_editable(mns, "description")
    f:write_nl()

    write_inherited_from(f, impl, cl, false)

    f:write_h("Signature", 2)
    f:write_code(eoutils.method_serialize(fn, cl))
    f:write_nl()

    f:write_h("C signature", 2)
    f:write_code(eoutils.function_serialize_c(fn, nil), "c")
    f:write_nl()

    local pars = fn:parameters_get():to_array()
    if #pars > 0 then
        f:write_h("Parameters", 2)
        build_parlist(f, pars)
        f:write_nl()
    end

    write_ilist(f, impl, cl)
    f:write_nl()

    f:finish()
end

build_property = function(impl, cl)
    local fn = impl:function_get()
    local pns = eoutils.func_nspaces_get(fn, cl)
    local propn = cl:name_get() .. "." .. fn:name_get()
    local f = writer.Writer(pns, propn)
    printgen("Generating property: " .. propn)
    write_back(f, cl)

    local pimp = fn:implement_get()

    local isget = pimp:is_prop_get()
    local isset = pimp:is_prop_set()

    local doc = eoutils.parent_doc_get(impl, cl, eolian.function_type.PROPERTY)
    local gdoc = eoutils.parent_doc_get(impl, cl, eolian.function_type.PROP_GET)
    local sdoc = eoutils.parent_doc_get(impl, cl, eolian.function_type.PROP_SET)

    if isget and isset then
        f:write_h("Description", 2)
        if doc or (not gdoc and not sdoc) then
            f:write_raw(docm.full_str_get(eos, doc, nil, true))
        end
        if (isget and gdoc) or (isset and sdoc) then
            f:write_nl(2)
        end
        f:write_nl()
        f:write_editable(pns, "description")
        f:write_nl()
    end

    local pgkeys = isget and fn:property_keys_get(eolian.function_type.PROP_GET):to_array() or {}
    local pskeys = isset and fn:property_keys_get(eolian.function_type.PROP_SET):to_array() or {}
    build_vallist(f, pgkeys, pskeys, "Keys")

    local pgvals = isget and fn:property_values_get(eolian.function_type.PROP_GET):to_array() or {}
    local psvals = isset and fn:property_values_get(eolian.function_type.PROP_SET):to_array() or {}
    build_vallist(f, pgvals, psvals, "Values")

    if isget and gdoc then
        if isset then
            f:write_h("Getter", 3)
        else
            f:write_h("Description", 2)
        end
        f:write_raw(docm.full_str_get(eos, gdoc, nil, true))
        if isset and sdoc then
            f:write_nl(2)
        end
        if isset then
            f:write_nl()
            f:write_editable(pns, "getter_description")
            f:write_nl()
        end
    end

    if isset and sdoc then
        if isget then
            f:write_h("Setter", 3)
        else
            f:write_h("Description", 2)
        end
        f:write_raw(docm.full_str_get(eos, sdoc, nil, true))
        if isget then
            f:write_nl()
            f:write_editable(pns, "getter_description")
            f:write_nl()
        end
    end

    f:write_nl()
    if not isget or not isset then
        f:write_nl()
        f:write_br()
        f:write_editable(pns, "description")
        f:write_nl()
    end

    write_inherited_from(f, impl, cl, true)

    f:write_h("Signature", 2)
    f:write_code(eoutils.property_serialize(fn, cl))
    f:write_nl()

    f:write_h("C signature", 2)
    local codes = {}
    if isget then
        codes[#codes + 1] = eoutils.function_serialize_c(fn, eolian.function_type.PROP_GET)
    end
    if isset then
        codes[#codes + 1] = eoutils.function_serialize_c(fn, eolian.function_type.PROP_SET)
    end
    f:write_code(table.concat(codes, "\n"), "c")
    f:write_nl()

    write_ilist(f, impl, cl)
    f:write_nl()

    f:finish()
end

local build_stats_keyref = function()
    for i, cl in ipairs(dutil.filter_prop_not(eos:classes_get(), "is_beta")) do
        stats.check_class(cl)
        keyref.add(cl:name_get():gsub("%.", "_"), eoutils.obj_nspaces_get(cl), "c")
        for imp in cl:implements_get() do
            -- TODO: handle doc overrides in stats system
            if not eoutils.impl_is_overridden(imp, cl) then
                local func = imp:function_get()
                local fns = eoutils.func_nspaces_get(func, cl)
                if imp:is_prop_get() or imp:is_prop_set() then
                    if imp:is_prop_get() then
                        stats.check_property(func, cl, eolian.function_type.PROP_GET)
                        keyref.add(func:full_c_name_get(eolian.function_type.PROP_GET), fns, "c")
                    end
                    if imp:is_prop_set() then
                        stats.check_property(func, cl, eolian.function_type.PROP_SET)
                        keyref.add(func:full_c_name_get(eolian.function_type.PROP_SET), fns, "c")
                    end
                else
                    stats.check_method(func, cl)
                    keyref.add(func:full_c_name_get(eolian.function_type.METHOD), fns, "c")
                end
            end
        end
        for ev in cl:events_get() do
            keyref.add(ev:c_macro_get(), eoutils.event_nspaces_get(ev, cl), "c")
        end
    end
    for i, tp in ipairs(dutil.filter_prop_not(eos:aliases_get(), "is_beta")) do
        stats.check_alias(tp)
    end
    for i, tp in ipairs(dutil.filter_prop_not(eos:structs_get(), "is_beta")) do
        stats.check_struct(tp)
    end
    for i, tp in ipairs(dutil.filter_prop_not(eos:enums_get(), "is_beta")) do
        stats.check_enum(tp)
    end
    for i, v in ipairs(dutil.filter_prop_not(eos:constants_get(), "is_beta")) do
        stats.check_constant(v)
    end
end

local build_typedecls = function()
    for tp in eos:aliases_get() do
        if not tp:is_beta() then
            render_template("alias", eoutils.obj_nspaces_get(tp), tp:name_get(), {
                type_obj = tp
            })
        end
    end

    for tp in eos:structs_get() do
        if not tp:is_beta() then
            render_template(
                "struct_enum", eoutils.obj_nspaces_get(tp), tp:name_get(), {
                    type_obj = tp, obj_fields = tp:struct_fields_get()
                }
            )
        end
    end

    for tp in eos:enums_get() do
        if not tp:is_beta() then
            render_template(
                "struct_enum", eoutils.obj_nspaces_get(tp), tp:name_get(), {
                    type_obj = tp, obj_fields = tp:enum_fields_get()
                }
            )
        end
    end
end

local build_variables = function()
    for v in eos:constants_get() do
        if not v:is_beta() then
            render_template(
                "variable", eoutils.obj_nspaces_get(v), v:name_get(), {
                    var_obj = v, var_is_constant = true
                }
            )
        end
    end
end

local scan_directory = function(dir)
    if not dir then
        if not eos:system_directory_add() then
            error("failed scanning system directory")
        end
        return
    end
    if not eos:directory_add(dir) then
        error("failed scanning directory: " .. dir)
    end
end

local parse = function(st)
    if not eos:all_eot_files_parse() then
        error("failed parsing eo type files")
    end
    if st and st:match("%.") then
        if not eos:file_parse(st:gsub("%.", "_"):lower() .. ".eo") then
            error("failed parsing eo file")
        end
    else
        if not eos:all_eo_files_parse() then
            error("failed parsing eo files")
        end
    end
end

getopt.parse {
    args = arg,
    descs = {
        { category = "General" },
        { "h", "help", nil, help = "Show this message.", metavar = "CATEGORY",
            callback = getopt.help_cb(io.stdout)
        },
        { "v", "verbose", false, help = "Be verbose." },
        { "p", "print-gen", false, help = "Print what is being generated." },

        { category = "Generator" },
        { "r", "root", true, help = "Root path of the docs." },
        { "n", "namespace", true, help = "Root namespace of the docs." },
        { nil, "disable-notes", false, help = "Disable notes plugin usage." },
        { nil, "disable-folded", false, help = "Disable folded plugin usage." },
        { nil, "disable-title", false, help = "Disable title plugin usage." },
        { "m", "use-markdown", false,
            help = "Generate Markdown instead of DokuWiki syntax." },
        { nil, "pass", true, help = "The pass to run (optional) "
            .. "(rm, ref, clist, classes, types, vars, stats or class name)." }
    },
    error_cb = function(parser, msg)
        io.stderr:write(msg, "\n")
        getopt.help(parser, io.stderr)
    end,
    done_cb = function(parser, opts, args)
        if opts["h"] then
            return
        end
        if opts["p"] then
            printgen = function(...) print(...) end
        end
        local rootns = (not opts["n"] or opts["n"] == "")
            and "develop:api" or opts["n"]
        local dr
        if not opts["r"] or opts["r"] == "" then
            dr = "dokuwiki/data/pages"
        else
            dr = opts["r"]
        end
        dr = dutil.path_join(dr, dutil.nspace_to_path(rootns))
        dutil.init(dr, rootns)
        writer.set_backend("dokuwiki")
        eos = eolian:new()
        if #args == 0 then
            scan_directory()
        else
            for i, p in ipairs(args) do
                scan_directory(p)
            end
        end

        local st = opts["pass"]

        parse(st)
        eoutils.build_class_children(eos)

        if st == "clist" then
            for cl in eos:classes_get() do
                if not cl:is_beta() then
                    print(cl:name_get())
                end
            end
            return
        end

        local wfeatures = {
            notes = not opts["disable-notes"],
            folds = not opts["disable-folded"],
            title = not opts["disable-title"]
        }
        writer.init(rootns, wfeatures)
        docm.init(rootns, wfeatures.notes, wfeatures.title)
        if not st or st == "rm" then
            dutil.rm_root()
            dutil.mkdir_r(nil)
        end
        if not st or st == "ref" then
            build_ref()
        end
        if not st or st == "classes" then
            build_classes()
        end
        if st and st:match("%.") then
            local cl = eos:class_by_name_get(st)
            if cl then
                build_class(cl)
            end
        end
        if not st or st == "types" then
            build_typedecls()
        end
        if not st or st == "vars" then
            build_variables()
        end

        if not st or st == "stats" then
            stats.init(not not opts["v"])
            build_stats_keyref()
            keyref.build()
            -- newline if printing what's being generated
            printgen()
            stats.print()
        end
    end
}

return true