-
Notifications
You must be signed in to change notification settings - Fork 497
Code documentation support #471
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
paoloose
wants to merge
35
commits into
Dav1dde:glad2
Choose a base branch
from
paoloose:glad2-inline-docs
base: glad2
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 27 commits
Commits
Show all changes
35 commits
Select commit
Hold shift + click to select a range
768fda8
add docs flag
paoloose 67a5683
add utils
paoloose 283ce5b
update .gitignore
paoloose 148070e
integrate code documentation
paoloose 0034c22
add support for doc comments on template
paoloose 4129015
add docsgl documentation
paoloose 7fca80c
improve templating
paoloose c1f7e86
make text parsing utilities more versatile
paoloose a8fc033
support custom break logic for templates
paoloose ec699c1
implement DocsGL documentation
paoloose 5977606
fix parsing for descriptions with multiple parameters
paoloose f2c4fbb
fully parse doc params description
paoloose 0e267f7
fix crash when no --with-docs is present
paoloose 947a8f0
fix pipe format for templates
paoloose 48d8bab
rename ApiDocumentation to SpecificationDocs
paoloose 3a15146
only load documentation if --with-docs is present
paoloose 3cd03ec
fix <dt> listing format
paoloose d87dfc4
improve parsing and add basic support for equations
paoloose 300b6fe
revert specification load change
paoloose 5b5798c
remove specification-docs dependency
paoloose c71a789
refactor: pass docs to generator.generate instead of the specification
paoloose 0c956e2
change docs out dir from .cached to .docs
paoloose 29a8ee7
make --with-docs a global option since it's language agnostic
paoloose 9aa3dbd
fix: log warning when no documentation is found for the spec
paoloose b728972
fix docs rendering
paoloose 5b73a9f
prefer lxml instead of xml package
paoloose 13e26fd
replace DocsGL with Khronos refpages
paoloose 57c5ead
fix parameters parsing and math namespacing
paoloose cbdb5a6
rename --with-docs to --docs
paoloose 70740de
don't xinclude by default when parsing xml from file
paoloose 37409b4
drop dependency lxml
paoloose 4a24403
implement requested changes
paoloose 4ac233b
improve readability and add comments for reference
paoloose 6e7e98d
Merge branch 'glad2' into glad2-inline-docs
paoloose 29de9f5
update math_symbols_map to handle new entity
paoloose File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -18,4 +18,6 @@ dist/ | |
| /rust/ | ||
| target/ | ||
| Cargo.lock | ||
| .vscode/ | ||
| .vscode/ | ||
| .docs/ | ||
| .venv/ | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,190 @@ | ||
| import re | ||
| import glad.util | ||
| from lxml import etree | ||
paoloose marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| from glad.parse import DocumentationSet, SpecificationDocs, CommandDocs, xml_parse | ||
| from glad.util import prefix, suffix, raw_text | ||
|
|
||
| class OpenGLRefpages(SpecificationDocs): | ||
| DOCS_NAME = 'opengl_refpages' | ||
|
|
||
| URL = 'https://github.com/KhronosGroup/OpenGL-Refpages/archive/refs/heads/main.zip' | ||
| SPEC = 'gl' | ||
|
|
||
| def select(self, feature_set): | ||
| current_major = list(feature_set.info)[0].version.major | ||
paoloose marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| commands = dict() | ||
|
|
||
| # At the time of writing Khronos hosts documentations for gl4 and gl2.1. | ||
| available_versions = ['gl2.1'] | ||
| if current_major >= 4: | ||
| available_versions.append('gl4') | ||
|
|
||
| for version_dir in available_versions: | ||
| version_dir = self.docs_dir / f'{version_dir}' | ||
| if not version_dir.exists(): | ||
| break | ||
|
|
||
| for html_file in version_dir.glob('*.xml'): | ||
| for command, docs in OpenGLRefpages.docs_from_html_file(html_file).items(): | ||
| commands.setdefault(command, docs) | ||
|
|
||
| return DocumentationSet(commands=commands) | ||
|
|
||
| @classmethod | ||
| def docs_from_html_file(cls, path): | ||
| commands_parsed = dict() | ||
| version = path.parent.name | ||
| tree = xml_parse(path, recover=True) | ||
|
|
||
| # gl4 files contain a namespace that polutes the tags, so we clean it up. | ||
| for elem in tree.getiterator(): | ||
| try: | ||
| if elem.tag.startswith('{'): | ||
| elem.tag = etree.QName(elem).localname | ||
| except: | ||
| pass | ||
| etree.cleanup_namespaces(tree) | ||
|
|
||
| sections = tree.findall('.//refsect1') | ||
|
|
||
| # Brief parsing | ||
| # Command brief description appears in the first 'refnamediv' block | ||
| brief_block = tree.find('.//refnamediv//refpurpose') | ||
|
|
||
| if brief_block is None: | ||
| return dict() | ||
|
|
||
| if version == 'gl2.1': | ||
| url = f'https://registry.khronos.org/OpenGL-Refpages/{version}/xhtml/{path.stem}.xml' | ||
| else: | ||
| url = f'https://registry.khronos.org/OpenGL-Refpages/{version}/html/{path.stem}.xhtml' | ||
| brief = f'[{path.stem}]({url}) — {suffix(".", cls.xml_text(brief_block))}' | ||
|
|
||
| # Description parsing | ||
| description = [] | ||
| description_blocks = next( | ||
| (s for s in sections if raw_text(s.find('title')) == 'Description'), | ||
| None, | ||
| ) | ||
| if description_blocks is not None: | ||
| blocks = description_blocks.findall('./*') | ||
| description = list( | ||
| filter( | ||
| bool, | ||
| (prefix(CommandDocs.BREAK, cls.xml_text(p)) for p in blocks if p.tag != 'h2'), | ||
| ), | ||
| ) | ||
|
|
||
| # Notes parsing | ||
| notes = [] | ||
| notes_blocks = next((s for s in sections if raw_text(s.find('title')) == 'Notes'), None) | ||
| if notes_blocks is not None: | ||
| blocks = notes_blocks.findall('./*') | ||
| notes = list( | ||
| filter( | ||
| bool, | ||
| (prefix(CommandDocs.BREAK, cls.xml_text(p)) for p in blocks if p.tag != 'h2'), | ||
| ), | ||
| ) | ||
|
|
||
| # Parameters parsing | ||
| # Khronos specs puts all the function definitions inside funcsynopsis/funcdef blocks. | ||
| # | ||
| # However, instead of describing each function on a separate file, they group multiple | ||
| # related function definitions, whose parameters may be different, into a single file. | ||
| # This means that we have to find the correct block of parameters for each definition. | ||
| funcdefs = [ | ||
| d for d in tree.findall('.//funcsynopsis/*') | ||
| if d.find('.//funcdef') is not None | ||
| ] | ||
|
|
||
| for func_def in funcdefs: | ||
| func_name = func_def.find('.//function').text | ||
| func_params = [raw_text(s) for s in func_def.findall('.//parameter')] | ||
|
|
||
| # Params are defined in a separate section, called 'Parameters for <func_name>' | ||
| # or just 'Parameters'. | ||
| params_block = next( | ||
| (s for s in sections if raw_text(s.find('title')) == f'Parameters for {func_name}'), | ||
| None, | ||
| ) | ||
| if params_block is None: | ||
| for p in list(s for s in sections if raw_text(s.find('title')) == 'Parameters'): | ||
| block_params = [raw_text(n) for n in p.findall('.//term//parameter')] | ||
| if all(func_param in block_params for func_param in func_params): | ||
| params_block = p | ||
| break | ||
|
|
||
| # At this point we interpret params_block=None as a void parameter list. | ||
|
|
||
| params = [] | ||
| # A description can apply for more than one param (term), so we stack them until | ||
| # we find a listitem, which is a description of a param. | ||
| terms_stack = [] | ||
| for param_or_desc in params_block.findall('.//varlistentry/*') if params_block is not None else []: | ||
| if param_or_desc.tag == 'term': | ||
| terms_stack.append(param_or_desc) | ||
| continue | ||
| if param_or_desc.tag == 'listitem': | ||
| for term in terms_stack: | ||
| param_name = raw_text(term.find('.//parameter')) | ||
| if param_name in func_params: | ||
| params.append(CommandDocs.Param(param_name, cls.xml_text(param_or_desc))) | ||
| terms_stack.clear() | ||
|
|
||
| commands_parsed[func_name] = CommandDocs( | ||
| brief, params, description, notes, None, None, | ||
| ) | ||
| return commands_parsed | ||
|
|
||
| @staticmethod | ||
| def format(e, is_tail=False): | ||
| if is_tail: | ||
| if e.tag == 'dt': | ||
| # closing a definition term | ||
| return '\n' | ||
| if e.tag == 'mtr': | ||
| # closing a mathjax row | ||
| return '\n' | ||
| r = re.sub(r'\n+', '', e.tail) | ||
| if e.tag in ('mn', 'msub'): | ||
| return '' | ||
| return re.sub(r'\n+', '', e.tail) | ||
|
|
||
| if e.tag == 'a': | ||
| return f'' | ||
| if e.tag == 'code': | ||
| return f'`{e.text}`' | ||
| if e.tag == 'dt': | ||
| return f'\n{CommandDocs.BREAK}- ' | ||
| if e.tag == 'li': | ||
| return f'\n{CommandDocs.BREAK}-{e.text}' | ||
| return re.sub(r'\n+', '', e.text) | ||
|
|
||
| @classmethod | ||
| def xml_text(cls, e): | ||
| def paren(expr): | ||
| if re.match(r'^[a-zA-Z0-9_]+$', expr): | ||
| return expr | ||
| return f'({expr})' | ||
|
|
||
| def mfenced(e): | ||
| if e.attrib['close']: | ||
| return f'{e.attrib["open"]}{", ".join(cls.xml_text(c) for c in e)}{e.attrib["close"]}' | ||
| return f'{e.attrib["open"]}{" ".join(cls.xml_text(c) for c in e)}' | ||
|
|
||
| text = ''.join(glad.util.itertext( | ||
| e, | ||
| convert={ | ||
| 'table': lambda _: f'(table omitted)', | ||
| 'informaltable': lambda _: f'(table omitted)', | ||
| 'programlisting': lambda _: f'(code omitted)', | ||
| 'mml:mfrac': lambda e, : f'{paren(cls.xml_text(e[0]))}/{paren(cls.xml_text(e[1]))}', # | ||
| 'mml:msup': lambda e: f'{paren(cls.xml_text(e[0]))}^{paren(cls.xml_text(e[1]))}', # | ||
| 'mml:msub': lambda e: f'{paren(cls.xml_text(e[0]))}_{paren(cls.xml_text(e[1]))}', # | ||
| 'mml:mtd': lambda e: f'{cls.xml_text(e[0])}; ', # | ||
| 'mml:mfenced': mfenced, # | ||
| }, | ||
| format=cls.format, | ||
| )) | ||
| return re.sub(r'\n? +', ' ', text.strip()) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.