summaryrefslogtreecommitdiff
path: root/doc/tools
diff options
context:
space:
mode:
Diffstat (limited to 'doc/tools')
-rw-r--r--doc/tools/doc_status.py85
-rw-r--r--doc/tools/makerst.py95
2 files changed, 112 insertions, 68 deletions
diff --git a/doc/tools/doc_status.py b/doc/tools/doc_status.py
index 6b6b794f11..170ded9f50 100644
--- a/doc/tools/doc_status.py
+++ b/doc/tools/doc_status.py
@@ -1,5 +1,6 @@
-#!/usr/bin/env python3
+#!/usr/bin/env python
+import fnmatch
import os
import sys
import re
@@ -22,6 +23,7 @@ flags = {
'o': True,
'i': False,
'a': True,
+ 'e': False,
}
flag_descriptions = {
'c': 'Toggle colors when outputting.',
@@ -34,6 +36,7 @@ flag_descriptions = {
'o': 'Toggle overall column.',
'i': 'Toggle collapse of class items columns.',
'a': 'Toggle showing all items.',
+ 'e': 'Toggle hiding empty items.',
}
long_flags = {
'colors': 'c',
@@ -63,6 +66,8 @@ long_flags = {
'collapse': 'i',
'all': 'a',
+
+ 'empty': 'e',
}
table_columns = ['name', 'brief_description', 'description', 'methods', 'constants', 'members', 'signals']
table_column_names = ['Name', 'Brief Desc.', 'Desc.', 'Methods', 'Constants', 'Members', 'Signals']
@@ -91,7 +96,7 @@ def validate_tag(elem, tag):
def color(color, string):
- if flags['c']:
+ if flags['c'] and terminal_supports_color():
color_format = ''
for code in colors[color]:
color_format += '\033[' + str(code) + 'm'
@@ -105,6 +110,15 @@ ansi_escape = re.compile(r'\x1b[^m]*m')
def nonescape_len(s):
return len(ansi_escape.sub('', s))
+def terminal_supports_color():
+ p = sys.platform
+ supported_platform = p != 'Pocket PC' and (p != 'win32' or
+ 'ANSICON' in os.environ)
+
+ is_a_tty = hasattr(sys.stdout, 'isatty') and sys.stdout.isatty()
+ if not supported_platform or not is_a_tty:
+ return False
+ return True
################################################################################
# Classes #
@@ -134,8 +148,8 @@ class ClassStatusProgress:
return self.to_colored_string()
def to_colored_string(self, format='{has}/{total}', pad_format='{pad_described}{s}{pad_total}'):
- ratio = self.described / self.total if self.total != 0 else 1
- percent = round(100 * ratio)
+ ratio = float(self.described) / float(self.total) if self.total != 0 else 1
+ percent = int(round(100 * ratio))
s = format.format(has=str(self.described), total=str(self.total), percent=str(percent))
if self.described >= self.total:
s = color('part_good', s)
@@ -182,6 +196,14 @@ class ClassStatus:
ok = ok and self.progresses[k].is_ok()
return ok
+ def is_empty(self):
+ sum = 0
+ for k in self.progresses:
+ if self.progresses[k].is_ok():
+ continue
+ sum += self.progresses[k].total
+ return sum < 1
+
def make_output(self):
output = {}
output['name'] = color('name', self.name)
@@ -218,6 +240,7 @@ class ClassStatus:
return output
+ @staticmethod
def generate_for_class(c):
status = ClassStatus()
status.name = c.attrib['name']
@@ -227,15 +250,16 @@ class ClassStatus:
for tag in list(c):
if tag.tag in ['methods']:
for sub_tag in list(tag):
- methods.append(sub_tag.find('name'))
+ methods.append(sub_tag.attrib['name'])
if tag.tag in ['members']:
for sub_tag in list(tag):
try:
- methods.remove(sub_tag.find('setter'))
- methods.remove(sub_tag.find('getter'))
+ if(sub_tag.attrib['setter'].startswith('_') == False):
+ methods.remove(sub_tag.attrib['setter'])
+ if(sub_tag.attrib['getter'].startswith('_') == False):
+ methods.remove(sub_tag.attrib['getter'])
except:
pass
-
for tag in list(c):
if tag.tag == 'brief_description':
@@ -246,7 +270,7 @@ class ClassStatus:
elif tag.tag in ['methods', 'signals']:
for sub_tag in list(tag):
- if sub_tag.find('name') in methods or tag.tag == 'signals':
+ if sub_tag.attrib['name'] in methods or tag.tag == 'signals':
descr = sub_tag.find('description')
status.progresses[tag.tag].increment(len(descr.text.strip()) > 0)
elif tag.tag in ['constants', 'members']:
@@ -274,17 +298,21 @@ input_class_list = []
merged_file = ""
for arg in sys.argv[1:]:
- if arg.startswith('--'):
- flags[long_flags[arg[2:]]] = not flags[long_flags[arg[2:]]]
- elif arg.startswith('-'):
- for f in arg[1:]:
- flags[f] = not flags[f]
- elif os.path.isdir(arg):
- for f in os.listdir(arg):
- if f.endswith('.xml'):
- input_file_list.append(os.path.join(arg, f));
- else:
- input_class_list.append(arg)
+ try:
+ if arg.startswith('--'):
+ flags[long_flags[arg[2:]]] = not flags[long_flags[arg[2:]]]
+ elif arg.startswith('-'):
+ for f in arg[1:]:
+ flags[f] = not flags[f]
+ elif os.path.isdir(arg):
+ for f in os.listdir(arg):
+ if f.endswith('.xml'):
+ input_file_list.append(os.path.join(arg, f));
+ else:
+ input_class_list.append(arg)
+ except KeyError:
+ print("Unknown command line flag: " + arg)
+ sys.exit(1)
if flags['i']:
for r in ['methods', 'constants', 'members', 'signals']:
@@ -356,8 +384,13 @@ for file in input_file_list:
class_names.sort()
if len(input_class_list) < 1:
- input_class_list = class_names
+ input_class_list = ['*']
+filtered_classes = set()
+for pattern in input_class_list:
+ filtered_classes |= set(fnmatch.filter(class_names, pattern))
+filtered_classes = list(filtered_classes)
+filtered_classes.sort()
################################################################################
# Make output table #
@@ -369,10 +402,7 @@ table_column_chars = '|'
total_status = ClassStatus('Total')
-for cn in input_class_list:
- if not cn in classes:
- print('Cannot find class ' + cn + '!')
- sys.exit(255)
+for cn in filtered_classes:
c = classes[cn]
validate_tag(c, 'class')
@@ -383,6 +413,9 @@ for cn in input_class_list:
if (flags['b'] and status.is_ok()) or (flags['g'] and not status.is_ok()) or (not flags['a']):
continue
+ if flags['e'] and status.is_empty():
+ continue
+
out = status.make_output()
row = []
for column in table_columns:
@@ -436,7 +469,7 @@ for row_i, row in enumerate(table):
if cell_i == 0:
row_string += table_row_chars[3] + cell + table_row_chars[3] * (padding_needed - 1)
else:
- row_string += table_row_chars[3] * math.floor(padding_needed / 2) + cell + table_row_chars[3] * math.ceil((padding_needed / 2))
+ row_string += table_row_chars[3] * int(math.floor(float(padding_needed) / 2)) + cell + table_row_chars[3] * int(math.ceil(float(padding_needed) / 2))
row_string += table_column_chars
print(row_string)
diff --git a/doc/tools/makerst.py b/doc/tools/makerst.py
index 696e3c9c78..dc015d781b 100644
--- a/doc/tools/makerst.py
+++ b/doc/tools/makerst.py
@@ -3,21 +3,25 @@
import codecs
import sys
+import os
import xml.etree.ElementTree as ET
input_list = []
for arg in sys.argv[1:]:
+ if arg.endswith(os.sep):
+ arg = arg[:-1]
input_list.append(arg)
if len(input_list) < 1:
- print 'usage: makerst.py <classes.xml>'
+ print('usage: makerst.py <path to folders> and/or <path to .xml files> (order of arguments irrelevant)')
+ print('example: makerst.py "../../modules/" "../classes" path_to/some_class.xml')
sys.exit(0)
def validate_tag(elem, tag):
if elem.tag != tag:
- print "Tag mismatch, expected '" + tag + "', got " + elem.tag
+ print("Tag mismatch, expected '" + tag + "', got " + elem.tag)
sys.exit(255)
@@ -34,11 +38,10 @@ def ul_string(str, ul):
def make_class_list(class_list, columns):
-
f = codecs.open('class_list.rst', 'wb', 'utf-8')
prev = 0
col_max = len(class_list) / columns + 1
- print ('col max is ', col_max)
+ print(('col max is ', col_max))
col_count = 0
row_count = 0
last_initial = ''
@@ -102,7 +105,6 @@ def make_class_list(class_list, columns):
def rstize_text(text, cclass):
-
# Linebreak + tabs in the XML should become two line breaks unless in a "codeblock"
pos = 0
while True:
@@ -187,8 +189,11 @@ def rstize_text(text, cclass):
post_text = text[endq_pos + 1:]
tag_text = text[pos + 1:endq_pos]
+ escape_post = False
+
if tag_text in class_names:
tag_text = make_type(tag_text)
+ escape_post = True
else: # command
cmd = tag_text
space_pos = tag_text.find(' ')
@@ -207,7 +212,7 @@ def rstize_text(text, cclass):
cmd = tag_text[:space_pos]
param = tag_text[space_pos + 1:]
tag_text = param
- elif cmd.find('method') == 0:
+ elif cmd.find('method') == 0 or cmd.find('member') == 0 or cmd.find('signal') == 0:
cmd = tag_text[:space_pos]
param = tag_text[space_pos + 1:]
@@ -216,12 +221,14 @@ def rstize_text(text, cclass):
tag_text = ':ref:`' + class_param + '.' + method_param + '<class_' + class_param + '_' + method_param + '>`'
else:
tag_text = ':ref:`' + param + '<class_' + cclass + "_" + param + '>`'
+ escape_post = True
elif cmd.find('image=') == 0:
tag_text = "" # '![](' + cmd[6:] + ')'
elif cmd.find('url=') == 0:
tag_text = ':ref:`' + cmd[4:] + '<' + cmd[4:] + ">`"
elif cmd == '/url':
- tag_text = ')'
+ tag_text = ''
+ escape_post = True
elif cmd == 'center':
tag_text = ''
elif cmd == '/center':
@@ -246,13 +253,15 @@ def rstize_text(text, cclass):
inside_code = True
else:
tag_text = make_type(tag_text)
+ escape_post = True
+
+ # Properly escape things like `[Node]s`
+ if escape_post and post_text and post_text[0].isalnum(): # not punctuation, escape
+ post_text = '\ ' + post_text
text = pre_text + tag_text + post_text
pos = len(pre_text) + len(tag_text)
- # tnode = ET.SubElement(parent,"div")
- # tnode.text=text
-
return text
@@ -272,7 +281,6 @@ def make_method(
event=False,
pp=None
):
-
if (declare or pp == None):
t = '- '
else:
@@ -302,16 +310,11 @@ def make_method(
if declare or pp == None:
- # span.attrib["class"]="funcdecl"
- # a=ET.SubElement(span,"a")
- # a.attrib["name"]=name+"_"+m.attrib["name"]
- # a.text=name+"::"+m.attrib["name"]
-
- s = ' **' + m.attrib['name'] + '** '
+ s = '**' + m.attrib['name'] + '** '
else:
s = ':ref:`' + m.attrib['name'] + '<class_' + cname + "_" + m.attrib['name'] + '>` '
- s += ' **(**'
+ s += '**(**'
argfound = False
for a in mdata['argidx']:
arg = mdata[a]
@@ -331,16 +334,11 @@ def make_method(
if 'default' in arg.attrib:
s += '=' + arg.attrib['default']
- argfound = True
-
- if argfound:
- s += ' '
s += ' **)**'
if 'qualifiers' in m.attrib:
s += ' ' + m.attrib['qualifiers']
-# f.write(s)
if (not declare):
if (pp != None):
pp.append((t, s))
@@ -355,24 +353,23 @@ def make_heading(title, underline):
def make_rst_class(node):
-
name = node.attrib['name']
f = codecs.open("class_" + name.lower() + '.rst', 'wb', 'utf-8')
# Warn contributors not to edit this file directly
f.write(".. Generated automatically by doc/tools/makerst.py in Godot's source tree.\n")
- f.write(".. DO NOT EDIT THIS FILE, but the doc/base/classes.xml source instead.\n\n")
+ f.write(".. DO NOT EDIT THIS FILE, but the " + name + ".xml source instead.\n")
+ f.write(".. The source is found in doc/classes or modules/<name>/doc_classes.\n\n")
f.write(".. _class_" + name + ":\n\n")
f.write(make_heading(name, '='))
if 'inherits' in node.attrib:
inh = node.attrib['inherits'].strip()
-# whle inh in classes[cn]
f.write('**Inherits:** ')
first = True
- while(inh in classes):
+ while (inh in classes):
if (not first):
f.write(" **<** ")
else:
@@ -436,10 +433,10 @@ def make_rst_class(node):
f.write(sep)
for s in ml:
rt = s[0]
- while(len(rt) < longest_s):
+ while (len(rt) < longest_s):
rt += " "
st = s[1]
- while(len(st) < longest_t):
+ while (len(st) < longest_t):
st += " "
f.write("| " + rt + " | " + st + " |\n")
f.write(sep)
@@ -449,7 +446,9 @@ def make_rst_class(node):
if events != None and len(list(events)) > 0:
f.write(make_heading('Signals', '-'))
for m in list(events):
+ f.write(".. _class_" + name + "_" + m.attrib['name'] + ":\n\n")
make_method(f, node.attrib['name'], m, True, name, True)
+ f.write('\n')
d = m.find('description')
if d == None or d.text.strip() == '':
continue
@@ -463,12 +462,14 @@ def make_rst_class(node):
f.write(make_heading('Member Variables', '-'))
for c in list(members):
+ # Leading two spaces necessary to prevent breaking the <ul>
+ f.write(" .. _class_" + name + "_" + c.attrib['name'] + ":\n\n")
s = '- '
s += make_type(c.attrib['type']) + ' '
s += '**' + c.attrib['name'] + '**'
if c.text.strip() != '':
- s += ' - ' + c.text.strip()
- f.write(s + '\n')
+ s += ' - ' + rstize_text(c.text.strip(), name)
+ f.write(s + '\n\n')
f.write('\n')
constants = node.find('constants')
@@ -494,8 +495,6 @@ def make_rst_class(node):
f.write(make_heading('Member Function Description', '-'))
for m in list(methods):
f.write(".. _class_" + name + "_" + m.attrib['name'] + ":\n\n")
-# f.write(ul_string(m.attrib['name'],"^"))
- #f.write('\n<a name="'+m.attrib['name']+'">' + m.attrib['name'] + '</a>\n------\n')
make_method(f, node.attrib['name'], m, True, name)
f.write('\n')
d = m.find('description')
@@ -506,26 +505,38 @@ def make_rst_class(node):
f.write('\n')
-for file in input_list:
+file_list = []
+
+for path in input_list:
+ if os.path.basename(path) == 'modules':
+ for subdir, dirs, _ in os.walk(path):
+ if 'doc_classes' in dirs:
+ doc_dir = os.path.join(subdir, 'doc_classes')
+ class_file_names = [f for f in os.listdir(doc_dir) if f.endswith('.xml')]
+ file_list += [os.path.join(doc_dir, f) for f in class_file_names]
+ elif not os.path.isfile(path):
+ file_list += [os.path.join(path, f) for f in os.listdir(path) if f.endswith('.xml')]
+ elif os.path.isfile(path) and path.endswith('.xml'):
+ file_list.append(path)
+
+for file in file_list:
tree = ET.parse(file)
doc = tree.getroot()
if 'version' not in doc.attrib:
- print "Version missing from 'doc'"
+ print("Version missing from 'doc'")
sys.exit(255)
version = doc.attrib['version']
-
- for c in list(doc):
- if c.attrib['name'] in class_names:
- continue
- class_names.append(c.attrib['name'])
- classes[c.attrib['name']] = c
+ if doc.attrib['name'] in class_names:
+ continue
+ class_names.append(doc.attrib['name'])
+ classes[doc.attrib['name']] = doc
class_names.sort()
# Don't make class list for Sphinx, :toctree: handles it
-#make_class_list(class_names, 2)
+# make_class_list(class_names, 2)
for cn in class_names:
c = classes[cn]