1
0
Fork 0
mirror of https://github.com/juce-framework/JUCE.git synced 2026-01-10 23:44:24 +00:00

Docs: Restore Doxygen macro descriptions

This commit is contained in:
Tom Poole 2025-09-23 22:26:43 +01:00
parent 84e180b82c
commit f226d2e038
3 changed files with 250 additions and 188 deletions

View file

@ -1,20 +1,14 @@
from pathlib import Path
import argparse
import os
import subprocess
from pathlib import Path, PurePath
import xml.etree.ElementTree as ET
from xml.etree.ElementTree import Element
import re
def get_modules_directory_xml_element(xml_dir: Path) -> Element:
for entry in os.listdir('xml'):
if not entry.startswith('dir_'):
continue
root = ET.parse(xml_dir / entry).getroot()
location = root.find('.//location')
path = PurePath(location.attrib['file'])
referenced_dir_path_parts = path.parts
if referenced_dir_path_parts[-1] == 'modules':
return root, path
MODULES_DIR = Path('../../modules')
assert MODULES_DIR.is_dir()
ABS_MODULES_DIR = MODULES_DIR.resolve()
def get_module_description(module_header: Path) -> str:
with open(module_header, 'r') as f:
@ -24,52 +18,107 @@ def get_module_description(module_header: Path) -> str:
if line.startswith('description:'):
return line.removeprefix('description:').strip()
def get_doxygen_items_recursive(xml_file_ref: str, items: list):
element = ET.parse(f'{Path('xml') / xml_file_ref}.xml').getroot()
item_type = element.find('compounddef').attrib['kind']
if item_type == 'dir':
name = element.find('.//compoundname').text
new_item = {
'name': name,
'classes': [],
'dirs': []
}
items.append(new_item)
items = new_item['classes']
for class_reference in element.findall('.//innerclass'):
html_filename = f'{class_reference.attrib['refid']}.html'
if (Path('doc') / html_filename).exists():
items.append({
'name': class_reference.text.removeprefix('juce::'),
'url': f'./{html_filename}',
})
for file_reference in element.findall('.//innerfile'):
get_doxygen_items_recursive(file_reference.attrib['refid'], items)
for dir_reference in element.findall('.//innerdir'):
get_doxygen_items_recursive(dir_reference.attrib['refid'], new_item['dirs'])
def createContentDict() -> dict:
return {
'classes/structs': {},
'functions': {},
'typedefs': {},
'macros': {},
'enums': {},
'dirs': {}
}
def remove_empty_doxygen_items_recursive(parent: dict):
parent['dirs'] = list(filter(lambda x: x['classes'] or x['dirs'], parent['dirs']))
for d in parent['dirs']:
remove_empty_doxygen_items_recursive(d)
def getContentObjectFromPath(root_object: dict, location: str) -> dict:
path_in_modules = Path(location).relative_to(ABS_MODULES_DIR)
path_components = path_in_modules.parts
contents = root_object[path_components[0]]
for path_component in path_components[1:-1]:
if not (path_component in contents['dirs']):
contents['dirs'][path_component] = createContentDict()
contents = contents['dirs'][path_component]
return contents
def parseNamespaceXml(root_object, filename):
element = ET.parse(f'xml/{filename}').getroot()
for class_reference in element.findall('.//innerclass'):
refid = class_reference.attrib['refid']
html_filename = f'{refid}.html'
if not Path(f'doc/{html_filename}').is_file():
continue
xml_path = Path(f'xml/{refid}.xml')
if not xml_path.is_file():
continue
class_element = ET.parse(xml_path).getroot()
location = class_element.find('.//location').attrib['file']
contents = getContentObjectFromPath(root_object, location)
contents['classes/structs'][class_reference.text.removeprefix('juce::')] = {
'url': f'./{html_filename}'
}
namespace_def_id = element.find('.//compounddef').attrib['id']
namespace_id_prefix = f'{namespace_def_id}_1'
for kind, contents_key in (('function', 'functions'), ('typedef', 'typedefs'), ('enum', 'enums')):
for reference in element.findall(f".//memberdef[@kind='{kind}']"):
desc = reference.find('briefdescription')
if (len(desc) == 0) and (not desc.text.strip()):
continue
ref_id = reference.attrib['id']
if not ref_id.startswith(namespace_id_prefix):
continue
name = reference.find('.//qualifiedname').text.removeprefix('juce::')
if re.search(r'operator[+\-*/=!<>]*$', name):
continue
anchor = ref_id.removeprefix(namespace_id_prefix)
location = reference.find('.//location').attrib['file']
contents = getContentObjectFromPath(modules, location)
contents[contents_key][name] = {
'url': f'./{namespace_def_id}.html#{anchor}'
}
def parseFileXml(root_object, filename):
element = ET.parse(f'xml/{filename}').getroot()
compounddef_id = element.find('compounddef').attrib['id']
def_reference_link_prefix = compounddef_id + '_1'
for def_reference in element.findall(".//memberdef[@kind='define']"):
def_id = def_reference.attrib['id']
if not def_id.startswith(def_reference_link_prefix):
continue
def_desc = def_reference.find('briefdescription')
if (len(def_desc) == 0) and (not def_desc.text.strip()):
continue
html_filename = f'{compounddef_id}.html'
if not (Path('doc') / html_filename).exists():
continue
location = def_reference.find('.//location').attrib['file']
contents = getContentObjectFromPath(root_object, location)
anchor = def_id.removeprefix(def_reference_link_prefix)
contents['macros'][def_reference.find('name').text] = {
'url': f'./{html_filename}#{anchor}',
}
def write_module_html_recursive(parent: Element, data: dict):
if data['classes']:
class_list = ET.SubElement(parent, 'ul', {'class': 'juce-module-class-list'})
for c in data['classes']:
item = ET.SubElement(class_list, 'li', {'class': 'juce-module-class-item'})
ET.SubElement(item, 'a', {'href': c['url'], 'class': 'juce-module-class-link'}).text = c['name']
for section_key, section_contents in data.items():
if section_key in ('dirs', 'description'):
continue
if not section_contents:
continue
#ET.SubElement(parent, 'span', {'class': 'juce-module-contents-title'}).text = section_key
section_list = ET.SubElement(parent, 'ul', {'class': 'juce-module-class-list'})
for section_item_key, section_item_contents in sorted(section_contents.items()):
item = ET.SubElement(section_list, 'li', {'class': 'juce-module-class-item'})
ET.SubElement(item, 'a', {'href': section_item_contents['url'], 'class': 'juce-module-class-link'}).text = section_item_key
if data['dirs']:
dir_table = ET.SubElement(parent, 'table', {'class': 'juce-module-dir-table'})
for d in data['dirs']:
for dir_key, dir_contents in sorted(data['dirs'].items()):
row = ET.SubElement(dir_table, 'tr', {'class': 'juce-module-dir-row'})
ET.SubElement(row, 'td', {'class': 'juce-module-dir-name'}).text = d['name']
ET.SubElement(row, 'td', {'class': 'juce-module-dir-name'}).text = dir_key
content = ET.SubElement(row, 'td', {'class': 'juce-module-dir-contents'})
write_module_html_recursive(content, d)
write_module_html_recursive(content, dir_contents)
parser = argparse.ArgumentParser()
parser.add_argument('--sitemap-url', help='a sitemap URL for Doxygen')
parser.add_argument('--sitemap-url', help='The Doxygen sitemap configuration variable')
args = parser.parse_args()
if args.sitemap_url:
@ -78,18 +127,25 @@ if args.sitemap_url:
print('--- Running Doxygen')
subprocess.run("doxygen", shell=True, check=True)
print('--- Parsing module headers')
modules = {}
for module_name in os.listdir(MODULES_DIR):
module_dir = MODULES_DIR / module_name
if not module_dir.is_dir():
continue
module_header = module_dir / f'{module_name}.h'
if not module_header.is_file():
continue
modules[module_name] = createContentDict()
modules[module_name]['description'] = get_module_description(module_header)
print('--- Parsing Doxygen XML')
xml_dir = Path('xml')
root, root_path = get_modules_directory_xml_element(xml_dir)
modules = []
module_descriptions = {}
for dir_reference in root.findall('.//innerdir'):
module_name = dir_reference.text
module_path = root_path / module_name
module_header_path = module_path / f'{module_name}.h'
module_descriptions[module_name] = get_module_description(module_header_path)
get_doxygen_items_recursive(dir_reference.attrib['refid'], modules)
remove_empty_doxygen_items_recursive(modules[-1])
for xml_filename in os.listdir('xml'):
if xml_filename.startswith('namespacejuce'):
parseNamespaceXml(modules, xml_filename)
elif xml_filename.startswith('juce__'):
# There are no macros in the namespace XML so we need to get them separately
parseFileXml(modules, xml_filename)
print('--- Creating JUCE Module HTML')
@ -99,24 +155,24 @@ ET.SubElement(module_icon, 'path', {'d': 'M 28.0000 26.6406 L 50.0783 14.1016 C
main_div = ET.Element('div', {'class': 'juce-modules-continer'})
toc_div = ET.SubElement(main_div, 'div', {'class': 'juce-module-toc-container'})
ET.SubElement(toc_div, 'p', {'class': 'juce-module-toc-desc'}).text = "Here are the JUCE modules with some brief descriptions:"
ET.SubElement(toc_div, 'p', {'class': 'juce-module-toc-desc'}).text = "Here is a summary of the JUCE modules. To search absolutely everything please use the search bar."
toc_table = ET.SubElement(toc_div, 'table', {'class': 'juce-module-toc-table'})
for module in modules:
for key, contents in sorted(modules.items()):
toc_row = ET.SubElement(toc_table, 'tr', {'class': 'juce-module-toc-row'})
module_toc_name = ET.SubElement(toc_row, 'td', {'class': 'juce-module-toc-module-name'})
ET.SubElement(module_toc_name, 'a', {'href': f'#{module['name']}', 'class': 'juce-module-toc-module-name-link'}).text = module['name']
ET.SubElement(toc_row, 'td', {'class': 'juce-module-toc-module-decs'}).text = module_descriptions[module['name']]
ET.SubElement(module_toc_name, 'a', {'href': f'#{key}', 'class': 'juce-module-toc-module-name-link'}).text = key
ET.SubElement(toc_row, 'td', {'class': 'juce-module-toc-module-decs'}).text = contents['description']
ET.SubElement(main_div, 'div', {'class': 'juce-module-toc-divider'})
for module in modules:
for key, contents in sorted(modules.items()):
module_div = ET.SubElement(main_div, 'div', {'class': 'juce-module'})
module_header_div = ET.SubElement(module_div, 'div', {'class': 'juce-module-header'})
module_title_div = ET.SubElement(module_header_div, 'div', {'class': 'juce-module-title'})
module_title_div.append(module_icon)
ET.SubElement(module_title_div, 'span', {'id': module['name'], 'class': 'juce-module-name'}).text = module['name']
ET.SubElement(module_header_div, 'span', {'class': 'juce-module-desc'}).text = module_descriptions[module['name']]
ET.SubElement(module_title_div, 'span', {'id': key, 'class': 'juce-module-name'}).text = key
ET.SubElement(module_header_div, 'span', {'class': 'juce-module-desc'}).text = contents['description']
module_contents_div = ET.SubElement(module_div, 'div', {'class': 'juce-module-contents'})
write_module_html_recursive(module_contents_div, module)
write_module_html_recursive(module_contents_div, contents)
print('--- Updating Doxygen HTML')
html = ET.tostring(main_div, encoding='utf-8', method='html').decode('utf-8')