Put in a new (experimental) doc generator built only by templates

This is (in my mind) meant to replace the current elua generator.

Currently the generated output is pratically identical to the elua
one, just some little difference here and there, some for thecnical
reasons and some just for my preference.

I consider this work just a starting point, extending the
templates we can now easily improve our docs. Whithout the need
to touch a single line of code.

Really I think this is a great improvements, and this are some
numbers to prove it:

Current elua implementation:
4185 lines of code in 7 lua files
generation time: ~ 7 seconds

New generator:
115 lines of python + 513 lines of templates
generation time: ~ 8 seconds (can be optimizd ALOT)

To generate the full Efl.* docs just run "./gendoc.py -v" in this folder.

...will wait for reviews (in particular from @andy and @q66)
This commit is contained in:
Davide Andreoli 2018-01-03 16:18:58 +01:00
parent d280ab7410
commit 2f69ba27d3
7 changed files with 628 additions and 0 deletions

View File

@ -0,0 +1,24 @@
<!--(include)-->doc_macros.include<!--(end)-->#!
~~Title: ${alias.full_name}$~~
====== ${alias.full_name}$ (alias) ======
${BEST_SUMMARY(obj=alias)}$
${BEST_DESCRIPTION(obj=alias)}$
${BEST_SINCE(obj=alias)}$
${OBJECT_STATIC_CONTENT(obj=alias, section='description')}$
===== Signature =====
<code>
TODO
</code>
===== C signature =====
<code>
TODO
</code>

View File

@ -0,0 +1,106 @@
<!--(include)-->doc_macros.include<!--(end)-->#!
~~Title: ${cls.full_name}$~~
====== ${cls.full_name}$ (${CLS_TYPE}$) ======
${BEST_SUMMARY(obj=cls)}$
${BEST_DESCRIPTION(obj=cls)}$
${BEST_SINCE(obj=cls)}$
${OBJECT_STATIC_CONTENT(obj=cls, section='description')}$
<!--(if len(list(cls.inherits)) > 0)-->
===== Inheritance =====
<!--(for inherit in cls.hierarchy)-->
=> ${CLS_LINK(cls=inherit)}$#!
<!--(end)-->
++++ Full hierarchy |
<!--(for inherit in cls.inherits)-->
* ${CLS_LINK(cls=inherit)}$ //(${CLS_TYPE(cls=inherit)}$)//
<!--(for inherit in inherit.inherits)-->
* ${CLS_LINK(cls=inherit)}$ //(${CLS_TYPE(cls=inherit)}$)//
<!--(for inherit in inherit.inherits)-->
* ${CLS_LINK(cls=inherit)}$ //(${CLS_TYPE(cls=inherit)}$)//
<!--(for inherit in inherit.inherits)-->
* ${CLS_LINK(cls=inherit)}$ //(${CLS_TYPE(cls=inherit)}$)//
<!--(for inherit in inherit.inherits)-->
* ${CLS_LINK(cls=inherit)}$ //(${CLS_TYPE(cls=inherit)}$)//
<!--(for inherit in inherit.inherits)-->
* ${CLS_LINK(cls=inherit)}$ //(${CLS_TYPE(cls=inherit)}$)//
<!--(for inherit in inherit.inherits)-->
* ${CLS_LINK(cls=inherit)}$ //(${CLS_TYPE(cls=inherit)}$)//
<!--(for inherit in inherit.inherits)-->
* ${CLS_LINK(cls=inherit)}$ //(${CLS_TYPE(cls=inherit)}$)//
<!--(for inherit in inherit.inherits)-->
* ${CLS_LINK(cls=inherit)}$ //(${CLS_TYPE(cls=inherit)}$)//
<!--(for inherit in inherit.inherits)-->
* ${CLS_LINK(cls=inherit)}$ //(${CLS_TYPE(cls=inherit)}$)//
<!--(end)-->
<!--(end)-->
<!--(end)-->
<!--(end)-->
<!--(end)-->
<!--(end)-->
<!--(end)-->
<!--(end)-->
<!--(end)-->
<!--(end)-->
++++
<!--(end)-->
===== Properties =====
<!--(for impl in cls.implements)-->
<!--(if impl.is_property)-->
${IMPLEMENT_FULL(impl=impl, cls=cls)}$
\\
<!--(end)-->
<!--(else)-->
No properties defined in this class.
<!--(end)-->
===== Methods =====
<!--(for impl in cls.implements)-->
<!--(if impl.is_method)-->
${IMPLEMENT_FULL(impl=impl, cls=cls)}$
\\
<!--(end)-->
<!--(else)-->
No Methods defined in this class.
<!--(end)-->
===== Inherited members =====
<!--(for cls2 in cls.inherits_full)-->
^ ${CLS_LINK(cls=cls2)}$ //(${CLS_TYPE(cls=cls2)}$)//^^^
<!--(for impl in cls2.implements)-->
| ${FUNC_SCOPE(func=impl.function)}$ #!
| **${FUNC_LINK(func=impl.function)}$** #!
| ${BEST_SUMMARY(obj=impl)}$ |
<!--(end)-->
<!--(else)-->
No inherits defined in this class.
<!--(end)-->
===== Events =====
<!--(for i, ev in enumerate(cls.events))-->
<!--(if i == 0)-->
^ Local implemented ^^ event info ^
<!--(end)-->
| ''${ev.name}$'' | ${BEST_SUMMARY(obj=ev)}$ ${OBJECT_SCOPE(obj=ev)}$ ${EVENT_TAGS}$| ${TYPE_LINK(type=ev.type) if ev.type else 'None'}$ |
<!--(end)-->
<!--(for cls2 in cls.inherits_full)-->
<!--(for i, ev in enumerate(cls2.events))-->
<!--(if i == 0)-->
^ Inherited from ${CLS_LINK(cls=cls2)}$ //(${CLS_TYPE(cls=cls2)}$)// ^^ event info ^
<!--(end)-->
| ''${ev.name}$'' | ${BEST_SUMMARY(obj=ev)}$ ${OBJECT_SCOPE(obj=ev)}$ ${EVENT_TAGS}$ | ${TYPE_LINK(type=ev.type) if ev.type else 'None'}$ |
<!--(end)-->
<!--(end)-->

View File

@ -0,0 +1,39 @@
<!--(include)-->doc_macros.include<!--(end)-->#!
~~Title: ${enum.full_name}$~~
====== ${enum.full_name}$ (enum) ======
${BEST_SUMMARY(obj=enum)}$
${BEST_DESCRIPTION(obj=enum)}$
${BEST_SINCE(obj=enum)}$
${OBJECT_STATIC_CONTENT(obj=enum, section='description')}$
===== Fields =====
${OBJECT_STATIC_CONTENT(obj=enum, section='fields')}$
<!--(for field in enum.enum_fields)-->
* **${field.name}$** - ${BEST_SUMMARY(obj=field)}$
<!--(end)-->
===== Signature =====
<code>
enum {
<!--(for field in enum.enum_fields)-->
${field.name}$: ${field.value.serialize}$,
<!--(end)-->
}
</code>
===== C Signature =====
<code>
typedef enum {
<!--(for field in enum.enum_fields)-->
${field.c_name}$ = ${field.value.serialize}$,
<!--(end)-->
} ${enum.full_name.replace('.', '_')}$;
</code>

View File

@ -0,0 +1,253 @@
#!##############################################################################
#!#### BEST_SUMMARY(obj) #####################################################
#!##############################################################################
<!--(macro BEST_SUMMARY)-->
<!--(if isinstance(obj, Implement))-->
<!--(if obj.documentation_get(obj.function.type))-->
${UNTOKENIZE(tokens=obj.documentation_get(obj.function.type).summary_tokens)}$#!
<!--(else)-->
<!--(for parent_impl in obj.class_.implements)-->
<!--(if parent_impl.function.name == obj.function.name)-->
<!--(if parent_impl.documentation_get(parent_impl.function.type))-->
${UNTOKENIZE(tokens=parent_impl.documentation_get(parent_impl.function.type).summary_tokens)}$#!
<!--(end)-->
<!--(end)-->
<!--(end)-->
<!--(end)-->
<!--(elif obj.documentation)-->
${UNTOKENIZE(tokens=obj.documentation.summary_tokens)}$#!
<!--(else)-->
**MISSING DOCS !!!!!**#!
<!--(end)-->
<!--(end)-->
#!##############################################################################
#!#### BEST_DESCRIPTION(obj) #################################################
#!##############################################################################
<!--(macro BEST_DESCRIPTION)-->
<!--(if isinstance(obj, Implement))-->
<!--(if obj.documentation_get(obj.function.type))-->
${UNTOKENIZE(tokens=obj.documentation_get(obj.function.type).description_tokens)}$#!
<!--(else)-->
<!--(for parent_impl in obj.class_.implements)-->
<!--(if parent_impl.function.name == obj.function.name)-->
<!--(if parent_impl.documentation_get(parent_impl.function.type))-->
${UNTOKENIZE(tokens=parent_impl.documentation_get(parent_impl.function.type).description_tokens)}$#!
<!--(end)-->
<!--(end)-->
<!--(end)-->
<!--(end)-->
<!--(elif obj.documentation)-->
${UNTOKENIZE(tokens=obj.documentation.description_tokens)}$#!
<!--(else)-->
**MISSING DOCS !!!!!**#!
<!--(end)-->
<!--(end)-->
#!##############################################################################
#!#### BEST_SINCE(obj) #######################################################
#!##############################################################################
<!--(macro BEST_SINCE)-->
<!--(if obj.documentation and obj.documentation.since)-->
//Since ${obj.documentation.since}$//
<!--(end)-->
<!--(end)-->
#!##############################################################################
#!#### UNTOKENIZE(tokens) ####################################################
#!##############################################################################
<!--(macro UNTOKENIZE)-->
<!--(for i, paragraph in enumerate(tokens))-->
${'\n\n' if i else ''}$#!
<!--(for token in paragraph)-->
<!--(if token.type == Eolian_Doc_Token_Type.TEXT)-->
%%${token.text}$%%#!
<!--(elif token.type == Eolian_Doc_Token_Type.REF)-->
**REF ${token.text}$ ${token.ref}$ ??**#!
<!--(elif token.type == Eolian_Doc_Token_Type.MARKUP_MONOSPACE)-->
''${token.text}$''#!
<!--(elif token.type == Eolian_Doc_Token_Type.MARK_NOTE)-->
<note>
${token.text}$
</note>
<!--(elif token.type == Eolian_Doc_Token_Type.MARK_WARNING)-->
<note warning>
${token.text}$
</note>
<!--(elif token.type == Eolian_Doc_Token_Type.MARK_REMARK)-->
<note tip>
${token.text}$
</note>
<!--(elif token.type == Eolian_Doc_Token_Type.MARK_TODO)-->
<note>
**TODO:** ${token.text}$
</note>
<!--(else)-->
**USUPPORTED TOKEN TYPE ${token}$**
<!--(end)-->
<!--(end)-->
<!--(end)-->
<!--(end)-->
#!##############################################################################
#!#### CLS_TYPE(cls) #########################################################
#!##############################################################################
<!--(macro CLS_TYPE)-->
<!--(if cls.type == Eolian_Class_Type.REGULAR)-->
class#!
<!--(elif cls.type == Eolian_Class_Type.ABSTRACT)-->
class#!
<!--(elif cls.type == Eolian_Class_Type.MIXIN)-->
mixin#!
<!--(elif cls.type == Eolian_Class_Type.INTERFACE)-->
interface#!
<!--(end)-->
<!--(end)-->
#!##############################################################################
#!#### CLS_LINK(cls) #########################################################
#!##############################################################################
<!--(macro CLS_LINK)-->
[[:develop:api#!
<!--(for n in cls.namespaces)-->
:${n.lower()}$#!
<!--(end)-->
:${cls.name.lower()}$|${cls.full_name}$]]
<!--(end)-->
#!##############################################################################
#!#### EVENT_LINK(cls, ev) ###################################################
#!##############################################################################
<!--(macro EVENT_LINK)-->
[[:develop:api#!
<!--(for n in cls.namespaces)-->
:${n.lower()}$#!
<!--(end)-->
:${cls.name.lower()}$#!
:event#!
:${ev.name.lower().replace(',','_')}$|${ev.name}$]]
<!--(end)-->
#!##############################################################################
#!#### TYPEDECL_LINK(typedecl) ###############################################
#!##############################################################################
<!--(macro TYPEDECL_LINK)-->
[[:develop:api#!
<!--(for n in typedecl.namespaces)-->
:${n.lower()}$#!
<!--(end)-->
:${typedecl.name.lower()}$|${typedecl.full_name}$]]
<!--(end)-->
#!##############################################################################
#!#### TYPE_LINK(type) #######################################################
#!##############################################################################
<!--(macro TYPE_LINK)-->
[[:develop:api#!
<!--(for n in type.namespaces)-->
:${n.lower()}$#!
<!--(end)-->
:${type.name.lower()}$|${type.full_name}$]]
<!--(end)-->
#!##############################################################################
#!#### FUNC_LINK(func) #######################################################
#!##############################################################################
<!--(macro FUNC_LINK)-->
[[:develop:api#!
<!--(for n in func.class_.namespaces)-->
:${n.lower()}$#!
<!--(end)-->
<!--(if func.type == Eolian_Function_Type.METHOD)-->
:method#!
<!--(else)-->
:property#!
<!--(end)-->
:${func.name}$|${func.name}$]]
<!--(end)-->
#!##############################################################################
#!#### FUNC_SCOPE(func) ######################################################
#!##############################################################################
<!--(macro FUNC_SCOPE)-->
<!--(if func.is_class)-->
''class method'' #!
<!--(end)-->
<!--(if func.type == Eolian_Function_Type.PROPERTY)-->
<!--(if func.getter_scope == Eolian_Object_Scope.PROTECTED)-->
''protected get'' #!
<!--(elif func.getter_scope == Eolian_Object_Scope.PRIVATE)-->
''private get'' #!
<!--(end)-->
<!--(if func.setter_scope == Eolian_Object_Scope.PROTECTED)-->
''protected set'' #!
<!--(elif func.setter_scope == Eolian_Object_Scope.PRIVATE)-->
''private set'' #!
<!--(end)-->
<!--(elif func.type == Eolian_Function_Type.METHOD)-->
<!--(if func.method_scope == Eolian_Object_Scope.PROTECTED)-->
''protected'' #!
<!--(elif func.method_scope == Eolian_Object_Scope.PRIVATE)-->
''private'' #!
<!--(end)-->
<!--(end)-->
<!--(end)-->
#!##############################################################################
#!#### OBJECT_SCOPE(obj) #####################################################
#!##############################################################################
<!--(macro OBJECT_SCOPE)-->
<!--(if obj.scope == Eolian_Object_Scope.PROTECTED)-->
''private'' #!
<!--(elif obj.scope == Eolian_Object_Scope.PRIVATE)-->
''protected'' #!
<!--(end)-->
<!--(end)-->
#!##############################################################################
#!#### EVENT_TAGS(ev) ########################################################
#!##############################################################################
<!--(macro EVENT_TAGS)-->
<!--(if ev.is_hot)-->
''hot'' #!
<!--(elif ev.is_restart)-->
''restart'' #!
<!--(end)-->
<!--(end)-->
#!##############################################################################
#!#### IMPLEMENT_FULL(impl, cls) #############################################
#!##############################################################################
<!--(macro IMPLEMENT_FULL)-->
**${FUNC_LINK(func=impl.function)}$** #!
(#!
<!--(if impl.is_property)-->
<!--(for i, val in enumerate(impl.function.getter_values or impl.function.setter_values))-->
${', ' if i else ''}$${val.type.name}$#!
<!--(end)-->
<!--(elif impl.is_method)-->
<!--(for i, param in enumerate(impl.function.parameters))-->
${', ' if i else ''}$#!
//${param.type.name}$// ''${param.direction.name.lower()}$'' **${param.name}$**#!
<!--(end)-->
<!--(end)-->
)#!
<!--(if impl.is_prop_get and impl.is_prop_set)-->
''rw'' #!
<!--(elif impl.is_prop_get)-->
''read only'' #!
<!--(elif impl.is_prop_set)-->
''write only'' #!
<!--(elif impl.is_method)-->
<!--(if impl.function.method_return_type)-->
=> //${impl.function.method_return_type.name}$// #!
<!--(else)-->
=> //None// #!
<!--(end)-->
<!--(end)-->
${FUNC_SCOPE(func=impl.function)}$#!
<!--(if impl.class_ != cls)-->
//[Overridden from ${CLS_LINK(cls=impl.class_)}$]// #!
<!--(end)-->
\\
> ${BEST_SUMMARY(obj=impl)}$
<!--(end)-->
#!##############################################################################
#!#### OBJECT_STATIC_CONTENT(obj, section) ###################################
#!##############################################################################
<!--(macro OBJECT_STATIC_CONTENT)-->
{{page>:develop:api-include#!
<!--(for ns in obj.namespaces)-->
:${ns.lower()}$#!
<!--(end)-->
:${obj.name.lower()}$#!
:{section}#!
&nouser&nolink&nodate}}
<!--(end)-->

View File

@ -0,0 +1,52 @@
<!--(include)-->doc_macros.include<!--(end)-->#!
~~Title: EFL Reference~~
{{page>:develop:api-include:reference:general&nouser&nolink&nodate}}
<!--(for ns in nspaces)-->
<!--(if ns.name.startswith('Efl'))-->
===== ${ns.name}$ =====
<!--(for i, cls in enumerate(ns.regulars))-->
<!--(if i == 0)-->
^ Classes ^^
<!--(end)-->
| ${CLS_LINK}$ | ${BEST_SUMMARY(obj=cls)}$ |
<!--(end)-->
#!
<!--(for i, cls in enumerate(ns.interfaces))-->
<!--(if i == 0)-->
^ Interfaces ^^
<!--(end)-->
| ${CLS_LINK}$ | ${BEST_SUMMARY(obj=cls)}$ |
<!--(end)-->
#!
<!--(for i, cls in enumerate(ns.mixins))-->
<!--(if i == 0)-->
^ Mixins ^^
<!--(end)-->
| ${CLS_LINK}$ | ${BEST_SUMMARY(obj=cls)}$ |
<!--(end)-->
#!
<!--(for i, typedecl in enumerate(ns.aliases))-->
<!--(if i == 0)-->
^ Aliases ^^
<!--(end)-->
| ${TYPEDECL_LINK}$ | ${BEST_SUMMARY(obj=typedecl)}$ |
<!--(end)-->
#!
<!--(for i, typedecl in enumerate(ns.structs))-->
<!--(if i == 0)-->
^ Structures ^^
<!--(end)-->
| ${TYPEDECL_LINK}$ | ${BEST_SUMMARY(obj=typedecl)}$ |
<!--(end)-->
#!
<!--(for i, typedecl in enumerate(ns.enums))-->
<!--(if i == 0)-->
^ Enumerations ^^
<!--(end)-->
| ${TYPEDECL_LINK}$ | ${BEST_SUMMARY(obj=typedecl)}$ |
<!--(end)-->
<!--(end)-->
<!--(end)-->

View File

@ -0,0 +1,39 @@
<!--(include)-->doc_macros.include<!--(end)-->#!
~~Title: ${struct.full_name}$~~
====== ${struct.full_name}$ (struct) ======
${BEST_SUMMARY(obj=struct)}$
${BEST_DESCRIPTION(obj=struct)}$
${BEST_SINCE(obj=struct)}$
${OBJECT_STATIC_CONTENT(obj=struct, section='description')}$
===== Fields =====
${OBJECT_STATIC_CONTENT(obj=struct, section='fields')}$
<!--(for field in struct.struct_fields)-->
* **${field.name}$** - ${BEST_SUMMARY(obj=field)}$
<!--(end)-->
===== Signature =====
<code>
struct ${struct.full_name}$ {
<!--(for field in struct.struct_fields)-->
${field.name}$: ${field.type.name}$,
<!--(end)-->
}
</code>
===== C Signature =====
<code>
typedef struct _${struct.full_name.replace('.', '_')}$ {
<!--(for field in struct.struct_fields)-->
${field.name}$: **TODO (issue with Typedecl.c_type need Unit)**,
<!--(end)-->
} ${struct.full_name.replace('.', '_')}$;
</code>

115
src/scripts/gendoc/gendoc.py Executable file
View File

@ -0,0 +1,115 @@
#!/usr/bin/env python3
# encoding: utf-8
"""
Efl documentation generator
Use this script without arguments to generate the full documentation of the Efl
namespace in a folder called 'dokuwiki' (-v to see all generated files)
--help to see all other options
"""
import os
import sys
import argparse
import atexit
# Use .eo files from the source tree (not the installed ones)
script_path = os.path.dirname(os.path.realpath(__file__))
root_path = os.path.abspath(os.path.join(script_path, '..', '..', '..'))
SCAN_FOLDER = os.path.join(root_path, 'src', 'lib')
# Use pyolian from source (not installed)
pyolian_path = os.path.join(root_path, 'src', 'scripts')
sys.path.insert(0, pyolian_path)
from pyolian import eolian
from pyolian.generator import Template
# parse args
parser = argparse.ArgumentParser(description='Pyolian DocuWiki generator.')
parser.add_argument('--root-path', '-r', metavar='FOLDER', default='dokuwiki',
help='where to write files to (root of dokuwiki) '
'default to: "./dokuwiki"')
parser.add_argument('--verbose', '-v', action='store_true',
help='print a line for each rendered file')
parser.add_argument('--namespace', '-n', metavar='ROOT', default='Efl',
help='root namespace of the docs. (default to "Efl")')
_choices = ['start', 'classes', 'enums', 'structs', 'aliases']
parser.add_argument('--step', '-s', metavar='STEP', default=None,
choices=_choices,
help='A single step to run (default to all), '
'valid choises: '+ ', '.join(_choices))
args = parser.parse_args()
# load the whole eolian db (from .eo files in source tree)
eolian_db = eolian.Eolian()
if not isinstance(eolian_db, eolian.Eolian):
raise(RuntimeError('Eolian, failed to create Eolian state'))
if not eolian_db.directory_scan(SCAN_FOLDER):
raise(RuntimeError('Eolian, failed to scan source directory'))
if not eolian_db.all_eot_files_parse():
raise(RuntimeError('Eolian, failed to parse all EOT files'))
if not eolian_db.all_eo_files_parse():
raise(RuntimeError('Eolian, failed to parse all EO files'))
# cleanup the database on exit
def cleanup_db():
global eolian_db
del eolian_db
atexit.register(cleanup_db)
# calculate the full path for the txt page of the given object
def page_path_for_object(obj):
path = ['data', 'pages', 'develop', 'api']
for ns in obj.namespaces:
path.append(ns.lower())
output_file = obj.name.lower() + '.txt'
return os.path.join(args.root_path, *path, output_file)
# render the main start.txt page
if args.step in ('start', None):
t = Template('doc_start.template')
output_file = os.path.join(args.root_path,'data','pages','develop','api','start.txt')
t.render(output_file, args.verbose, nspaces=eolian_db.all_namespaces)
# render a page for each Class
if args.step in ('classes', None):
t = Template('doc_class.template')
for cls in eolian_db.all_classes:
if cls.full_name.startswith(args.namespace):
output_file = page_path_for_object(cls)
t.render(output_file, args.verbose, cls=cls.full_name)
# render a page for each Enum
if args.step in ('enums', None):
t = Template('doc_enum.template')
for enum in eolian_db.typedecl_all_enums:
if enum.full_name.startswith(args.namespace):
output_file = page_path_for_object(enum)
t.render(output_file, args.verbose, enum=enum.full_name)
# render a page for each Struct
if args.step in ('structs', None):
t = Template('doc_struct.template')
for struct in eolian_db.typedecl_all_structs:
if struct.full_name.startswith(args.namespace):
output_file = page_path_for_object(struct)
t.render(output_file, args.verbose, struct=struct.full_name)
# render a page for each Alias
if args.step in ('aliases', None):
t = Template('doc_alias.template')
for alias in eolian_db.typedecl_all_aliases:
if alias.full_name.startswith(args.namespace):
output_file = page_path_for_object(alias)
t.render(output_file, args.verbose, alias=alias.full_name)