diff --git a/docs/docs_writer.py b/docs/docs_writer.py
new file mode 100644
index 00000000..4e35718e
--- /dev/null
+++ b/docs/docs_writer.py
@@ -0,0 +1,224 @@
+import os
+
+
+class DocsWriter:
+ """Utility class used to write the HTML files used on the documentation"""
+ def __init__(self, filename, type_to_path_function):
+ """Initializes the writer to the specified output file,
+ creating the parent directories when used if required.
+
+ 'type_to_path_function' should be a function which, given a type name
+ and a named argument relative_to, returns the file path for the specified
+ type, relative to the given filename"""
+ self.filename = filename
+ self.handle = None
+
+ # Should be set before calling adding items to the menu
+ self.menu_separator_tag = None
+
+ # Utility functions TODO There must be a better way
+ self.type_to_path = lambda t: type_to_path_function(t, relative_to=self.filename)
+
+ # Control signals
+ self.menu_began = False
+ self.table_columns = 0
+ self.table_columns_left = None
+
+ # High level writing
+ def write_head(self, title, relative_css_path):
+ """Writes the head part for the generated document, with the given title and CSS"""
+ self.write('''
+
+
+ ''')
+
+ def set_menu_separator(self, relative_image_path):
+ """Sets the menu separator. Must be called before adding entries to the menu"""
+ if relative_image_path:
+ self.menu_separator_tag = '

' % relative_image_path
+ else:
+ self.menu_separator_tag = None
+
+ def add_menu(self, name, link=None):
+ """Adds a menu entry, will create it if it doesn't exist yet"""
+ if self.menu_began:
+ if self.menu_separator_tag:
+ self.write(self.menu_separator_tag)
+ else:
+ # First time, create the menu tag
+ self.write('
')
+
+ def write_title(self, title, level=1):
+ """Writes a title header in the document body, with an optional depth level"""
+ self.write('
' % level)
+ self.write(title)
+ self.write('' % level)
+
+ def write_code(self, tlobject):
+ """Writes the code for the given 'tlobject' properly formatted ith with hyperlinks"""
+ self.write('
---')
+ self.write('functions' if tlobject.is_function else 'types')
+ self.write('---\n')
+
+ # Write the function or type and its ID
+ if tlobject.namespace:
+ self.write(tlobject.namespace)
+ self.write('.')
+
+ self.write(tlobject.name)
+ self.write('#')
+ self.write(hex(tlobject.id)[2:].rjust(8, '0'))
+
+ # Write all the arguments (or do nothing if there's none)
+ for arg in tlobject.args:
+ self.write(' ')
+
+ # "Opening" modifiers
+ if arg.generic_definition:
+ self.write('{')
+
+ # Argument name
+ self.write(arg.name)
+ self.write(':')
+
+ # "Opening" modifiers
+ if arg.is_flag:
+ self.write('flags.%d?' % arg.flag_index)
+
+ if arg.is_generic:
+ self.write('!')
+
+ if arg.is_vector:
+ self.write('Vector<' % self.type_to_path('vector'))
+
+ # Argument type
+ if arg.type:
+ self.write('%s' % arg.type)
+ else:
+ self.write('#')
+
+ # "Closing" modifiers
+ if arg.is_vector:
+ self.write('>')
+
+ if arg.generic_definition:
+ self.write('}')
+
+ # Now write the resulting type (result from a function, or type for a constructor)
+ self.write(' = %s' % tlobject.result)
+
+ self.write('
')
+
+ def begin_table(self, column_count):
+ """Begins a table with the given 'column_count', required to automatically
+ create the right amount of columns when adding items to the rows"""
+ self.table_columns = column_count
+ self.table_columns_left = 0
+ self.write('
')
+
+ def add_row(self, text, link=None, bold=False, align=None):
+ """This will create a new row, or add text to the next column
+ of the previously created, incomplete row, closing it if complete"""
+ if not self.table_columns_left:
+ # Starting a new row
+ self.write('')
+ self.table_columns_left = self.table_columns
+
+ self.write('')
+
+ if bold:
+ self.write('')
+ if link:
+ self.write('')
+
+ # Finally write the real table data, the given text
+ self.write(text)
+
+ if link:
+ self.write('')
+ if bold:
+ self.write('')
+
+ self.write(' | ')
+
+ self.table_columns_left -= 1
+ if not self.table_columns_left:
+ self.write('
')
+
+ def end_table(self):
+ # If there was any column left, finish it before closing the table
+ if self.table_columns_left:
+ self.write('')
+
+ self.write('
')
+
+ def write_text(self, text):
+ """Writes a paragraph of text"""
+ self.write('
')
+ self.write(text)
+ self.write('
')
+
+ def end_body(self):
+ """Ends the whole document. This should be called the last"""
+ self.write('
')
+
+ # "Low" level writing
+ def write(self, s):
+ """Wrapper around handle.write"""
+ self.handle.write(s)
+
+ # With block
+ def __enter__(self):
+ # Sanity check
+ os.makedirs(os.path.dirname(self.filename), exist_ok=True)
+ self.handle = open(self.filename, 'w', encoding='utf-8')
+ return self
+
+ def __exit__(self, exc_type, exc_val, exc_tb):
+ self.handle.close()