import sys import string import os import posixpath import codecs import re import json import operator import shutil import datetime import textwrap class LuaDoc: """ Documentation generator for Traintastic's builtin Lua scripting language """ DEFAULT_LANGUAGE = 'en-us' FILENAME_INDEX = 'index.md' FILENAME_GLOBALS = 'globals.md' FILENAME_PV = 'pv.md' FILENAME_ENUM = 'enum.md' FILENAME_SET = 'set.md' FILENAME_OBJECT = 'object/index.md' FILENAME_EXAMPLES = 'examples.md' FILENAME_INDEX_AZ = 'index-az.md' missing_terms = [] version = None def __init__(self, project_root: str) -> None: self._project_root = project_root self._globals = LuaDoc._find_globals(project_root) self._enums = LuaDoc._find_enums(project_root) self._sets = LuaDoc._find_sets(project_root) self._libs = LuaDoc._find_libs(project_root) self._objects = LuaDoc._find_objects(project_root) self._object_categories, self._object_category_other = LuaDoc._build_category_tree(self._objects) self._examples = LuaDoc._find_examples(project_root) self._patch() self._add_cross_references() self.set_language(LuaDoc.DEFAULT_LANGUAGE) def set_language(self, language: str) -> None: self._language = language self._terms = LuaDoc._load_terms(language) self.missing_terms = [] def _get_term(self, term: str, optional: bool = False) -> str: if term not in self._terms: if optional: return None if term not in self.missing_terms: self.missing_terms.append(term) return '$' + term + '$' definition = self._terms[term] definition = re.sub(r'{ref:([a-z0-9_\.]+?)(|#[a-z0-9_]+)(|\|.+?)}', self._ref_link, definition) return definition def _load_terms(language: str) -> dict: terms = {} for item in json.loads(LuaDoc._read_file(posixpath.join(os.path.dirname(__file__), 'luadoc', 'terms', language + '.json'))): if item['definition'] is not None and item['definition'] != '': terms[item['term']] = item['definition'] return terms def _add_cross_references(self): for object in self._objects: for item in object['items']: cpp_types = [] if item['type'] == 'property' and 'cpp_template_type' in item: cpp_types = [item['cpp_template_type']] elif item['type'] == 'method' and 'cpp_template_type' in item: [result, args] = item['cpp_template_type'].rstrip(')').split('(') cpp_types = [result] + [s.strip() for s in args.split(',')] elif item['type'] == 'event': cpp_types = [s.strip() for s in item['cpp_template_type'].split(',')] for cpp_type in cpp_types: for enum in self._enums: if enum['cpp_name'] == cpp_type: enum['see_also'].append('[`' + object['lua_name'] + '.' + item['lua_name'] + '`](../' + object['filename'] + '#' + item['lua_name'] + ')') for set in self._sets: if set['cpp_name'] == cpp_type: set['see_also'].append('[' + object['lua_name'] + '.' + item['lua_name'] + '](../' + object['filename'] + '#' + item['lua_name'] + ')') def _ref_link(self, m: re.Match) -> str: id = m.group(1) fragment = m.group(2) title = m.group(3).lstrip('|') if id.startswith('enum.'): id = id.removeprefix('enum.') for enum in self._enums: if enum['lua_name'] == id: return '[' + (self._get_term(enum['name']) if title == '' else title) + '](' + enum['filename'] + fragment + ')' elif id.startswith('set.'): id = id.removeprefix('set.') for set in self._sets: if set['lua_name'] == id: return '[' + (self._get_term(set['name']) if title == '' else title) + '](' + set['filename'] + fragment + ')' elif id.startswith('object.'): for object in self._objects: if object['lua_name'] == id: return '[' + (self._get_term(object['name']) if title == '' else title) + '](' + object['filename'] + fragment + ')' elif id == 'globals': return '[' + (self._get_term('globals:title') if title == '' else title) + '](' + self.FILENAME_GLOBALS + fragment + ')' elif id == 'enum': return '[' + (self._get_term('enum:title') if title == '' else title) + '](' + self.FILENAME_ENUM + fragment + ')' elif id == 'set': return '[' + (self._get_term('set:title') if title == '' else title) + '](' + self.FILENAME_SET + fragment + ')' elif id == 'object': return '[' + (self._get_term('object:title') if title == '' else title) + '](' + self.FILENAME_OBJECT + fragment + ')' elif id == 'pv': return '[' + (self._get_term('pv:title') if title == '' else title) + '](' + self.FILENAME_PV + fragment + ')' return '' + m.group(0) + '' def _fix_links(md: str, cur_dir: str): if cur_dir != '': md = re.sub(r'\]\(((enum|set|object)/[^\)]+)\)', lambda m : LuaDoc._fix_link_helper(m, cur_dir), md) return md def _fix_link_helper(m: re.Match, cur_dir: str): link = m[1] if link.startswith(cur_dir + '/'): link = link[len(cur_dir) + 1:] else: link = '../' + link return '](' + link + ')' def _load_data(items: list, filename: str) -> list: data = json.loads(LuaDoc._read_file(filename)) new_items = [] for item in items: if isinstance(item, str): item = {'lua_name': item} if item['lua_name'] not in data: raise RuntimeError('"{:s}" missing in {:s}'.format(item['lua_name'], filename)) if 'parameters' in item: params_in_data = len(data[item['lua_name']]['parameters']) if 'parameters' in data[item['lua_name']] else 0 if len(item['parameters']) != params_in_data: raise RuntimeError('"{:s}" invalid number of parameters, expected {:d}, got {:d} in {:s}'.format( item['lua_name'], len(item['parameters']), params_in_data, filename)) item.update(data[item['lua_name']]) if 'parameters' in item and len(item['parameters']) != 0: for i in range(len(item['parameters'])): if 'name' not in item['parameters'][i]: raise RuntimeError('name missing for parameter {:d} of {:s} in {:s}'.format(1 + i, item['lua_name'], filename)) new_items.append(item) return new_items def _read_file(filename: str) -> str: with codecs.open(filename, 'r', 'utf-8') as f: return f.read() def _write_file(filename: str, contents: str) -> None: os.makedirs(os.path.dirname(filename), mode=0o755, exist_ok=True) with codecs.open(filename, 'w', 'utf-8') as f: f.write('[//]: # (This file is generated by luadoc.py, DO NOT EDIT)' + os.linesep + os.linesep) f.write(contents) def _copy_file(src: str, dst: str) -> None: os.makedirs(dst, mode=0o755, exist_ok=True) shutil.copyfile(src, posixpath.join(dst, os.path.basename(src))) def _find_globals(project_root: str) -> dict: globals = [] sandbox_cpp = LuaDoc._read_file(posixpath.join(project_root, 'server', 'src', 'lua', 'sandbox.cpp')) # lua base lib: for args in re.findall(r'addBaseLib\(\s*L\s*,[^{]*{(.+?)}\);', sandbox_cpp, flags=re.DOTALL): for name in re.findall(r'"([a-z0-9_]+)"', args): globals.append(name) # lua libs: globals += re.findall(r'addLib\(\s*L\s*,\s*LUA_[A-Z]+\s*,\s*luaopen_([a-z]+)\s*,', sandbox_cpp) # setfield: for name in re.findall(r'lua_setfield\(\s*L\s*,\s*-2\s*,\s*"([A-Za-z][A-Za-z_]*)"\s*\);', sandbox_cpp): globals.append(name) globals = LuaDoc._load_data(globals, posixpath.join(os.path.dirname(__file__), 'luadoc', 'globals.json')) return globals def _find_enums(project_root: str) -> list: enums_hpp = LuaDoc._read_file(posixpath.join(project_root, 'server', 'src', 'lua', 'enums.hpp')) m = re.findall(r'#define LUA_ENUMS([ \nA-Za-z0-9_,\\]+)\n\n', enums_hpp) m = re.sub(r'[ \\\n]+', '', m[0]) enums = [] for cpp_name in m.split(','): filename = posixpath.join(project_root, 'shared', 'src', 'traintastic', 'enum', cpp_name.lower() + '.hpp') hpp = LuaDoc._read_file(filename) enum = re.search(r'enum class ' + cpp_name + r'[ ]*(:[ ]*[A-Za-z0-9_]+|)[ \n]*{(.+?)};', hpp, re.DOTALL) items = [{'cpp_name': m[0], 'description': m[3], 'type': 'constant'} for m in re.findall(r'^[ ]*([A-Za-z0-9_]+)[ ]*=[ ]*.+?[ ]*(,|)[ ]*(//!< (.+)|)\n', enum.group(2), flags=re.MULTILINE)] if enum is not None else None info = re.search(r'TRAINTASTIC_ENUM\([ ]*' + cpp_name + r'[ \n]*,[ \n]*"([a-z0-9_]+)"[ \n]*,[ \n]*\d+[ \n]*,[ \n]*{(.+?)}[ \n]*\);', hpp, re.DOTALL) if info is None: raise RuntimeError('Reading enum info failed for {:s}'.format(filename)) for item in items: m = re.search(cpp_name + '::' + item['cpp_name'] + r'[ ]*,[ ]*"([A-Za-z0-9_]+)"', info.group(2)) if m is not None: item['lua_name'] = m.group(1).upper() items = filter(lambda item: 'lua_name' in item, items) enums.append({ 'filename': posixpath.join('enum', info.group(1).lower() + '.md'), 'name': 'enum.' + info.group(1).lower() + ':title', 'cpp_name': cpp_name, 'lua_name': info.group(1), 'items': items, 'see_also': [] }) return enums def _find_sets(project_root: str) -> list: sets_hpp = LuaDoc._read_file(posixpath.join(project_root, 'server', 'src', 'lua', 'sets.hpp')) m = re.findall(r'#define LUA_SETS([ \nA-Za-z0-9_,\\]+)\n\n', sets_hpp) m = re.sub(r'[ \\\n]+', '', m[0]) sets = [] for cpp_name in m.split(','): filename = posixpath.join(project_root, 'shared', 'src', 'traintastic', 'set', cpp_name.lower() + '.hpp') hpp = LuaDoc._read_file(filename) set = re.search(r'enum class ' + cpp_name + r'[ ]*(:[ ]*[A-Za-z0-9_]+|)[ \n]*{(.+?)};', hpp, re.DOTALL) items = [{'cpp_name': m[0], 'description': m[3], 'type': 'constant'} for m in re.findall(r'\b([A-Za-z0-9_]+)[ ]*=[ ]*.+?[ ]*(,|)[ ]*(//!< (.+)|)\n', set.group(2))] if set is not None else None info = re.search(r'TRAINTASTIC_SET\([ ]*' + cpp_name + r'[ \n]*,[ \n]*"([a-z0-9_]+)"[ \n]*,[ \n]*\d[ \n]*,[ \n]*(\([^,]+?\))[ \n]*,[ \n]*{(.+?)}[ \n]*\);', hpp, re.DOTALL) if info is None: raise RuntimeError('Reading set info failed for {:s}'.format(filename)) for item in items: m = re.search(cpp_name + '::' + item['cpp_name'] + r'[ ]*,[ ]*"([A-Za-z0-9_]+)"', info.group(3)) if m is not None: item['lua_name'] = m.group(1).upper() sets.append({ 'filename': posixpath.join('set', info.group(1).lower() + '.md'), 'name': 'set.' + info.group(1).lower() + ':title', 'cpp_name': cpp_name, 'lua_name': info.group(1), 'items': items, 'see_also': [] }) return sets def _find_libs(project_root: str) -> dict: libs = {} # lua libs: sandbox_cpp = LuaDoc._read_file(posixpath.join(project_root, 'server', 'src', 'lua', 'sandbox.cpp')) for name, args in re.findall(r'addLib\(\s*L\s*,\s*LUA_[A-Z]+\s*,\s*luaopen_([a-z]+)\s*,\s*{(.+?)}\);', sandbox_cpp, flags=re.DOTALL): items = [] for item_name in re.findall(r'"([a-z0-9_]+)"', args): items.append(item_name) libs[name] = { 'filename': name + '.md', 'name': name + ':title', 'lua_name': name, 'items': items} # log lib: name = 'log' items = [] log_hpp = LuaDoc._read_file(posixpath.join(project_root, 'server', 'src', 'lua', 'log.hpp')) for item_name in re.findall(r'static\s+int\s+([a-z]+)\(\s*lua_State\s*\*\s*L\s*\)', log_hpp): items.append(item_name) libs[name] = { 'filename': name + '.md', 'name': name + ':title', 'lua_name': name, 'items': items} # class lib: name = 'class' items = [] class_hpp = LuaDoc._read_file(posixpath.join(project_root, 'server', 'src', 'lua', 'class.hpp')) for item_name in re.findall(r'static\s+int\s+([a-zA-Z]+)\(\s*lua_State\s*\*\s*L\s*\)', class_hpp): if item_name == 'getClass': item_name = 'get' items.append(item_name) class_cpp = LuaDoc._read_file(posixpath.join(project_root, 'server', 'src', 'lua', 'class.cpp')) for item_name in re.findall(r'registerValue<[a-zA-Z0-9]+>\(\s*L\s*,\s*"([A-Z_]+)"\s*\);', class_cpp): if item_name == 'getClass': item_name = 'get' items.append(item_name) libs[name] = { 'filename': name + '.md', 'name': name + ':title', 'lua_name': name, 'items': items} # load meta data: for name, lib in libs.items(): filename = posixpath.join(os.path.dirname(__file__), 'luadoc', name + '.json') lib_json = json.loads(LuaDoc._read_file(filename)) items = [] for item_name in lib['items']: if item_name not in lib_json: raise RuntimeError('"{:s}" missing in {:s}'.format(item_name, filename)) item = lib_json[item_name] if item_name in lib_json else {} item['lua_name'] = item_name items.append(item) lib['items'] = items return libs def _find_objects(project_root: str) -> dict: objects = [] # index all cpp classes: cpp_classes = {} for root, dirs, files in os.walk(posixpath.join(project_root, 'server', 'src')): for file in files: if not file.endswith('.hpp'): continue filename_hpp = posixpath.join(root, file) hpp = LuaDoc._read_file(filename_hpp) m = re.search(r'class\s*([A-Za-z0-9]+)\s*(final|)\s*(:[^;]+?|){', hpp, flags=re.DOTALL) if m is None: continue base_classes = [] for base_class, _ in re.findall(r'public\s*([A-Za-z0-9]+)(<[A-Za-z0-9]+>|)', m.group(3)): if not base_class.startswith('std'): base_classes.append(base_class) lua_filename_hpp = posixpath.join(project_root, 'server', 'src', 'lua', 'object', os.path.basename(filename_hpp)) lua_filename_cpp = os.path.splitext(lua_filename_hpp)[0] + '.cpp' cpp_classes[m.group(1)] = { 'filename_hpp': filename_hpp, 'lua_filename_hpp': lua_filename_hpp if os.path.exists(lua_filename_hpp) else None, 'lua_filename_cpp': lua_filename_cpp if os.path.exists(lua_filename_cpp) else None, 'base_classes': base_classes} # indentify those that can be used in Lua: class_cpp = LuaDoc._read_file(posixpath.join(project_root, 'server', 'src', 'lua', 'class.cpp')) for cpp_name in re.findall(r'registerValue<([a-zA-Z0-9]+)>\(\s*L\s*,\s*"[A-Z0-9_]+"\s*\);', class_cpp): if cpp_name not in cpp_classes: raise RuntimeError('class {:s} not found'.format(cpp_name)) lua_name = 'object.' + cpp_name.lower() items = LuaDoc._find_object_items(cpp_classes, cpp_name) category = LuaDoc._get_object_category(cpp_classes, cpp_name) objects.append({ 'filename': posixpath.join('object', cpp_name.lower() + '.md'), 'lua_name': lua_name, 'name': lua_name + ':title', 'cpp_name': cpp_name, 'term_prefix': lua_name + '.', 'category': category, 'items': items }) return objects def _find_object_items(cpp_classes: dict, cpp_name: str) -> list: items = [] cpp_class = cpp_classes[cpp_name] term_prefix = 'object.' + cpp_name.lower() + '.' filename_hpp = cpp_class['filename_hpp'] filename_cpp = os.path.splitext(filename_hpp)[0] + '.cpp' hpp = LuaDoc._read_file(filename_hpp) cpp = LuaDoc._read_file(filename_cpp) if os.path.exists(filename_cpp) else hpp for cpp_type, cpp_template_type, cpp_item_name in re.findall(r'(Property|VectorProperty|ObjectProperty|ObjectVectorProperty|Method|Event)<(.*?)>\s+([A-Za-z0-9_]+);', hpp): m = re.search(cpp_item_name + r'({|\()\s*[\*]?this\s*,\s*"([a-z0-9_]+)".*?(PropertyFlags::ScriptReadOnly|PropertyFlags::ScriptReadWrite|MethodFlags::ScriptCallable|EventFlags::Scriptable)[^}]*}', cpp) if m is None: continue item = { 'cpp_name': cpp_item_name, 'cpp_type': cpp_type, 'cpp_template_type': cpp_template_type, 'lua_name': m.group(2), 'term_prefix': term_prefix } if cpp_type in ['Property', 'VectorProperty', 'ObjectProperty', 'ObjectVectorProperty']: item['type'] = 'property' elif cpp_type == 'Method': item['type'] = 'method' args = re.search(r'\((.*?)\)', cpp_template_type).group(1) item['parameters'] = [] if args == '' else [{}] * len(args.split(',')) item['return_values'] = 0 if cpp_template_type.startswith('void') else 1 elif cpp_type == 'Event': item['type'] = 'event' item['parameters'] = [{}] * (0 if cpp_template_type == '' else len(cpp_template_type.split(','))) items.append(item) # check for Lua wrapper: if cpp_class['lua_filename_hpp'] is not None: hpp = LuaDoc._read_file(cpp_class['lua_filename_hpp']) for method_name in re.findall(r'static\s+int\s+([a-z][a-z0-9_]*)\(\s*lua_State\s*\*\s*L\s*\)', hpp): item = { 'lua_name': method_name, 'term_prefix': term_prefix, 'type': 'method' } items.append(item) if cpp_class['lua_filename_cpp'] is not None: cpp = LuaDoc._read_file(cpp_class['lua_filename_cpp']) for property_name in re.findall(r'LUA_OBJECT_PROPERTY\(([a-z][a-z0-9_]*)\)', cpp): item = { 'lua_name': property_name, 'term_prefix': term_prefix, 'type': 'property' } items.append(item) item = LuaDoc._load_data(items, posixpath.join(posixpath.join(os.path.dirname(__file__), 'luadoc', 'object', cpp_name.lower() + '.json'))) # get special items that aren't detected: items += LuaDoc._get_special_object_items(cpp_name, term_prefix) for cpp_base_class in cpp_class['base_classes']: # todo cache this items += LuaDoc._find_object_items(cpp_classes, cpp_base_class) return items def _get_object_category(cpp_classes, cpp_name): for cpp_class in [cpp_name] + cpp_classes[cpp_name]['base_classes']: if cpp_class.startswith('Board') or cpp_class.endswith('Tile'): category = ['boards_tiles'] if cpp_class.startswith('Turnout'): category += ['turnouts'] elif cpp_class.startswith('Signal'): category += ['signals'] elif cpp_class.startswith(('Block', 'Sensor', 'NXButton')): category += ['control'] elif cpp_class.startswith(('Straight', 'Tunnel', 'Curve', 'Bridge', 'Cross', 'Buffer')): category += ['standard'] elif cpp_class in ['PushButtonTile', 'SwitchTile', 'LabelTile']: category += ['miscellaneous'] return category if cpp_class.startswith('Decoder'): return ['hardware', 'decoders'] if cpp_class == 'Interface' or cpp_class == 'InterfaceStatus': return ['hardware', 'interfaces'] if cpp_class.startswith('Input'): return ['hardware', 'inputs'] if cpp_class.startswith('Output') or cpp_class.endswith('Output'): return ['hardware', 'outputs'] if cpp_class.startswith('Identification'): return ['hardware', 'identification'] if cpp_class in ['Clock', 'World']: return ['world'] if cpp_class in ['Throttle', 'Train', 'TrainList', 'RailVehicleList'] or cpp_class.endswith('RailVehicle'): return ['trains_vehicles'] return [] def _get_or_create_category(categories, name): for category in categories: if category['id'] == name: return category category = {'id': name, 'name': 'object.category.' + name + ':title', 'items': [], 'categories': []} categories.append(category) return category def _build_category_tree(objects: list): categories = [] other = [] for object in objects: item = {'id': object['cpp_name'].lower(), 'href': object['filename'], 'title': object['name']} if not object['category']: other.append(item) continue current_level = categories for i, category_name in enumerate(object['category']): category = LuaDoc._get_or_create_category(current_level, category_name) if i == len(object['category']) - 1: category["items"].append(item) current_level = category["categories"] return categories, other def _find_examples(project_root: str) -> dict: examples = {} for root, dirs, files in os.walk(posixpath.join(project_root, 'manual', 'luadoc', 'example')): for file in files: if not file.endswith('.lua'): continue id = os.path.splitext(file)[0] examples[id] = { 'id': id, 'name': 'example.' + id + ':title', 'filename': posixpath.join('example', id + '.md'), 'code': LuaDoc._read_file(posixpath.join(root,file))} return examples def _get_special_object_items(cpp_name: str, term_prefix: str) -> list: items = [] if cpp_name == 'ObjectList': items.append({ 'lua_name': '__get', 'type': 'method', 'term_prefix': term_prefix, 'parameters': [{'name': 'index'}], 'return_values': 1, }) return items def _patch(self) -> None: """ Patch stuff that is hard to determine with a bit of regex magic """ for object in self._objects: # remove get_output output_channels: if object['cpp_name'] in ['HSI88Interface']: object['items'] = [item for item in object['items'] if item['lua_name'] not in ['get_output', 'output_channels']] # make get_input() channel arg non optional: if object['cpp_name'] in ['ECoSInterface', 'HSI88Interface', 'Z21Interface']: for item in object['items']: if item['lua_name'] == 'get_input': item['parameters'][0]['optional'] = False def build(self, output_dir: str) -> None: # reset missing terms self.missing_terms = [] self._build_globals(output_dir) self._build_pv(output_dir) self._build_enums(output_dir) self._build_sets(output_dir) for _, lib in self._libs.items(): self._build_lib(output_dir, lib) self._build_objects(output_dir) self._build_examples(output_dir) self._build_index_az(output_dir) def _build_items_md(self, items: list, term_prefix: str, lua_prefix: str = '') -> str: md = '' items = sorted(items, key=operator.itemgetter('lua_name')) constants = [item for item in items if item['type'] == 'constant'] if len(constants) > 0: md += '## ' + self._get_term('constants') + os.linesep + os.linesep for item in constants: item_term_prefix = item['term_prefix'] if 'term_prefix' in item else term_prefix md += '### `' + lua_prefix + item['lua_name'] + '`' + os.linesep md += self._get_term(item_term_prefix + item['lua_name'].lower() + ':description') + os.linesep + os.linesep libraries = [item for item in items if item['type'] == 'library'] if len(libraries) > 0: md += '## ' + self._get_term('libraries') + os.linesep + os.linesep for item in libraries: md += '### `' + lua_prefix + item['lua_name'] + '`' + os.linesep if item['lua_name'] == 'enum': href = LuaDoc.FILENAME_ENUM elif item['lua_name'] == 'set': href = LuaDoc.FILENAME_SET else: href = self._libs[item['lua_name']]['filename'] md += '[' + self._get_term(item['lua_name'] + ':title') + '](' + href + ')' + os.linesep + os.linesep objects = [item for item in items if item['type'] == 'object'] if len(objects) > 0: md += '## ' + self._get_term('objects') + os.linesep + os.linesep for item in objects: item_term_prefix = item['term_prefix'] if 'term_prefix' in item else term_prefix md += '### `' + lua_prefix + item['lua_name'] + '`' + os.linesep md += self._get_term(item_term_prefix + item['lua_name'].lower() + ':description') + os.linesep + os.linesep properties = [item for item in items if item['type'] == 'property'] if len(properties) > 0: md += '## ' + self._get_term('properties') + os.linesep + os.linesep for item in properties: item_term_prefix = item['term_prefix'] if 'term_prefix' in item else term_prefix md += '### `' + lua_prefix + item['lua_name'] + '`' + os.linesep md += ' ' + self._get_term(item_term_prefix + item['lua_name'].lower() + ':description') + os.linesep + os.linesep for function_or_method in ['function', 'method']: functions = [item for item in items if item['type'] == function_or_method] if len(functions) > 0: md += '## ' + self._get_term(function_or_method + 's') + os.linesep + os.linesep for item in functions: item_term_prefix = item['term_prefix'] if 'term_prefix' in item else term_prefix md += '### `' + lua_prefix if item['lua_name'] == '__get': md += '[' else: md += item['lua_name'] + '(' optional = 0 for p in item['parameters']: is_optional = 'optional' in p and p['optional'] is_first = p == item['parameters'][0] if is_optional: md += '[' if is_first else ' [' optional += 1 if not is_first: md += ',' if not is_optional and optional > 0: md += ']' * optional optional = 0 if not is_first: md += ' ' md += p['name'] if is_optional and 'default' in p: md += ' = ' + str(p['default']) md += ']' * optional if item['lua_name'] == '__get': md += ']' else: md += ')' md += '` {#' + item['lua_name'] + '}' + os.linesep + os.linesep md += self._get_term(item_term_prefix + item['lua_name'].lower() + ':description') + os.linesep + os.linesep for qualifier in ['warning', 'note', 'tip']: description = item_term_prefix + item['lua_name'].lower() + '.' + qualifier + ':description' if description in self._terms: md += '!!! ' + qualifier title = item_term_prefix + item['lua_name'].lower() + '.' + qualifier + ':title' if title in self._terms: md += ' "' + self._get_term(title) + '"' md += os.linesep + textwrap.indent(self._get_term(description), ' ' * 4) + os.linesep + os.linesep if len(item['parameters']) > 0: md += '**' + self._get_term('parameters') + ':**' + os.linesep + os.linesep for param in item['parameters']: md += '- `' + param['name'] + '` ' + os.linesep md += ' ' + self._get_term(item_term_prefix + item['lua_name'] + '.parameter.' + param['name'] + ':description') + os.linesep + os.linesep if item['return_values'] > 0: md += '**' + self._get_term('return_values') + '** ' + os.linesep md += self._get_term(item_term_prefix + item['lua_name'] + ':return_values') + os.linesep + os.linesep if 'examples' in item and len(item['examples']) != 0: md += '
v' + self._version + os.linesep md += '
' + datetime.datetime.now().strftime('%Y-%m-%d %H:%M:%S') + os.linesep md += self._get_term('index:description') + os.linesep + os.linesep md += '
enum.' + enum['lua_name'] + '' + set['lua_name'] + '' + item['lua_name'] + '', 'sub_title': 'globals', 'href': LuaDoc.FILENAME_GLOBALS + '#' + item['lua_name']})
# libs:
for _, lib in self._libs.items():
index[lib['lua_name'][0].upper()].append({'title': '' + lib['lua_name'] + '', 'sub_title': self._get_term(lib['name']), 'href': lib['filename']})
for item in lib['items']:
index[item['lua_name'][0].upper()].append({'title': '' + item['lua_name'] + '', 'sub_title': '' + lib['lua_name'] + '', 'href': lib['filename'] + '#' + item['lua_name']})
# enums:
index['E'].append({'title': 'enum', 'sub_title': self._get_term('enum:title'), 'href': LuaDoc.FILENAME_ENUM})
for enum in self._enums:
for item in enum['items']:
letter = item['lua_name'][0].upper()
index[letter].append({'title': '' + item['lua_name'] + '', 'sub_title': 'enum.' + enum['lua_name'] + '', 'href': enum['filename'] + '#' + item['lua_name']})
# sets:
index['S'].append({'title': 'set', 'sub_title': self._get_term('set:title'), 'href': LuaDoc.FILENAME_SET})
for set in self._sets:
for item in set['items']:
letter = item['lua_name'][0].upper()
index[letter].append({'title': '' + item['lua_name'] + '', 'sub_title': 'set.' + set['lua_name'] + '', 'href': set['filename'] + '#' + item['lua_name']})
# objects:
for object in self._objects:
name = self._get_term(object['name'])
letter = name[0].upper()
if letter in index:
index[letter].append({'title': name, 'sub_title': 'object', 'href': object['filename']})
md = '# ' + self._get_term('index-az:title') + os.linesep + os.linesep
md += self._get_term('index-az:description') + os.linesep + os.linesep
md += '