#!/usr/local/bin/python3.8
# coding: utf-8
# Dear future self,
#
# You're looking at this file because
# the parse function finally broke.
#
# It's not fixable. You have to rewrite it.
# Sincerely, past self
#
# Also, it's probably at least
# 2013. Did you ever take
# that trip to Iceland?
import re
def get_type_link(typ, file):
from gen_doc import objects
if typ == '':
return "void"
else:
if typ in objects:
return "cinnamon-js-" + objects[typ].prefix
elif file.name + "." + typ in objects:
return "cinnamon-js-" + objects[file.name + "." + typ].prefix
elif typ.endswith("s") and typ[:-1] in objects:
return "cinnamon-js-" + objects[typ[:-1]].prefix
elif typ.endswith("s") and file.name + "." + typ[:-1] in objects:
return "cinnamon-js-" + objects[file.name + "." + typ[:-1]].prefix
elif typ.startswith("Gio"):
return typ.replace("Gio.", "G")
elif typ.startswith("GLib"):
return typ.replace("GLib.", "G")
else:
return typ.replace('.', '')
def markup(line, obj):
line = re.sub('@(\w*)', '\g<1>
', line)
line = re.sub('`([^`]*)`', '\g<1>
', line)
line = re.sub('\*\*([^*]*)\*\*', '\g<1>', line)
line = re.sub('\*([^*]*)\*', '\g<1>', line)
def format_type_link(match):
res = match.group(1)
return '{name}
'.format(
link = get_type_link(res, obj.file),
name = res)
line = re.sub('#(([\w]*\.)?[\w]+)', format_type_link, line)
def format_ext_link(match):
if match.group(1):
full = match.group(1) + match.group(3)
else:
full = match.group(3)
if match.group(4):
full += match.group(4)
owner = match.group(1)
if owner:
owner = owner[:-1] # remove trailing .
else:
owner = "this"
thing = match.group(3)
from gen_doc import objects
object = None
if owner == "this":
object = obj.object
if owner in objects:
object = objects[owner]
elif obj.file.name + "." + owner in objects:
object = objects[obj.file.name + "." + owner]
if object is None:
return '{name}
'.format(name = full)
func_names = [x.name for x in object.functions]
enum_names = [x.name for x in object.enums]
prop_names = [x.name for x in object.properties]
if thing in prop_names and not full.endswith("()"):
return '{full}
'.format(
prefix = object.prefix,
thing = thing,
full = full)
elif thing in func_names or (thing in enum_names and not full.endswith("()")):
return '{full}
'.format(
prefix = object.prefix,
thing = thing,
full = full)
else:
return '{name}
'.format(name = full)
line = re.sub('%(([\w]+\.)?[\w]+\.)?([\w]+)(\(\))?', format_ext_link, line)
return line
class JSThing():
def append_description(self, desc):
self.description += desc.replace('<', '<').replace('>', '>')
def get_xml_description(self, description = None):
if description is None:
description = self.description
stuff = description.split('\n')
joined = ['']
in_code = False
in_list = False
for line in stuff:
if line.strip() == '```':
if in_code:
joined[-1] += '```'
joined.append('')
else:
if in_list:
joined[-1] += '\n```'
else:
joined.append('```\n')
in_code = not in_code
continue
if in_code:
joined[-1] += '\n' + line
continue
line = line.strip()
if line == '\\' and in_list:
joined[-1] += '\n\n'
elif len(line) == 0 or line == '\\':
# New line if empty
joined.append('')
in_list = False
else:
if joined[-1] == '' and line.startswith('- '):
in_list = True
if line.startswith('- '):
joined.append('')
joined[-1] += ' ' + line
description = ''
in_list = False
list_buffer = []
for line in joined:
if line.split('\n')[0].strip() == '```':
description += '{0}'\
.format(line.replace('```', ''))
continue
if line == '':
continue
line = line.strip()
if line.startswith('-'):
in_list = True
list_buffer.append(self.get_xml_description(line[1:]))
continue
if in_list:
description += '' + \
'\n'.join('{0}'.format(item) for item in list_buffer) + \
''
list_buffer = []
in_list = False
line = markup(line, self)
description += '{0}'.format(line)
if in_list:
description += '' + \
'\n'.join('{0}'.format(item) for item in list_buffer) + \
''
list_buffer = []
return description
def add_property(self, prop):
if prop.name == "short_description":
self.short_description = prop
else:
self.properties.append(prop)
prop.file = self.file
prop.object = self.object
class JSSignal(JSThing):
def __init__ (self, name):
self.name = name
self.description = ''
self.short_description = JSProperty(None, '', '')
self.properties = []
class JSFunction(JSThing):
def __init__ (self, name):
self.name = name
self.description = ''
self.short_description = JSProperty(None, '', '')
self.properties = []
self.return_value = JSProperty(None, '', '')
def set_return(self, retval):
self.return_value = retval
retval.file = self.file
retval.obj = self.object
class JSProperty(JSThing):
def __init__ (self, name, arg_type, desc):
self.name = name
self.arg_type = arg_type if arg_type else ''
self.description = ''
self.append_description(desc + "\n")
class JSFile(JSThing):
def __init__ (self, directory, name):
self.directory = directory
self.name = name[0].capitalize() + name[1:]
self.orig_name = self.name
self.imports = "imports.{0}.{1}".format(directory, name)
self.prefix = directory + "-" + name
self.description = ''
self.short_description = JSProperty(None, '', '')
self.properties = []
self.objects = []
self.signals = []
self.enums = []
self.functions = []
self.file = self
self.object = self
def is_interesting(self):
return len(self.functions) + len(self.properties) + len(self.description) > 0
def add_function(self, func):
self.functions.append(func)
func.file = self
func.object = self
def add_object(self, obj):
self.objects.append(obj)
obj.parent = self
obj.directory = self.directory
obj.prefix = self.prefix + "-" + obj.name
obj.name = self.name + "-" + obj.name
obj.file = self
def add_enum(self, obj):
self.enums.append(obj)
obj.parent = self
obj.directory = self.directory
obj.prefix = self.prefix + "-" + obj.name
obj.file = self
class JSObject(JSThing):
def __init__ (self, name):
self.name = name
self.orig_name = name
self.inherit = ''
self.description = ''
self.short_description = JSProperty(None, '', '')
self.parent = None
self.directory = None
self.prefix = None
self.functions = []
self.properties = []
self.signals = []
self.enums = []
self.object = self
def add_function(self, func):
self.functions.append(func)
func.file = self.file
func.object = self
def add_signal(self, signal):
self.signals.append(signal)
signal.file = self
signal.object = self
def set_inherit(self, inherit):
self.inherit = inherit
class JSEnum(JSThing):
def __init__ (self, name):
self.name = name
self.description = ''
self.short_description = JSProperty(None, '', '')
self.properties = []
self.object = self
PART_FORMAT = '''\
]>
{chapters}
'''
SGML_CHAPTER_FORMAT = '''
{title}
{entries}
'''
SGML_ENTRY_FORMAT = ''
FILE_FORMAT = '''\
]>
{name}
3
{name}
{name}
{short_description}
{func_header}
{prop_header}
{signal_header}
{enum_header}
{hierarchy}
{description}
{functions}
{properties}
{signals}
{enums}
'''
FUNCTION_HEADER_FORMAT = '''
Functions
{function_headers}
'''
FUNCTION_HEADER_ITEM_FORMAT = '''
{return_name}
{name} ()
'''
PROPERTY_HEADER_FORMAT = '''
Properties
{property_headers}
'''
SIGNAL_HEADER_FORMAT = '''
Signals
{signal_headers}
'''
SIGNAL_HEADER_ITEM_FORMAT = '''
{name}
'''
ENUM_HEADER_FORMAT = '''
Types and Values
{enum_headers}
'''
ENUM_HEADER_ITEM_FORMAT = '''
enum
{name}
'''
PROPERTY_HEADER_ITEM_FORMAT = '''
{type_name}
{name}
'''
HIERARCHY_FORMAT = '''
Object Hierarchy
Object
{hierarchy}
'''
HIERARCHY_ITEM_FORMAT = '{spacing}╰── {name}'
DESCRIPTION_FORMAT = '''
Description
{description}
'''
FUNCTIONS_FORMAT = '''
Functions
{functions}
'''
FUNCTION_ITEM_FORMAT = '''
{name} ()
{name}
{return_type}
{name} ({inline_params});
{description}
{params}
{return_desc}
'''
SIGNALS_FORMAT = '''
Signal details
{signals}
'''
SIGNAL_ITEM_FORMAT = '''
The “{name}” signal
{prefix}::{name}
user_function ({inline_params});
{description}
{params}
'''
FUNC_PARAMETERS_FORMAT = '''
Parameters
{param_items}
'''
INLINE_PARAMETER_FORMAT = '{type_name}{name}'
FUNC_PARAMETERS_ITEM_FORMAT = '''
{name}
{description}
'''
FUNC_RETURN_FORMAT = '''
Returns
{desc}
'''
PROPERTIES_FORMAT = '''
Property Details
{properties}
'''
PROPERTIES_ITEM_FORMAT = '''
The “{name}” property
cinnamon-js-{prefix}:{name}
{disp_name} {type_name}
{description}
'''
ENUMS_FORMAT = '''
Types and Values
{enums}
'''
ENUMS_ITEM_FORMAT = '''
enum {name}
{name}
{description}
Members
{enum_items}
'''
ENUMS_ITEM_ROW_FORMAT = '''
{name}
{description}
'''
def write_chapters_file(files):
chapters = {'ui': [], 'misc': []}
for _file in files:
if not _file.is_interesting() and len(_file.objects) == 0:
continue
entries = []
if _file.is_interesting():
_file.objects.insert(0, _file)
entries = [SGML_ENTRY_FORMAT.format(
directory = _file.directory,
name = obj.name) for obj in _file.objects]
chapters[_file.directory].append(SGML_CHAPTER_FORMAT.format(
prefix = _file.prefix,
title = _file.imports,
entries = "\n".join(entries)))
for directory, formatted_chapters in chapters.items():
with open(directory + '.xml', 'w') as part_file:
part_file.write(PART_FORMAT.format(title=directory, chapters="\n".join(formatted_chapters)))
def create_file(obj):
file_obj = open('{0}/{1}.xml'.format(obj.directory, obj.name), 'w', encoding="utf-8")
short_description = obj.short_description.description.replace("\n", " ").strip()
file_obj.write(FILE_FORMAT.format(
prefix = obj.prefix,
name = obj.name.replace("-", "."),
short_description = markup(short_description, obj),
func_header = get_function_header(obj),
signal_header = get_signal_header(obj),
prop_header = get_properties_header(obj),
enum_header = get_enum_header(obj),
hierarchy = get_hierarchy(obj),
description = get_description(obj),
functions = get_functions(obj),
signals = get_signals(obj),
properties = get_properties(obj),
enums = get_enums(obj)))
file_obj.close()
def get_function_header(obj):
if len(obj.functions) == 0:
return ""
functions = [FUNCTION_HEADER_ITEM_FORMAT.format(
return_link = get_type_link(func.return_value.arg_type, obj.file),
return_name = func.return_value.arg_type,
prefix = obj.prefix,
name = func.name) for func in obj.functions]
return FUNCTION_HEADER_FORMAT.format(
prefix = obj.prefix,
function_headers = "\n".join(functions))
def get_signal_header(obj):
if len(obj.signals) == 0:
return ""
signals = [SIGNAL_HEADER_ITEM_FORMAT.format(
prefix = obj.prefix,
name = sig.name) for sig in obj.signals]
return SIGNAL_HEADER_FORMAT.format(
prefix = obj.prefix,
signal_headers = "\n".join(signals))
def get_properties_header(obj):
if len(obj.properties) == 0:
return ""
properties = [PROPERTY_HEADER_ITEM_FORMAT.format(
type_link = get_type_link(prop.arg_type, obj.file),
type_name = prop.arg_type,
prefix = obj.prefix,
name = prop.name) for prop in obj.properties]
return PROPERTY_HEADER_FORMAT.format(
prefix = obj.prefix,
property_headers = "\n".join(properties))
def get_enum_header(obj):
if len(obj.enums) == 0:
return ""
enums = [ENUM_HEADER_ITEM_FORMAT.format(
prefix = obj.prefix,
name = enum.name) for enum in obj.enums]
return ENUM_HEADER_FORMAT.format(
prefix = obj.prefix,
enum_headers = "\n".join(enums))
def get_hierarchy(obj):
from gen_doc import objects
if isinstance(obj, JSFile):
return ""
name = obj.name.replace('-', '.')
hierarchy = []
try:
while True:
name = objects[name].inherit
if name in hierarchy:
break
if name:
hierarchy.insert(0, name)
except KeyError:
pass
count = 1
hierarchy_strs = []
for item in hierarchy:
try:
hierarchy_strs.append(HIERARCHY_ITEM_FORMAT.format(
spacing = ' ' * count * 4,
prefix = objects[item].prefix,
name = item))
except KeyError:
hierarchy_strs.append(HIERARCHY_ITEM_FORMAT.format(
spacing = ' ' * count * 4,
prefix = "void",
name = item))
count += 1
hierarchy_strs.append(HIERARCHY_ITEM_FORMAT.format(
spacing = ' ' * count * 4,
prefix = "void",
name = obj.name.replace('-', '.')))
return HIERARCHY_FORMAT.format(
prefix = obj.prefix,
hierarchy = "\n".join(hierarchy_strs))
def get_description(obj):
if len(obj.description) == 0:
return ""
return DESCRIPTION_FORMAT.format(
prefix=obj.prefix,
description = obj.get_xml_description())
def get_functions(obj):
if len(obj.functions) == 0:
return ""
functions = []
for func in obj.functions:
inline_params = ""
params = ""
if len(func.properties) > 0:
# Calculate how long the argument types are and make the arguments
# align
max_length = max(len(x.arg_type) for x in func.properties) + 3
# If no parameter has argument types, don't show that silly
# whitespace
if max_length == 3:
max_length = 0
inline_params = [INLINE_PARAMETER_FORMAT.format(
type_link = get_type_link(param.arg_type, obj.file),
type_name = param.arg_type,
name = " " * (max_length - len(param.arg_type)) + param.name) for param in func.properties]
inline_params = (',\n' + ' ' * (len(func.name) + 2)).join(inline_params)
params = [FUNC_PARAMETERS_ITEM_FORMAT.format(
name = param.name,
description = param.get_xml_description()) for param in func.properties]
params = FUNC_PARAMETERS_FORMAT.format(param_items = '\n'.join(params))
return_desc = ""
if func.return_value.name is not None:
return_desc = FUNC_RETURN_FORMAT.format(desc=func.return_value.get_xml_description())
functions.append(FUNCTION_ITEM_FORMAT.format(
prefix = obj.prefix,
name = func.name,
return_link = get_type_link(func.return_value.arg_type, obj.file),
return_type = func.return_value.arg_type,
description = func.get_xml_description(),
inline_params = inline_params,
params = params,
return_desc = return_desc))
return FUNCTIONS_FORMAT.format(
prefix = obj.prefix,
functions = "\n".join(functions))
def get_signals(obj):
if len(obj.signals) == 0:
return ""
signals = []
for sig in obj.signals:
inline_params = ""
params = ""
if len(sig.properties) > 0:
# Calculate how long the argument types are and make the arguments
# align
max_length = max(len(x.arg_type) for x in sig.properties) + 3
# If no parameter has argument types, don't show that silly
# whitespace
if max_length == 3:
max_length = 0
inline_params = [INLINE_PARAMETER_FORMAT.format(
type_link = get_type_link(param.arg_type, obj.file),
type_name = param.arg_type,
name = " " * (max_length - len(param.arg_type)) + param.name) for param in sig.properties]
inline_params = (',\n' + ' ' * (len(sig.name) + 2)).join(inline_params)
params = [FUNC_PARAMETERS_ITEM_FORMAT.format(
name = param.name,
description = param.get_xml_description()) for param in sig.properties]
params = FUNC_PARAMETERS_FORMAT.format(param_items = '\n'.join(params))
signals.append(SIGNAL_ITEM_FORMAT.format(
prefix = obj.prefix,
name = sig.name,
description = sig.get_xml_description(),
inline_params = inline_params,
params = params))
return SIGNALS_FORMAT.format(
prefix = obj.prefix,
signals = "\n".join(signals))
def get_properties(obj):
if len(obj.properties) == 0:
return ""
properties = [PROPERTIES_ITEM_FORMAT.format(
prefix = obj.prefix,
name = prop.name,
disp_name = ('“' + prop.name + '”').ljust(25),
type_link = get_type_link(prop.arg_type, obj.file),
type_name = prop.arg_type,
description = prop.get_xml_description()) for prop in obj.properties]
return PROPERTIES_FORMAT.format(
prefix = obj.prefix,
properties = "\n".join(properties))
def get_enums(obj):
if len(obj.enums) == 0:
return ""
enums = []
for enum in obj.enums:
items = [ENUMS_ITEM_ROW_FORMAT.format(
name = item.name,
description = item.get_xml_description()) for item in enum.properties]
enums.append(ENUMS_ITEM_FORMAT.format(
prefix = enum.prefix,
name = enum.name,
description = enum.get_xml_description(),
enum_items = "\n".join(items)))
return ENUMS_FORMAT.format(
prefix = obj.prefix,
enums = "\n".join(enums))