Repository: michaeljones/breathe Branch: main Commit: 021d6e52e8e2 Files: 468 Total size: 1.0 MB Directory structure: gitextract_1kiyib7g/ ├── .github/ │ ├── FUNDING.yml │ ├── dependabot.yml │ └── workflows/ │ ├── cache_doxygen.yml │ ├── create-release.yml │ ├── documentation.yml │ ├── lint.yml │ └── unit_tests.yml ├── .gitignore ├── .readthedocs.yaml ├── .ruff.toml ├── CHANGELOG.rst ├── CONTRIBUTING.rst ├── CONTRIBUTORS.rst ├── LICENSE ├── MANIFEST.in ├── Makefile ├── README.rst ├── breathe/ │ ├── README.rst │ ├── __init__.py │ ├── apidoc.py │ ├── cpp_util.py │ ├── directives/ │ │ ├── __init__.py │ │ ├── class_like.py │ │ ├── content_block.py │ │ ├── file.py │ │ ├── function.py │ │ ├── index.py │ │ ├── item.py │ │ └── setup.py │ ├── exception.py │ ├── file_state_cache.py │ ├── filetypes.py │ ├── finder/ │ │ ├── __init__.py │ │ ├── compound.py │ │ ├── factory.py │ │ └── index.py │ ├── parser.py │ ├── path_handler.py │ ├── process.py │ ├── project.py │ └── renderer/ │ ├── __init__.py │ ├── filter.py │ ├── mask.py │ ├── sphinxrenderer.py │ └── target.py ├── breathe-apidoc.py ├── documentation/ │ ├── .gitignore │ ├── Makefile │ ├── compare │ ├── environment.yaml │ ├── make.bat │ └── source/ │ ├── _static/ │ │ └── breathe.css │ ├── autofile.rst │ ├── autoindex.rst │ ├── class.rst │ ├── code/ │ │ ├── groups.h │ │ ├── namespaces.h │ │ ├── nested_list_1.h │ │ ├── nested_list_2.h │ │ ├── nested_list_3.h │ │ ├── nested_list_4.h │ │ ├── nested_list_5.h │ │ └── nutshell.h │ ├── codeblocks.rst │ ├── codeguide.rst │ ├── concept.rst │ ├── conf.py │ ├── contributing.rst │ ├── credits.rst │ ├── customcss.rst │ ├── define.rst │ ├── differences.rst │ ├── directives.rst │ ├── domains.rst │ ├── dot_graphs.rst │ ├── doxygen.rst │ ├── embeddedrst.rst │ ├── enum.rst │ ├── enumvalue.rst │ ├── file.rst │ ├── function.rst │ ├── group.rst │ ├── groups.rst │ ├── index.rst │ ├── inline.rst │ ├── latexmath.rst │ ├── lists.rst │ ├── markups.rst │ ├── members.rst │ ├── namespace.rst │ ├── page.rst │ ├── quickstart.rst │ ├── readthedocs.rst │ ├── specific.rst │ ├── spelling_wordlist.txt │ ├── struct.rst │ ├── tables.rst │ ├── template.rst │ ├── testpages.rst │ ├── tinyxml.rst │ ├── typedef.rst │ ├── union.rst │ └── variable.rst ├── examples/ │ ├── doxygen/ │ │ ├── .gitignore │ │ ├── Makefile │ │ ├── afterdoc.cfg │ │ ├── afterdoc.h │ │ ├── author.cfg │ │ ├── author.cpp │ │ ├── autolink.cfg │ │ ├── autolink.cpp │ │ ├── class.cfg │ │ ├── class.h │ │ ├── concept.cfg │ │ ├── concept.h │ │ ├── define.cfg │ │ ├── define.h │ │ ├── diagrams.cfg │ │ ├── diagrams_a.h │ │ ├── diagrams_b.h │ │ ├── diagrams_c.h │ │ ├── diagrams_d.h │ │ ├── diagrams_e.h │ │ ├── docstring.cfg │ │ ├── docstring.py │ │ ├── enum.cfg │ │ ├── enum.h │ │ ├── example.cfg │ │ ├── example.cpp │ │ ├── example_test.cpp │ │ ├── file.cfg │ │ ├── file.h │ │ ├── func.cfg │ │ ├── func.h │ │ ├── group.cfg │ │ ├── group.cpp │ │ ├── include.cfg │ │ ├── include.cpp │ │ ├── interface.cfg │ │ ├── interface.h │ │ ├── jdstyle.cfg │ │ ├── jdstyle.cpp │ │ ├── make.bat │ │ ├── manual.c │ │ ├── manual.cfg │ │ ├── memgrp.cfg │ │ ├── memgrp.cpp │ │ ├── overload.cfg │ │ ├── overload.cpp │ │ ├── page.cfg │ │ ├── page.doc │ │ ├── par.cfg │ │ ├── par.cpp │ │ ├── parblock.cfg │ │ ├── parblock.cpp │ │ ├── pyexample.cfg │ │ ├── pyexample.py │ │ ├── qtstyle.cfg │ │ ├── qtstyle.cpp │ │ ├── relates.cfg │ │ ├── relates.cpp │ │ ├── restypedef.cfg │ │ ├── restypedef.cpp │ │ ├── structcmd.cfg │ │ ├── structcmd.h │ │ ├── tag.cfg │ │ ├── tag.cpp │ │ ├── templ.cfg │ │ └── templ.cpp │ └── specific/ │ ├── .gitignore │ ├── Makefile │ ├── alias.cfg │ ├── alias.h │ ├── array.cfg │ ├── array.h │ ├── auto.cfg │ ├── auto_class.h │ ├── auto_function.h │ ├── c_enum.cfg │ ├── c_enum.h │ ├── c_file.cfg │ ├── c_file.h │ ├── c_macro.cfg │ ├── c_macro.h │ ├── c_struct.cfg │ ├── c_struct.h │ ├── c_typedef.cfg │ ├── c_typedef.h │ ├── c_union.cfg │ ├── c_union.h │ ├── class.cfg │ ├── class.cpp │ ├── class.h │ ├── code_blocks.cfg │ ├── code_blocks.h │ ├── cpp_anon.cfg │ ├── cpp_anon.h │ ├── cpp_concept.cfg │ ├── cpp_concept.h │ ├── cpp_constexpr_hax.cfg │ ├── cpp_constexpr_hax.h │ ├── cpp_enum.cfg │ ├── cpp_enum.h │ ├── cpp_friendclass.cfg │ ├── cpp_friendclass.h │ ├── cpp_function.cfg │ ├── cpp_function.h │ ├── cpp_function_lookup.cfg │ ├── cpp_function_lookup.h │ ├── cpp_inherited_members.cfg │ ├── cpp_inherited_members.h │ ├── cpp_ns_template_specialization.cfg │ ├── cpp_ns_template_specialization.h │ ├── cpp_trailing_return_type.cfg │ ├── cpp_trailing_return_type.h │ ├── cpp_union.cfg │ ├── cpp_union.h │ ├── decl_impl.cfg │ ├── decl_impl.cpp │ ├── decl_impl.h │ ├── define.cfg │ ├── define.h │ ├── dot_graphs.cfg │ ├── dot_graphs.h │ ├── dotfile.dot │ ├── enum.cfg │ ├── enum.h │ ├── fixedwidthfont.cfg │ ├── fixedwidthfont.h │ ├── functionOverload.cfg │ ├── functionOverload.h │ ├── group.cfg │ ├── group.h │ ├── headerfile.cfg │ ├── headerfile.h │ ├── headings.cfg │ ├── headings.h │ ├── image.cfg │ ├── image.h │ ├── inheritance.cfg │ ├── inheritance.h │ ├── inline.cfg │ ├── inline.h │ ├── interface.cfg │ ├── interface.h │ ├── latexmath.cfg │ ├── latexmath.h │ ├── links.cfg │ ├── links.h │ ├── lists.cfg │ ├── lists.h │ ├── make.bat │ ├── membergroups.cfg │ ├── membergroups.h │ ├── members.cfg │ ├── members.h │ ├── multifile/ │ │ ├── one/ │ │ │ └── Util.h │ │ └── two/ │ │ └── Util.h │ ├── multifile.cfg │ ├── name.cfg │ ├── name.h │ ├── namespacefile.cfg │ ├── namespacefile.h │ ├── nutshell.cfg │ ├── nutshell.h │ ├── parameters.cfg │ ├── parameters.h │ ├── programlisting.cfg │ ├── programlisting.h │ ├── programlistinginclude.txt │ ├── qtsignalsandslots.cfg │ ├── qtsignalsandslots.h │ ├── rst.cfg │ ├── rst.h │ ├── simplesect.cfg │ ├── simplesect.h │ ├── struct.cfg │ ├── struct.h │ ├── struct_function.cfg │ ├── struct_function.h │ ├── tables.cfg │ ├── tables.h │ ├── template_class.cfg │ ├── template_class.h │ ├── template_class_non_type.cfg │ ├── template_class_non_type.h │ ├── template_function.cfg │ ├── template_function.h │ ├── template_specialisation.cfg │ ├── template_specialisation.h │ ├── template_type_alias.cfg │ ├── template_type_alias.h │ ├── typedef.cfg │ ├── typedef.h │ ├── union.cfg │ ├── union.h │ ├── userdefined.cfg │ ├── userdefined.h │ ├── using_in_ns.cfg │ ├── using_in_ns.h │ ├── xrefsect.cfg │ └── xrefsect.h ├── make.bat ├── pyproject.toml ├── scripts/ │ ├── doxygen_cache.py │ └── generate_tests_results.py ├── tests/ │ ├── conftest.py │ ├── data/ │ │ ├── arange.xml │ │ ├── auto/ │ │ │ ├── auto_class.h │ │ │ ├── auto_function.h │ │ │ ├── compare.xml │ │ │ └── input.rst │ │ ├── classSample.xml │ │ ├── docutils.css │ │ ├── ellipsis.xml │ │ ├── examples/ │ │ │ ├── README.rst │ │ │ ├── doxyfile_template │ │ │ ├── test_alias/ │ │ │ │ ├── alias.h │ │ │ │ ├── compare.xml │ │ │ │ ├── extra_dox_opts.txt │ │ │ │ └── input.rst │ │ │ ├── test_array/ │ │ │ │ ├── array.h │ │ │ │ ├── compare.xml │ │ │ │ └── input.rst │ │ │ ├── test_c_enum/ │ │ │ │ ├── c_enum.h │ │ │ │ ├── compare.xml │ │ │ │ └── input.rst │ │ │ ├── test_c_file/ │ │ │ │ ├── c_file.h │ │ │ │ ├── compare.xml │ │ │ │ └── input.rst │ │ │ ├── test_class/ │ │ │ │ ├── class.cpp │ │ │ │ ├── class.h │ │ │ │ ├── compare.xml │ │ │ │ └── input.rst │ │ │ ├── test_code_blocks/ │ │ │ │ ├── code_blocks.h │ │ │ │ ├── compare.xml │ │ │ │ └── input.rst │ │ │ ├── test_cpp_concept/ │ │ │ │ ├── compare.xml │ │ │ │ ├── cpp_concept.h │ │ │ │ └── input.rst │ │ │ ├── test_cpp_enum/ │ │ │ │ ├── compare.xml │ │ │ │ ├── cpp_enum.h │ │ │ │ └── input.rst │ │ │ ├── test_cpp_friendclass/ │ │ │ │ ├── compare.xml │ │ │ │ ├── cpp_friendclass.h │ │ │ │ └── input.rst │ │ │ ├── test_cpp_function/ │ │ │ │ ├── compare.xml │ │ │ │ ├── cpp_function.h │ │ │ │ └── input.rst │ │ │ ├── test_cpp_inherited_members/ │ │ │ │ ├── compare.xml │ │ │ │ ├── cpp_inherited_members.h │ │ │ │ ├── extra_dox_opts.txt │ │ │ │ └── input.rst │ │ │ ├── test_cpp_trailing_return_type/ │ │ │ │ ├── compare-1.11.0.xml │ │ │ │ ├── compare.xml │ │ │ │ ├── cpp_trailing_return_type.h │ │ │ │ └── input.rst │ │ │ ├── test_define/ │ │ │ │ ├── compare.xml │ │ │ │ ├── define.h │ │ │ │ └── input.rst │ │ │ ├── test_diagrams/ │ │ │ │ ├── compare.xml │ │ │ │ ├── diagrams_a.h │ │ │ │ ├── diagrams_b.h │ │ │ │ ├── diagrams_c.h │ │ │ │ ├── diagrams_d.h │ │ │ │ ├── diagrams_e.h │ │ │ │ ├── extra_dox_opts.txt │ │ │ │ └── input.rst │ │ │ ├── test_dot_graphs/ │ │ │ │ ├── compare.xml │ │ │ │ ├── dot_graphs.h │ │ │ │ ├── dotfile.dot │ │ │ │ └── input.rst │ │ │ ├── test_group/ │ │ │ │ ├── compare-1.10.0.xml │ │ │ │ ├── compare.xml │ │ │ │ ├── group.h │ │ │ │ └── input.rst │ │ │ ├── test_group_content_only/ │ │ │ │ ├── compare.xml │ │ │ │ ├── group_content_only.hpp │ │ │ │ └── input.rst │ │ │ ├── test_group_member_ref/ │ │ │ │ ├── compare.xml │ │ │ │ ├── group.cpp │ │ │ │ └── input.rst │ │ │ ├── test_headings/ │ │ │ │ ├── compare.xml │ │ │ │ ├── extra_dox_opts.txt │ │ │ │ ├── headings.h │ │ │ │ └── input.rst │ │ │ ├── test_html_entities/ │ │ │ │ ├── compare.xml │ │ │ │ ├── entities.h │ │ │ │ └── input.rst │ │ │ ├── test_image/ │ │ │ │ ├── compare.xml │ │ │ │ ├── image.h │ │ │ │ └── input.rst │ │ │ ├── test_inheritance/ │ │ │ │ ├── compare.xml │ │ │ │ ├── inheritance.h │ │ │ │ └── input.rst │ │ │ ├── test_inline/ │ │ │ │ ├── compare.xml │ │ │ │ ├── inline.h │ │ │ │ └── input.rst │ │ │ ├── test_latexmath/ │ │ │ │ ├── compare.xml │ │ │ │ ├── input.rst │ │ │ │ └── latexmath.h │ │ │ ├── test_links/ │ │ │ │ ├── compare.xml │ │ │ │ ├── input.rst │ │ │ │ └── links.h │ │ │ ├── test_lists/ │ │ │ │ ├── compare.xml │ │ │ │ ├── input.rst │ │ │ │ └── lists.h │ │ │ ├── test_membergroups/ │ │ │ │ ├── compare.xml │ │ │ │ ├── input.rst │ │ │ │ └── membergroups.h │ │ │ ├── test_param_dirs/ │ │ │ │ ├── compare.xml │ │ │ │ ├── input.rst │ │ │ │ └── param_dirs.h │ │ │ ├── test_qtsignalsandslots/ │ │ │ │ ├── compare.xml │ │ │ │ ├── input.rst │ │ │ │ └── qtsignalsandslots.h │ │ │ ├── test_rst/ │ │ │ │ ├── compare.xml │ │ │ │ ├── input.rst │ │ │ │ └── rst.h │ │ │ ├── test_simplesect/ │ │ │ │ ├── compare.xml │ │ │ │ ├── input.rst │ │ │ │ └── simplesect.h │ │ │ ├── test_tables/ │ │ │ │ ├── compare.xml │ │ │ │ ├── input.rst │ │ │ │ └── tables.h │ │ │ ├── test_template_class_non_type/ │ │ │ │ ├── compare.xml │ │ │ │ ├── input.rst │ │ │ │ └── template_class_non_type.h │ │ │ ├── test_template_function/ │ │ │ │ ├── compare.xml │ │ │ │ ├── input.rst │ │ │ │ └── template_function.h │ │ │ ├── test_template_type_alias/ │ │ │ │ ├── compare.xml │ │ │ │ ├── input.rst │ │ │ │ └── template_type_alias.h │ │ │ ├── test_union/ │ │ │ │ ├── compare.xml │ │ │ │ ├── input.rst │ │ │ │ └── union.h │ │ │ ├── test_userdefined/ │ │ │ │ ├── compare.xml │ │ │ │ ├── input.rst │ │ │ │ └── userdefined.h │ │ │ └── test_xrefsect/ │ │ │ ├── compare.xml │ │ │ ├── extra_dox_opts.txt │ │ │ ├── input.rst │ │ │ └── xrefsect.h │ │ └── multi_project/ │ │ ├── A/ │ │ │ └── stuff.h │ │ ├── B/ │ │ │ └── stuff.h │ │ ├── C/ │ │ │ └── stuff.h │ │ ├── compare.xml │ │ └── input.rst │ ├── runtests.sh │ ├── test_examples.py │ ├── test_filters.py │ ├── test_parser.py │ ├── test_renderer.py │ ├── test_utils.py │ └── warnings/ │ ├── Makefile │ ├── make.bat │ └── source/ │ ├── class.rst │ ├── conf.py │ ├── define.rst │ ├── function.rst │ ├── group.rst │ └── index.rst └── xml_parser_generator/ ├── make_parser.py ├── module_template.py.in ├── schema.json ├── setuptools_builder.py └── stubs_template.pyi.in ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/FUNDING.yml ================================================ # These are supported funding model platforms github: breathe-doc patreon: # Replace with a single Patreon username open_collective: breathe ko_fi: # Replace with a single Ko-fi username tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry liberapay: # Replace with a single Liberapay username issuehunt: # Replace with a single IssueHunt username otechie: # Replace with a single Otechie username lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] ================================================ FILE: .github/dependabot.yml ================================================ version: 2 updates: - package-ecosystem: "github-actions" directory: "/" schedule: interval: "monthly" groups: github-actions: patterns: - "*" ================================================ FILE: .github/workflows/cache_doxygen.yml ================================================ name: download and cache Doxygen on: workflow_call jobs: install: runs-on: ubuntu-latest concurrency: group: linux-doxygen-${{ matrix.doxygen-version }}-cache strategy: fail-fast: false matrix: doxygen-version: ['1.9.4', '1.9.7'] steps: - uses: actions/cache/restore@v4 id: cache-doxygen with: path: doxygen-bin-arc key: ${{ runner.os }}-doxygen-${{ matrix.doxygen-version }} lookup-only: true restore-keys: | ${{ runner.os }}-doxygen- # TODO: Change to using github release downloads if possible - name: download Doxygen from SF binary archives if: steps.cache-doxygen.outputs.cache-hit != 'true' run: | mkdir doxygen-bin-arc && cd doxygen-bin-arc curl -L https://sourceforge.net/projects/doxygen/files/rel-${{ matrix.doxygen-version }}/doxygen-${{ matrix.doxygen-version }}.linux.bin.tar.gz > doxygen.tar.gz - uses: actions/cache/save@v4 if: steps.cache-doxygen.outputs.cache-hit != 'true' with: path: doxygen-bin-arc key: ${{ steps.cache-doxygen.outputs.cache-primary-key }} ================================================ FILE: .github/workflows/create-release.yml ================================================ name: Create release on: push: tags: - "v*" workflow_dispatch: permissions: contents: read concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true env: FORCE_COLOR: "1" UV_SYSTEM_PYTHON: "1" # make uv do global installs jobs: publish-pypi: runs-on: ubuntu-latest name: PyPI Release environment: release if: github.repository_owner == 'breathe-doc' permissions: id-token: write # for PyPI trusted publishing steps: - uses: actions/checkout@v6 with: persist-credentials: false - name: Set up Python uses: actions/setup-python@v6 with: python-version: "3" - name: Install uv uses: astral-sh/setup-uv@v7 with: version: latest enable-cache: false - name: Install Ruff uses: astral-sh/ruff-action@v3 with: args: --version version: 0.9.2 - name: Install build dependencies (pypa/build, twine) # Install jinja2 to make the parser - probably better to install it via # the Sphinx dependency in the pyproject.toml file but I don't know how # to do that at the moment run: uv pip install build "twine>=5.1" "jinja2>=3.1.6" setuptools - name: Generate parser run: | make parser make format-parser - name: Build distribution run: python -m build - name: Check distribution run: | twine check --strict dist/* - name: Upload to PyPI uses: pypa/gh-action-pypi-publish@release/v1 ================================================ FILE: .github/workflows/documentation.yml ================================================ name: Build documentation on: pull_request: workflow_dispatch: permissions: contents: read concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true env: DOXYGEN_VERSION: 1.9.4 FORCE_COLOR: "1" jobs: cache-doxygen: uses: ./.github/workflows/cache_doxygen.yml build: needs: cache-doxygen runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: Set up Python uses: actions/setup-python@v6 with: python-version: "3" cache: 'pip' - name: Install apt dependencies run: | sudo apt-get -y update sudo apt-get -y install graphviz - uses: actions/cache/restore@v4 id: cache-doxygen with: path: doxygen-bin-arc fail-on-cache-miss: true key: ${{ runner.os }}-doxygen-${{ env.DOXYGEN_VERSION }} - name: Install doxygen # at some point actions/cache/restore@4 started unpacking doxygen.tar.gz # automatically and I don't know why -- Rouslan run: | cd doxygen-bin-arc if test -d doxygen; then cd doxygen else gunzip doxygen.tar.gz tar xf doxygen.tar cd doxygen-${{ env.DOXYGEN_VERSION }} fi sudo make install - name: Install Python dependencies run: | python -m pip install --upgrade pip python -m pip install --editable .[build] python -m pip install .[docs] # Remove the version of breathe that is automatically installed by the previous commands # as it confuses the build. This build should pick up breathe from the repo source python -m pip uninstall --yes breathe - name: Build the documentation run: | make parser make html rm documentation/build/html/.buildinfo - uses: actions/upload-artifact@v5 with: name: docs build artifacts path: | documentation/build/html examples/*/*/xml ================================================ FILE: .github/workflows/lint.yml ================================================ name: Lint source code on: pull_request: workflow_dispatch: permissions: contents: read concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true env: FORCE_COLOR: "1" jobs: ruff: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 with: persist-credentials: false - name: Install Ruff uses: astral-sh/ruff-action@v3 with: args: --version version: 0.9.2 - name: Install dependencies # "--editable" is needed so that _parser.py is placed in the local folder # where pytest will import from run: | python -m pip install --upgrade pip python -m pip install --editable ".[build]" - name: Generate parser run: | make parser make format-parser - name: Lint with Ruff run: ruff check --output-format=github - name: Format with Ruff run: ruff format --diff mypy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: Set up Python uses: actions/setup-python@v6 with: python-version: "3" - name: Install dependencies # "--editable" is needed so that _parser.py is placed in the local folder # where pytest will import from run: | python -m pip install --upgrade pip python -m pip install --editable ".[build]" python -m pip install --editable ".[lint]" - name: Generate parser run: make parser - name: Type check with mypy run: mypy --warn-redundant-casts --warn-unused-ignores breathe tests twine: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: Set up Python uses: actions/setup-python@v6 with: python-version: "3" - name: Install dependencies # "--editable" is needed so that _parser.py is placed in the local folder # where pytest will import from run: | python -m pip install --upgrade pip python -m pip install --upgrade twine build python -m pip install --editable ".[build]" - name: Generate parser run: make parser - name: Lint with twine run: | python -m build . twine check dist/* ================================================ FILE: .github/workflows/unit_tests.yml ================================================ name: Tests on: pull_request: paths: - ".github/workflows/unit_tests.yml" - "breathe/**" - "tests/**" permissions: contents: read concurrency: group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} cancel-in-progress: true env: FORCE_COLOR: "1" PYTHONDEVMODE: "1" # -X dev PYTHONWARNDEFAULTENCODING: "1" # -X warn_default_encoding jobs: cache-doxygen: uses: ./.github/workflows/cache_doxygen.yml test: needs: cache-doxygen runs-on: ubuntu-latest strategy: fail-fast: false matrix: doxygen-version: - '1.9.4' - '1.9.7' python-version: - '3.9' - '3.10' - '3.11' - '3.12' - '3.13' sphinx-version: - '6.2' - '7.0' - '7.1' - '7.2' # Ubuntu 24.04 # version 7.3 broke up the domain modules into packages, changing # where some classes had to be imported from - '7.3' - '7.4' # Ubuntu 24.10 - '8.0' - '8.1' # Ubuntu 25.04 - '8.2' # Ubuntu 25.04 exclude: - python-version: '3.9' sphinx-version: '8.0' - python-version: '3.9' sphinx-version: '8.1' - python-version: '3.9' sphinx-version: '8.2' - python-version: '3.9' sphinx-version: 'latest' - python-version: '3.10' sphinx-version: '8.2' - python-version: '3.10' sphinx-version: 'latest' steps: - uses: actions/checkout@v6 - name: Set up Python ${{ matrix.python-version }} uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} cache: 'pip' - name: Install Sphinx ${{ matrix.sphinx-version }} if: matrix.sphinx-version != 'latest' run: | python -m pip install -Iv Sphinx==${{ matrix.sphinx-version }} - name: Install Sphinx master if: matrix.sphinx-version == 'latest' run: | python -m pip install "Sphinx @ git+https://github.com/sphinx-doc/sphinx.git@master" - uses: actions/cache/restore@v4 id: cache-doxygen with: path: doxygen-bin-arc fail-on-cache-miss: true key: ${{ runner.os }}-doxygen-${{ matrix.doxygen-version }} - name: Install doxygen # at some point actions/cache/restore@4 started unpacking doxygen.tar.gz # automatically and I don't know why -- Rouslan run: | cd doxygen-bin-arc if test -d doxygen; then cd doxygen else gunzip doxygen.tar.gz tar xf doxygen.tar cd doxygen-${{ matrix.doxygen-version }} fi sudo make install - name: Install dependencies # "--editable" is needed so that _parser.py is placed in the local # folder where pytest will import from run: | pip install --upgrade pip pip install --editable .[build] pip install --editable .[test] - name: Generate parser run: | make parser - name: Test with pytest if: matrix.sphinx-version != 'latest' run: python -m pytest -vv env: PYTHONWARNINGS: "error" # treat all warnings as errors - name: Test with pytest if: matrix.sphinx-version == 'latest' run: python -m pytest -vv ================================================ FILE: .gitignore ================================================ *.pyc /.project /.pydevproject build/ /dist /breathe.egg-info /.mypy_cache # Folder for placing stuff to be ignored /ignored # My virtual env folder /python-2.7.4 # Test folder for building simple examples of the documentation /simple # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm ## Directory-based project format .idea/ # if you remove the above rule, at least ignore user-specific stuff: # .idea/workspace.xml # .idea/tasks.xml # and these sensitive or high-churn files: # .idea/dataSources.ids # .idea/dataSources.xml # .idea/sqlDataSources.xml # .idea/dynamic.xml ## File-based project format *.ipr *.iws *.iml ## Additional for IntelliJ out/ # generated by mpeltonen/sbt-idea plugin .idea_modules/ # generated by JIRA plugin atlassian-ide-plugin.xml # generated by Crashlytics plugin (for Android Studio and Intellij) com_crashlytics_export_strings.xml # modified by build process examples/doxygen/example.tag examples/specific/dot_graphs/xml/dotfile.dot # generated in editable install /breathe/_parser.py ================================================ FILE: .readthedocs.yaml ================================================ version: 2 python: version: 3 sphinx: builder: html configuration: documentation/source/conf.py fail_on_warning: false conda: environment: documentation/environment.yaml ================================================ FILE: .ruff.toml ================================================ target-version = "py39" # Pin Ruff to Python 3.9 line-length = 100 output-format = "full" [format] preview = true quote-style = "double" [lint] preview = true ignore = [ "UP031", # Use format specifiers instead of percent format ] select = [ "C4", # flake8-comprehensions # "B", # flake8-bugbear # "D", # pydocstyle "E", # pycodestyle "F", # pyflakes "FA", # flake8-future-annotations "FLY", # flynt "FURB", # refurb "G", # flake8-logging-format "I", # isort "LOG", # flake8-logging # "N", # pep8-naming "PERF", # perflint "PGH", # pygrep-hooks "PT", # flake8-pytest-style "TC", # flake8-type-checking "TID", # flake8-tidy-imports "UP", # pyupgrade "W", # pycodestyle ] [lint.per-file-ignores] "breathe/parser/{compound,index}super.py" = [ "E266", # Too many leading `#` before block comment ] "examples/doxygen/pyexample.py" = [ "E266", # Too many leading `#` before block comment ] [lint.flake8-quotes] inline-quotes = "double" [lint.flake8-type-checking] exempt-modules = [] strict = true [lint.flake8-tidy-imports] ban-relative-imports = "all" [lint.isort] forced-separate = [ "tests", ] required-imports = [ "from __future__ import annotations", ] [lint.pydocstyle] convention = "pep257" ignore-decorators = ["typing.overload"] ignore-var-parameters = true ================================================ FILE: CHANGELOG.rst ================================================ Change Log ---------- Inspired by `Keepachangelog.com `__. - 2025-07-08 - **Breathe v5.0.0a5** Adjust create-release process to include ruff for formatting generated ``_parser.py`` file. - 2025-07-08 - **Breathe v5.0.0a4** Adjust create-release process to try to generate ``_parser.py`` file. - 2025-07-08 - **Breathe v5.0.0a3** Adjust create-release process to try to generate ``_parser.py`` file. - 2025-07-08 - **Breathe v5.0.0a2** - Removed sphinx_csharp git-based dependency `#1035 `__ - 2025-07-08 - **Breathe v5.0.0a1** Alpha release of v5. Contains a significant refactor of the internal logic. Thanks to @JasperCraeghs and @Rouslan. - Merging long standing refactor branches `#1029 `__ - Performance improvements, new tests, more typing annotations and miscellaneous fixes `#967 `__ - Support members in a Doxygen 1.9.7 group `#934 `__ - Fix docstring missing quotes `#1020 `__ - Fix docstring missing quotes `#1020 `__ - Fix some incorrect indents in the document `#1032 `__ - Enable imgconverter extension to fix LaTeX documentation build `#1027 `__ - Bump astral-sh/setup-uv from 5 to 6 in the github-actions group `#1025 `__ - 2025-02-22 - **Breathe v4.36.0** - Update `test_renderer` so that tests pass with Sphinx 7.2. `#976 `__ - Fix PosixPath issue with Sphinx 7.2. `#964 `__ - Avoid `RemovedInSphinx80Warning` in path-manipulation code. `#977 `__ - Require Sphinx 7.2 or later, Python 3.9 or later, and Doxygen 1.9.2 or later. `#887 `__, `#946 `__ `#955 `__ - Begin to use pathlib. - Resolve full title for doxygenpage and doxygengroup and allow for omitting the title all together `#939 `__ - Insert signature name for use with Sphinx Table of Contents `#959 `__ - Fix test failure with latest Sphinx master. `#1010 `__ - Fix error in template specialization with qualified arguments `#1010 `__ - 2023-02-28 - **Breathe v4.35.0** - Pull lone literal blocks in paragraphs up to produce correct doctree. `#833 `__ - Fix tests for changes in Sphinx 5.3. `#865 `__ - Bump Python requirement to 3.7. `#866 `__ - Support Sphinx 6. `#885 `__ - Support ``:sort:`` option to sort sections by name. `#879 `__ - 2022-06-20 - **Breathe v4.34.0** - Treat .unparsed as plain text. `#806 `__ - Remove unneeded type: ignore annotations. `#813 `__ - Fix internal ``NodeFinder`` visitor for when non-Docutils nodes are present in the content of a directive. `#812 `__ - Rename lint workflow. `#814 `__ - Type check pygments and limit docutils stub version. `#819 `__ - Convert dot files' relative path to absolute. `#821 `__ - CI, update Sphinx versions to test. `#834 `__ - CI, update for Sphinx 5.0.1. `#846 `__ - Fix inconsistency in example. `#843 `__ - Fix C# enum rendering crash. `#849 `__ - Drop Sphinx 3 support, add Sphinx 5 support. `#850 `__ - CICD: Disable python 3.6 for Sphinx master tests. `#853 `__ - Populate default include text-node's data field instead of raw-source. `#828 `__ - 2022-02-14 - **Breathe v4.33.1** - Avoid warning about multiple graphviz directives. `#804 `__ - 2022-02-14 - **Breathe v4.33.0** - Fix duplicate ``static`` in variable declarations. `#794 `__ - Update CICD for Sphinx 4.4.0 series. `#795 `__ - Pin version of black in CICD and reformat files. `#792 `__ - Fix code block highlighting. `#760 `__ - Refactoring, cleanup and typing improvements. `#802 `__ - Doxygen dot graphs to Sphinx graphviz. `#757 `__ - Support externally hosted images. `#705 `__ - Address a peculiarity in Doxygen aliases in doc. `#770 `__ - Add flag to doxygengroup, doxygennamespace to display only the description. `#718 `__ - Add support for MD block quotes with attribution(s). `#759 `__ - 2022-01-30 - **Breathe v4.32.0** - Added ``breathe_doxygen_aliases`` config variable. `#729 `__ - Render ``\remark``/``\remarks`` and ``\see``/``\sa`` using Sphinx/Docutils admonition style nodes. `#756 `__ - Render C++ scoped enums differently than unscoped enums, and with their underlying type. `#753 `__ - Render ``\retval`` lists using dedicated field list when Sphinx >= 4.3 is used. `#749 `__ - Make ``.. doxygenfunction`` handle function template specializations. `#750 `__ - Properly handle field-lists and admonitions in the detailed description of classes and functions. `#764 `__ - Add ``:confval:`breathe_show_include``` to control whether ``#include`` lines are shown. Defaults to ``True``. `#725 `__ - Fix sys.path adjustment in doc config. `#734 `__ - Fix sphinx renderer variable and function visitors for C#. `#737 `__ - Fix sphinx renderer class visitor for C#. `#738 `__ - Auto-format python code with black. `#743 `__ - Extend flake8 and address some style issues. `#745 `__ - Fix black formatting warning. `#747 `__ - Update Sphinx and Python versions tested against. `#765 `__ - Fix friend functions for older Doxygen versions. `#769 `__ - Doxygen >= 1.9.2 supports C++20 concepts, add support for them. `#779 `__ - Change the way directives are added to adhere to the interface, e.g., avoiding myst-parser to crash. `#780 `__ - Improved list of included files (with cross-references for local includes). `#763 `__ - Update flake8 and mypy related stuff. `#781 `__ - Update readme with logo and sponsorship info. `#784 `__ - Store version number in both setup.py and __init__.py. `#789 `__ - CICD: lint: continue with other jobs if black fails. `#791 `__ - 2021-09-14 - **Breathe v4.31.0** - Collapse multiple retvals into a single bullet list. `#697 `__ - Fix mypy issues on CI. `#731 `__ - Print usage message from 'compare' doc script. `#727 `__ - Test against Sphinx 4.0.3, 4.1.2 and 4.1.x branch. `#721 `__ - Fix duplicate ``static`` in function declarations. `#717 `__ `#720 `__ - Directive refactoring. `#698 `__ - Handle parsing errors. `#711 `__ - Make doxygenfunction more robust when matching parameters. `#722 `__ `#723 `__ - Separate, link and style the changelog. `#735 `__ - Update changelog and readme ahead of release. `#739 `__ - CICD: Track Sphinx 4.2.x development series. `#741 `__ - 2021-05-06 - **Breathe v4.30.0** - Fix retval rendering. `#687 `__ - Correctly label example as C. `#690 `__ - apidoc: add -m, --members option flag. `#694 `__ - 2021-04-30 - **Breathe v4.29.2** - Remove stale six dep. `#682 `__ - Render fields with multiple names instead of crashing. `#685 `__ - Start pytest via module instead of exe. `#686 `__ - 2021-04-23 - **Breathe v4.29.1** - Splice out parameter direction in field lists. `#675 `__ - Fixes for Sphinx v4. `#676 `__ - Fix paragraph in paragraph rendering. `#678 `__ - Strip names before lookup in doxygenfunction. `#679 `__ - When rendering template params, insert name by parsing. `#681 `__ - 2021-04-09 - **Breathe v4.29.0** - Do not add inline modifier for C#. `#668 `__ - Use add_css_file instead of deprecated/removed add_stylesheet. `#669 `__ - Use native docutils for field lists, notes, and warnings. `#670 `__ - Handle directives returning no nodes on error. `#672 `__ - 2021-03-29 - **Breathe v4.28.0** - Code and documentation for membergroups and members-only options. `#637 `__ - Add example.tag to gitignore as it gets modified during build process. `#644 `__ - Add support for content-only flag when rendering pages. `#645 `__ - When rendering a section, add target after title. `#647 `__ - Render pages content in order. `#651 `__ - Adds an ID to the rubric created for each section of a group. `#658 `__ - Add missing getter and setter for C#. `#661 `__ - Add support for rowspan/colspan to tables. `#642 `__ - 2021-02-16 - **Breathe v4.27.0** - Add various specifiers to functions and variables. `#628 `__ - Add multiply inherited class for PHP objects. `#630 `__ - Initial support for table rendering. `#632 `__ - Add rendering of \section, \subsection and \subsubsection. `#635 `__ - Sphinx 3.5 compatibility. `#640 `__ - Fix linking to sections. `#639 `__ - Add table examples to documentation. `#638 `__ - 2021-01-21 - **Breathe v4.26.1** - Fix doxygenfile causing duplicate IDs for unspecified sections. `#622 `__ - Fixes for doxygenfunction (friend keyword, friend class, arg checks). `#623 `__ - 2021-01-08 - **Breathe v4.26.0** - Add test for ellipsis ('...') in args. `#610 `__ - Sphinx 3.4.x compatibility. `#617 `__ - Adapt friendclass to Doxygen 1.9. `#618 `__ - 2020-12-16 - **Breathe v4.25.1** - Addendum to #606, for functions with '...'. `#609 `__ - 2020-12-15 - **Breathe v4.25.0** - Add support for \parblock parsing and rendering. `#603 `__ - Allow lookup in doxygenfunction without writing param names. `#606 `__ - 2020-12-01 - **Breathe v4.24.1** - Fix anchors on pages generated by Doxygen >= 1.8.17. `#602 `__ - 2020-11-15 - **Breathe v4.24.0** - Update CI for Sphinx 3.3.x and fix test mock. `#597 `__ - Add support for xrefitem based page generation (doxygenpage). `#596 `__ - 2020-10-20 - **Breathe v4.23.0** - Add initial xrefsect support. `#589 `__ - 2020-09-26 - **Breathe v4.22.1** - Fix anonymous struct/union usage in C domain. `#585 `__ - 2020-09-19 - **Breathe v4.22.0** - Fix Read the Docs build (again). `#576 `__ - New boolean `breathe_show_enumvalue_initializer` option specifying whether value of enumvalue should be displayed. `#581 `__ - 2020-09-10 - **Breathe v4.21.0** - Fix Read the Docs build. `#567 `__ - Document doxygenclass template specialisation spacing. `#570 `__ - Update upper Sphinx release to <3.4. `#571 `__ - Reuse breathe.__version__ in setup.py. `#572 `__ - Document :inner: for the doxygengroup section. `#573 `__ - Add support for verbatim inline elements. `#560 `__ - Fix wrong refid when Doxygen SEPARATE_MEMBER_PAGES is YES. `#566 `__ - 2020-08-19 - **Breathe v4.20.0** - Allow Sphinx 3.2. `#561 `__ - Update CI scripts with new Sphinx versions. `#552 `__ - Add support for C# using sphinx-csharp. `#550 `__ - Doc, fix typo, :source: -> :project:. `#551 `__ - Add support for innergroup. `#556 `__ - Avoid duplicate doxygen targets when debug tracing. `#563 `__ - Remove Travis badge from README file. `#564 `__ - 2020-06-17 - **Breathe v4.19.2** - Fix crash when visiting typedef. `#547 `__ - 2020-06-08 - **Breathe v4.19.1** - Mark package as compatible with Sphinx 3.1. - 2020-06-07 - **Breathe v4.19.0** - Refactoring. `#528 `__ - Make debug config variables available in conf.py. `#533 `__ - Fix warning formatting for function lookup. `#535 `__ - Correctly reverse nested namespaces in get_qualification. `#540 `__ - 2020-05-10 - **Breathe v4.18.1** - Fix friend class rendering and allow friend struct. `#522 `__ - Add extern examples to doc and remove variable hack. `#526 `__ - Render function candidates without using Sphinx directives. `#524 `__ - 2020-05-02 - **Breathe v4.18.0** - Support tiles in verbatim blocks. `#517 `__ - 2020-05-01 - **Breathe v4.17.0** - Scoped rendering, better integration with Sphinx, misc fixes. `#512 `__ - 2020-04-19 - **Breathe v4.16.0** - Strictly depend on Sphinx's minor version. `#498 `__ - Simplifications and fixes, use more of modern Sphinx natively. `#503 `__ - Add section option to the doxygen(auto)file directive. `#501 `__ - Fix link generation when enum is inside a group (enum FQDN). `#508 `__ - Fix creation of LaTeX math formulas. `#506 `__ - Improve documentation for doxygen(auto)file section option. `#509 `__ - 2020-04-07 - **Breathe v4.15.0** - Add license file to distribution. `#492 `__ - Update for Sphinx 3. `#491 `__ - 2020-04-07 - **Breathe v4.14.2** - Add GitHub actions. `#474 `__ - Fixes to use Sphinx 2.4.4. `#486 `__ - Add nose to python development requirements. #484. - Switch to pytest from nose. `#487 `__ - 2020-02-02 - **Breathe v4.14.1** - Use sphinx core instead of mathbase ext. `#469 `__ - Fix test failure for Sphinx >= 2.2.2. `#472 `__ - Update travis to Sphinx 2.3.1. `#471 `__ - 2019-11-26 - **Breathe v4.14.0** - Add events attribute to MockApp. `#452 `__ - Add bit field support for C/C++. `#454 `__ - Add alias and variable template support. `#461 `__ - 2019-08-01 - **Breathe v4.13.1** - Fix for template method pointer parameter issue. `#449 `__ - 2019-04-23 - **Breathe v4.13.0**.post0 - Drop support for python 2, require Sphinx >= 2.0. `#432 `__ - 2019-04-21 - **Breathe v4.13.0** - Adapt to upcoming Sphinx 2.0. `#411 `__ - Add support for rendering parameter direction information. `#428 `__ - 2019-03-15 - **Breathe v4.12.0** - Adapt to Sphinx 1.8. `#410 `__ - Let Sphinx handle more things. `#412 `__ - Use standard windows EOL for batch file. `#417 `__ - Fix flake8 F632 warnings. `#418 `__ - Update dep versions in readme, setup, requirements. `#419 `__ - Add option to render function parameters after the description. `#421 `__ - Remove spurious "typedef" in type declaration when using "using". `#424 `__ - 2018-12-11 - **Breathe v4.11.1** - Sphinxrenderer: handle typeless parameters gracefully. `#404 `__ - 2018-10-31 - **Breathe v4.11.0** - Fix typo in quickstart. `#393 `__ - Add support for QtSignals. `#401 `__ - 2018-08-07 - **Breathe v4.10.0** - Explicitly use Sphinx 1.7.5 for CI and dev. `#385 `__ - Print filename when printing ParserException. `#390 `__ - 2018-06-03 - **Breathe v4.9.1** - Don't append separator for paragraph type. `#382 `__ - 2018-06-01 - **Breathe v4.9.0** - Render newlines as separate paragraphs. `#380 `__ - 2018-05-26 - **Breathe v4.8.0** - Add quiet option to apidoc. `#375 `__ - Add PHP domain. `#351 `__ - Keep templates on adjacent lines. `#300 `__ - Show reference qualification for methods. `#332 `__ - Adapt tests/CI to newest Sphinx version. `#377 `__ - More robust name regex in renderer. `#370 `__ - Show base classes using Sphinx's cpp domain. `#295 `__ - Fix domain detection when rendering groups. `#365 `__ - Return parallel_{read,write}_safe true for Sphinx's -j. `#376 `__ - 2017-10-09 - **Breathe v4.7.3** - Support for enums in the cpp domain. - Handle case where compoundref does not have a refid value associated. - 2017-08-15 - **Breathe v4.7.2** - Fix issue with packaging on Python 2.7 with wheels. - 2017-08-13 - **Breathe v4.7.1** - Fixed bug regarding code snippets inside Doxygen comments. - 2017-08-09 - **Breathe v4.7.0** - New `outtypes` option to prevent documenting namespace and files - New boolean `breathe_show_define_initializer` option specifying whether value of macros should be displayed. - New boolean `breathe_use_project_refids` option controlling whether the refids generated by breathe for doxygen elements contain the project name or not. - Fixed - Support for Sphinx 1.6 - 2017-02-25 - **Breathe v4.6.0** - Support for the Interface directive - Display the contents of defines - 2017-02-12 - **Breathe v4.5.0** - Improve handling of c typedefs - Support new `desc_signature_line` node - Add `--project` flag to breathe-apidoc helper - Dropped testing for Python 3.3 and added 3.6 - 2016-11-13 - **Breathe v4.4.0** - Improve single line parameter documentation rendering - 2016-11-05 - **Breathe v4.3.1** - Version bump package confusion with wheel release - 2016-11-05 - **Breathe v4.3.0** - Rewritten rendering approach to use the visitor pattern - Dropped support for 2.6 & added testing for 3.5 - Fixed - Issue with running breathe-apidoc for the first time. - Improved handling of qualifiers, eg. const & volatile. - Supports functions in structs - Supports auto-doxygen code path on Windows - 2016-03-19 - **Breathe v4.2.0** - Added - Output links to a class' parents & children. - Support for Sphinx's `needs_extensions` config option. - breathe-apidoc script for generating ReStructuredText stub files with Breathe directives from doxygen xml files. - Fixed - Handling default values in parameter declarations - Output order not being reproducible due to iteration over Set. - Handling of multiple pointers and references - `SEVERE: Duplicate ID` warnings when using function overloads. - Use project name for link references when using default project. So we use the project name instead of 'project0'. - 2015-08-27 - **Breathe v4.1.0** - Added - ``breathe_doxygen_config_options`` config variable which allows for adding more config lines to the doxygen file used for the auto-directives. - Fixed - Display of array & array reference parameters for functions. - Handling of links to classes with template arguments. - Handling of unnamed enums in C. - Naming of template parameter section. - Finding functions that are within groups. - Rendering of 'typename' and 'class' keywords for templates. - 2015-04-02 - **Breathe v4.0.0** - Significant work on the code base with miminal reStructureText interface changes. To be documented. - 2014-11-09 - **Breathe v3.2.0** - Nothing Added, Deprecated or Removed - Fixed - Changed docutils/Sphinx node usage to fix latex/pdf output. - When checking for path separators check for both ``/`` and ``\`` regardless of the platform. - ``KeyError`` when using ``auto`` directives without specifying the ``:project:`` option even though the default project config setting was set. - Use of ``doxygenfunction`` no longer inappropriately triggers the duplicate target check and fails to output link targets. - Support for inline urls in the doxygen comments. - Support for array notation in function parameters. - Reduced intention by changing ``section-defs`` to use ``container`` & ``rubric`` nodes rather than ``desc`` nodes with signatures & content. Now headings like 'Public Functions' appear inline with their subject matter. - 2014-09-07 - **Breathe v3.1.0** - Nothing Deprecated or Removed - Added - The ``doxygenclass`` directive can now reference template specialisations by specifying the specialisation in the argument name. - Fixed - Displaying function parameters for Qt slots output. Previously they were missing even though Qt Slots are essentially just functions. - Displaying headings from doxygen comments as emphasized text. - Crash when generating warning about being unable to find a define, variable, enum, typedef or union. - Only output the definition name for a function parameter if the declartion name is not available. Previously, where they were both available we were getting two names next to each other for no good reason. - 2014-08-04 - **Breathe v3.0.0** - Improve output of const, volatile, virtual and pure-virtual keywords. - Fix css class output for HTML so that object types rather than names are output as the css classes. eg. 'function' instead of 'myFunction'. - Fix issue with Breathe getting confused over functions appearing in header and implementation files. - Improve matching for overloaded functions when using ``doxygenfunction`` directive. Also, provide a list of potential matches when no match is found. - Improved ``:members:`` implementation to handle inner classes properly. - Updated ``doxygenstruct`` to share the ``doxygenclass`` implementation path which grants it the options from ``doxygenclass`` directive. - Added ``:outline:`` option support to ``doxygengroup`` & ``doxygennamespace`` directives. - Added ``doxygennamespace`` directive. - Added ``:undoc-members:`` option to ``doxygenclass`` & ``doxygengroup`` directives. - **Breaking change**: Removed ``:sections:`` option for ``doxygenclass`` & ``doxygengroup`` directives and replaced it with ``:members:``, ``:protected-members:`` and ``:private-members:``, and changed ``breathe_default_sections`` config variable to ``breathe_default_members``. This is designed to more closely match the Sphinx autodoc functionality and interface. - 2014-06-15 - **Breathe v2.0.0** - Add compare script for checking changes to documentation caused by changes in the implementation. - Switched to ``https`` reference for MathJax Javascript. - **Breaking change**: Change ``autodoxygen*`` directives to require explicitly declared source files in the ``conf.py`` rather than attempting to detect them from the directive arguments. - Switch documentation hosting to ReadTheDocs.org. - **Breaking change**: Switch to assuming all relative paths are relative to the directory holding the ``conf.py`` file. Previously, it would assume they were relative to the user's current working directory. This breaks projects which use separate build & source directories. - Add ``doxygenunion`` directive. - Add ``doxygengroup`` directive. - Add support for lists in the output. They were previously ignored. - Updated implementation to use the docutils nodes that Sphinx does where possible. - 2014-06-01 - **Breathe v1.2.0** - Change log not recorded. ================================================ FILE: CONTRIBUTING.rst ================================================ Contributing ============ Firstly, thank you for making it this far. We really appreciate anyone who even thinks about reporting an issue or helping out with the code or docs. It is kind of you to take the time. Please Provide an Example ------------------------- If you're having trouble with how the contents of a particular Doxygen comment or a particular set of code is being processed then please provide an example of the in question. Ideally as a pull-request with additions to the `examples/specific` folder but feel free to provide the comment content in your issue instead. It really helps to accelerate development if we have examples to work from. ================================================ FILE: CONTRIBUTORS.rst ================================================ Contributors ============ Many thanks to everyone that has contributed to the project. - `Andne `_ - `arximboldi `_ - `D4N `_ - `dean0x7d `_ - `dg-dboehi `_ - `eric-wieser `_ - `fetzerch `_ - `gmarull `_ - `hidmic `_ - `ishitatsuyuki `_ - `jakobandersen `_ - `mattip `_ - `michaeljones `_ - `nijel `_ - `olitheolix `_ - `pczerkas `_ - `queezythegreat `_ - `remyleone `_ - `rhssk `_ - `rogerbarton `_ - `ropg `_ - `rscohn2 `_ - `rweickelt `_ - `SylvainCorlay `_ - `t-b `_ - `Tiwalun `_ - `utzig `_ - `vermeeren `_ - `vitaut `_ - `xuhongxu96 `_ And many more via issues and suggestions. ================================================ FILE: LICENSE ================================================ // BSD license, modified to remove the organisation as there isn't one. Copyright (c) 2009, Michael Jones All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * The names of its contributors may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ================================================ FILE: MANIFEST.in ================================================ recursive-include xml_parser_generator *.py *.in *.json include requirements/*.txt tests/*.py graft tests/data exclude breathe/_parser.py global-exclude *.py[cod] ================================================ FILE: Makefile ================================================ RM = rm -f GENERATED_MOD = breathe/_parser.py .PHONY: all all: html pdf .PHONY: html html: data $(MAKE) -C documentation html .PHONY: pdf pdf: data $(MAKE) -C documentation latexpdf .PHONY: data data: $(MAKE) -C examples/doxygen all $(MAKE) -C examples/tinyxml all $(MAKE) -C examples/specific all $(GENERATED_MOD): \ xml_parser_generator/schema.json \ xml_parser_generator/module_template.py.in \ xml_parser_generator/make_parser.py python3 xml_parser_generator/setuptools_builder.py .PHONY: parser parser: $(GENERATED_MOD) .PHONY: format-parser format-parser: ruff format $(GENERATED_MOD) .PHONY: distclean distclean: clean $(MAKE) -C documentation clean $(RM) $(GENERATED_MOD) .PHONY: clean clean: $(MAKE) -C examples/doxygen $@ $(MAKE) -C examples/tinyxml $@ $(MAKE) -C examples/specific $@ .PHONY: test test: $(GENERATED_MOD) cd tests && python3 -m pytest -v --maxfail=1 .PHONY: dev-test dev-test: $(GENERATED_MOD) cd tests && PYTHONPATH=../:$(PYTHONPATH) python3 -m pytest -v .PHONY: ruff ruff: ruff check ruff format .PHONY: type-check type-check: $(GENERATED_MOD) mypy --warn-redundant-casts --warn-unused-ignores breathe tests ================================================ FILE: README.rst ================================================ .. image:: https://www.breathe-doc.org/img/logo.svg :align: center :alt: Breathe logo :width: 200 :height: 200 :target: https://www.breathe-doc.org Breathe ======= **Your technical docs, beautifully integrated** .. image:: https://github.com/breathe-doc/breathe/actions/workflows/unit_tests.yml/badge.svg :target: https://github.com/breathe-doc/breathe/actions/workflows/unit_tests.yml :alt: Build Status Website_ • Documentation_ • Sponsor_ **Sponsor**: If you benefit from using Breathe as a company or an individual, you can financially support the Breathe project with recurring or one off contributions via `Open Collective `_. .. _Website: https://www.breathe-doc.org/ .. _Documentation: https://breathe.readthedocs.io/en/latest/ .. _Sponsor: https://opencollective.com/breathe ---- Breathe is a Sphinx plugin providing beautifully integrated Doxygen output in your user-facing documentation. It allows you to combine Doxygen's excellent technical understanding of your code base with the superb long form documentation output of the Sphinx system. For Packagers ------------- - Breathe packages on PyPI are PGP signed for Breathe >= v4.28.0. - Breathe tarballs on GitHub are PGP signed for Breathe >= v4.29.0. Download -------- Breathe is available from github and `PyPI, the Python Package Index `_. It can be installed with:: pip install breathe Documentation ------------- The documentation is available `here `__. Thank you to the people running `Read the Docs `_ for such an excellent service. The source for the documentation is in the ``documentation`` folder if you want to built it and read it locally. Testing ------- The testsuite can be run with:: make dev-test The documentation also does a good effort of covering the available functionality with different examples. To build the documentation, run:: make This will run doxygen over the example code and then run the Breathe documentation. View the results at:: documentation/build/html/index.html Further to this if you want to compare the current documentation output against a previous state in order to check for regressions there is a ``compare`` script in the ``documentation`` folder. It takes two arguments which are two commit references that you'd like to compare. This means that all your changes have to be committed first. Also the script does not resolve state dependent references like ``HEAD`` so provide concrete commit references like sha1s or branch names. A typical example is to compare your current branch output to master:: # Make sure all your changes are committed first cd documentation ./compare master my-branch This will do a checkout and build at each commit and then run ``meld`` against the resulting directories so you can see the differences introduced by your branch. Requirements ------------ Breathe requires Python 3.9+, Sphinx 7.2+, and Doxygen 1.9.2+. Mailing List Archives --------------------- The archive for the Google groups list can be found `here `__. The previous mailing list was on `librelist.com `__ and the archives are available `here `__. Please post new questions as GitHub issues. Projects Using Breathe ---------------------- Examples of projects that use Breathe: - `PyTorch `_ - `OpenPilot `_ - `XGBoost `_ - `NumPy `_ - `Mozilla's DeepSpeech `_ - `Microsoft's LightGBM `_ - `PyBind11 `_ - `Ceph `_ - `Apache Arrow `_ - `LVGL `_ - `Espressif IoT Development Framework `_ - `Zephyr Project `_ - `Plaid ML `_ - `Sony's Neural Network Libraries `_ - `fmt `_ Release ------- 1. Update `CHANGELOG.rst` and create the git tag (`vX.Y.Z`). 2. Push the tag to GitHub. 3. The `create-release.yml` workflow will publish the release to PyPI. 4. Go to https://github.com/breathe-doc/breathe/tags, select the new tag, and click the "Create release from tag" button to publish a GitHub release. Maintainers ----------- Breathe is currently maintained by `vermeeren `_ & `jakobandersen `_ and was formerly maintained by `michaeljones `_ & `vitaut `_. See `CONTRIBUTORS `_ for the full list. Acknowledgements ---------------- - Dimitri van Heesch for `Doxygen `_. - Georg Brandl for `Sphinx `_. - David Goodger for `Docutils `_ and reStructuredText. Changelog --------- See the `CHANGELOG.rst `_ ================================================ FILE: breathe/README.rst ================================================ breathe ======= - **Subpackages** - **directive** - Contains some rst directive definitions. These were split out of `directives.py` when it started to become too large. - **finder** - Provides classes for finding nodes within the set of xml files generated by doxygen. Finders are generally used in the `run` methods of the directives to find the xml node which is then passed to the renderer to create the output. - **renderer** - Provides classes for rendering an xml node from the doxygen xml into docutils rst nodes which can be used by Sphinx. Breathe should ideally only produce rst nodes and not concern itself with the final output (html, latex, etc.) - **Submodules** - **directives** - Contains the definitions of some of the directives. The rest are in the files in the `directive` folder. It also contains all the set up code which registers with Sphinx and wires together all the various factories. - **parser** - Contains code for parsing the doxygen xml into a tree of Python objects. Most of its content is imported from `_parser`, which is generated automatically when Breathe is built. - **process** - Contains the code responsible for running the `doxygen` process when using the `autodoxygen` directives. - **project** - Handles the concept of a `Project` which is the breathe term for a folder full of doxygen xml files. - **exception** - Contains the base class for all custom exceptions. ================================================ FILE: breathe/__init__.py ================================================ from __future__ import annotations from typing import TYPE_CHECKING if TYPE_CHECKING: from sphinx.application import Sphinx __version__ = "5.0.0a5" def setup(app: Sphinx): from breathe.directives.setup import setup as directive_setup from breathe.file_state_cache import setup as file_state_cache_setup from breathe.renderer.sphinxrenderer import setup as renderer_setup directive_setup(app) file_state_cache_setup(app) renderer_setup(app) return {"version": __version__, "parallel_read_safe": True, "parallel_write_safe": True} ================================================ FILE: breathe/apidoc.py ================================================ """ breathe.apidoc ~~~~~~~~~~~~~~ Parses doxygen XML tree looking for C/C++ modules and creates ReST files appropriately to create code documentation with Sphinx. It also creates a modules index (See TYPEDICT below.). This is derived from the "sphinx-autopackage" script, which is: Copyright 2008 Société des arts technologiques (SAT), http://www.sat.qc.ca/ :copyright: Originally by Sphinx Team, C++ modifications by Tatsuyuki Ishi :license: BSD, see LICENSE for details. """ from __future__ import annotations import argparse import os import sys import xml.etree.ElementTree from pathlib import Path from breathe import __version__ # Reference: Doxygen XSD schema file, CompoundKind only # Only what breathe supports are included # Translates identifier to English TYPEDICT = { "class": "Class", "interface": "Interface", "struct": "Struct", "union": "Union", "file": "File", "namespace": "Namespace", "group": "Group", } # Types that accept the :members: option. MEMBERS_TYPES = ["class", "group", "interface", "namespace", "struct"] def print_info(msg, args): if not args.quiet: print(msg) def write_file(name, text, args): """Write the output file for module/package .""" fname = Path(args.destdir, f"{name}.{args.suffix}") if args.dryrun: print_info("Would create file %s." % fname, args) return if not args.force and fname.is_file(): print_info("File %s already exists, skipping." % fname, args) else: print_info("Creating file %s." % fname, args) fname.parent.mkdir(parents=True, exist_ok=True) try: orig = fname.read_text(encoding="utf-8") if orig == text: print_info("File %s up to date, skipping." % fname, args) return except FileNotFoundError: # Don't mind if it isn't there pass fname.write_text(text, encoding="utf-8") def format_heading(level, text): """Create a heading of [1, 2 or 3 supported].""" underlining = ["=", "-", "~"][level - 1] * len(text) return "%s\n%s\n\n" % (text, underlining) def format_directive(package_type, package, args): """Create the breathe directive and add the options.""" directive = ".. doxygen%s:: %s\n" % (package_type, package) if args.project: directive += " :project: %s\n" % args.project if args.members and package_type in MEMBERS_TYPES: directive += " :members:\n" return directive def create_package_file(package, package_type, package_id, args): """Build the text of the file and write the file.""" # Skip over types that weren't requested if package_type not in args.outtypes: return text = format_heading(1, "%s %s" % (TYPEDICT[package_type], package)) text += format_directive(package_type, package, args) write_file(Path(package_type, package_id), text, args) def create_modules_toc_file(key, value, args): """Create the module's index.""" if not Path(args.destdir, key).is_dir(): return text = format_heading(1, "%s list" % value) text += ".. toctree::\n" text += " :glob:\n\n" text += " %s/*\n" % key write_file("%slist" % key, text, args) def recurse_tree(args): """ Look for every file in the directory tree and create the corresponding ReST files. """ index = xml.etree.ElementTree.parse(Path(args.rootpath, "index.xml")) # Assuming this is a valid Doxygen XML for compound in index.getroot(): create_package_file( compound.findtext("name"), compound.get("kind"), compound.get("refid"), args ) class TypeAction(argparse.Action): def __init__(self, option_strings, dest, **kwargs): super().__init__(option_strings, dest, **kwargs) self.default = TYPEDICT.keys() self.metavar = ",".join(TYPEDICT.keys()) def __call__(self, parser, namespace, values, option_string=None): assert isinstance(values, str) value_list = values.split(",") for value in value_list: if value not in TYPEDICT: raise ValueError("%s not a valid option" % value) setattr(namespace, self.dest, value_list) def main(): """Parse and check the command line arguments.""" parser = argparse.ArgumentParser( description="""\ Parse XML created by Doxygen in and create one reST file with breathe generation directives per definition in the . Note: By default this script will not overwrite already created files.""", formatter_class=argparse.RawDescriptionHelpFormatter, ) parser.add_argument( "-o", "--output-dir", action="store", dest="destdir", help="Directory to place all output", required=True, ) parser.add_argument( "-f", "--force", action="store_true", dest="force", help="Overwrite existing files" ) parser.add_argument( "-m", "--members", action="store_true", dest="members", help="Include members for types: %s" % MEMBERS_TYPES, ) parser.add_argument( "-n", "--dry-run", action="store_true", dest="dryrun", help="Run the script without creating files", ) parser.add_argument( "-T", "--no-toc", action="store_true", dest="notoc", help="Don't create a table of contents file", ) parser.add_argument( "-s", "--suffix", action="store", dest="suffix", help="file suffix (default: rst)", default="rst", ) parser.add_argument( "-p", "--project", action="store", dest="project", help="project to add to generated directives", ) parser.add_argument( "-g", "--generate", action=TypeAction, dest="outtypes", help="types of output to generate, comma-separated list", ) parser.add_argument( "-q", "--quiet", action="store_true", dest="quiet", help="suppress informational messages" ) parser.add_argument( "--version", action="version", version="Breathe (breathe-apidoc) %s" % __version__ ) parser.add_argument("rootpath", type=str, help="The directory contains index.xml") args = parser.parse_args() args.suffix = args.suffix.removeprefix(".") if not os.path.isdir(args.rootpath): print("%s is not a directory." % args.rootpath, file=sys.stderr) sys.exit(1) if "index.xml" not in os.listdir(args.rootpath): print("%s does not contain a index.xml" % args.rootpath, file=sys.stderr) sys.exit(1) if not os.path.isdir(args.destdir): if not args.dryrun: os.makedirs(args.destdir) args.rootpath = os.path.abspath(args.rootpath) recurse_tree(args) if not args.notoc: for key in args.outtypes: create_modules_toc_file(key, TYPEDICT[key], args) # So program can be started with "python -m breathe.apidoc ..." if __name__ == "__main__": main() ================================================ FILE: breathe/cpp_util.py ================================================ from __future__ import annotations import re from typing import TYPE_CHECKING if TYPE_CHECKING: from collections.abc import Iterator RE_NAME_PART = re.compile(r"([<>()[\]]|::)") def _check_pair(dest: list[str], tokens: Iterator[str], start: str, end: str) -> bool: if dest[-1] == start: for tok in tokens: dest.append(tok) if tok == end: break # If we're inside angle brackets, we assume "<" and ">" are brackets # and not comparison operators. Once we're inside other brackets, we # only need to worry about recursive brackets and can ignore the # other types. if start == "<": _check_all_pairs(dest, tokens) else: _check_pair(dest, tokens, start, end) return True return False def _check_all_pairs(dest: list[str], tokens: Iterator[str]) -> None: if not _check_pair(dest, tokens, "<", ">"): if not _check_pair(dest, tokens, "(", ")"): if not _check_pair(dest, tokens, "[", "]"): _check_pair(dest, tokens, "{", "}") def split_name(name: str) -> list[str]: """Split a qualified C++ name into the namespace components. E.g. turn "A::D::E<(F>G::H),(I" into ["A","D","E<(F>G::H),(I"] This can produce incorrect results if any of the template parameters are strings containing brackets. """ last: list[str] = [] parts = [last] tokens = iter(RE_NAME_PART.split(name)) for tok in tokens: if tok == "::": last = [] parts.append(last) else: last.append(tok) _check_all_pairs(last, tokens) return ["".join(subparts) for subparts in parts] ================================================ FILE: breathe/directives/__init__.py ================================================ from __future__ import annotations from typing import TYPE_CHECKING from docutils import nodes from sphinx.directives import SphinxDirective from breathe import parser from breathe.finder import factory from breathe.renderer import RenderContext, format_parser_error from breathe.renderer.sphinxrenderer import SphinxRenderer if TYPE_CHECKING: from collections.abc import Sequence from typing import Any from sphinx.application import Sphinx from breathe.parser import DoxygenParser from breathe.project import ProjectInfo, ProjectInfoFactory from breathe.renderer import TaggedNode from breathe.renderer.filter import DoxFilter from breathe.renderer.mask import MaskFactoryBase from breathe.renderer.target import TargetHandler class _WarningHandler: def __init__(self, state, context: dict[str, Any]) -> None: self.state = state self.context = context def warn( self, raw_text: str, *, rendered_nodes: Sequence[nodes.Node] | None = None, unformatted_suffix: str = "", ) -> list[nodes.Node]: raw_text = self.format(raw_text) + unformatted_suffix if rendered_nodes is None: rendered_nodes = [nodes.paragraph("", "", nodes.Text(raw_text))] return [ nodes.warning("", *rendered_nodes), self.state.document.reporter.warning(raw_text, line=self.context["lineno"]), ] def format(self, text: str) -> str: return text.format(**self.context) class BaseDirective(SphinxDirective): @property def directive_args(self) -> list: # the order must be the same as in docutils.parsers.rst.Directive.__init__ return [ self.name, self.arguments, self.options, self.content, self.lineno, self.content_offset, self.block_text, self.state, self.state_machine, ] @property def project_info_factory(self) -> ProjectInfoFactory: return self.env.temp_data["breathe_project_info_factory"] @property def dox_parser(self) -> DoxygenParser: return self.env.temp_data["breathe_dox_parser"] @property def app(self) -> Sphinx: return self.env.app def get_doxygen_index(self, project_info: ProjectInfo) -> parser.DoxygenIndex: return self.dox_parser.parse_index(project_info) def create_finder_from_root( self, root: factory.FinderRoot, project_info: ProjectInfo ) -> factory.Finder: return factory.create_finder_from_root(self.env.app, self.dox_parser, root, project_info) def create_warning(self, project_info: ProjectInfo | None, **kwargs) -> _WarningHandler: if project_info: proj_name = project_info.name() proj_path = project_info.project_path() tail = f'in doxygen xml output for project "{proj_name}" from directory: {proj_path}' else: tail = "" context = dict(lineno=self.lineno, tail=tail, **kwargs) return _WarningHandler(self.state, context) def render( self, node_stack: list[TaggedNode], project_info: ProjectInfo, filter_: DoxFilter, target_handler: TargetHandler, mask_factory: MaskFactoryBase, directive_args, ) -> list[nodes.Node]: "Standard render process used by subclasses" try: object_renderer = SphinxRenderer( self.dox_parser.app, project_info, [tn.value for tn in node_stack], self.state, self.state.document, target_handler, self.dox_parser, filter_, ) except parser.ParserError as e: return format_parser_error( "doxygenclass", e.message, e.filename, self.state, self.lineno, True ) except parser.FileIOError as e: return format_parser_error( "doxygenclass", e.error, e.filename, self.state, self.lineno, True ) context = RenderContext(node_stack, mask_factory, directive_args) node = node_stack[0].value assert isinstance(node, parser.Node) return object_renderer.render(node, context) ================================================ FILE: breathe/directives/class_like.py ================================================ from __future__ import annotations from typing import TYPE_CHECKING, cast from docutils.parsers.rst.directives import flag, unchanged, unchanged_required from breathe.directives import BaseDirective from breathe.file_state_cache import MTimeError from breathe.project import ProjectError from breathe.renderer import filter from breathe.renderer.mask import NullMaskFactory from breathe.renderer.target import create_target_handler if TYPE_CHECKING: import sys from typing import ClassVar if sys.version_info >= (3, 11): from typing import NotRequired, TypedDict else: from typing_extensions import NotRequired, TypedDict from docutils.nodes import Node from breathe.project import ProjectOptions DoxClassOptions = TypedDict( "DoxClassOptions", { "path": str, "project": str, "members": NotRequired[str], "membergroups": str, "members-only": NotRequired[None], "protected-members": NotRequired[None], "private-members": NotRequired[None], "undoc-members": NotRequired[None], "show": str, "outline": NotRequired[None], "no-link": NotRequired[None], "allow-dot-graphs": NotRequired[None], }, ) else: DoxClassOptions = None ProjectOptions = None class _DoxygenClassLikeDirective(BaseDirective): kind: ClassVar[str] required_arguments = 1 optional_arguments = 0 final_argument_whitespace = True option_spec = { "path": unchanged_required, "project": unchanged_required, "members": unchanged, "membergroups": unchanged_required, "members-only": flag, "protected-members": flag, "private-members": flag, "undoc-members": flag, "show": unchanged_required, "outline": flag, "no-link": flag, "allow-dot-graphs": flag, } has_content = False def run(self) -> list[Node]: name = self.arguments[0] options = cast("DoxClassOptions", self.options) try: project_info = self.project_info_factory.create_project_info( cast("ProjectOptions", options) ) except ProjectError as e: warning = self.create_warning(None, kind=self.kind) return warning.warn("doxygen{kind}: %s" % e) try: d_index = self.get_doxygen_index(project_info) except MTimeError as e: warning = self.create_warning(None, kind=self.kind) return warning.warn("doxygen{kind}: %s" % e) matches: list[filter.FinderMatch] = list( filter.compound_finder_filter(name, self.kind, d_index) ) if len(matches) == 0: warning = self.create_warning(project_info, name=name, kind=self.kind) return warning.warn('doxygen{kind}: Cannot find class "{name}" {tail}') target_handler = create_target_handler(options, self.env) filter_ = filter.create_class_filter(self.app, name, options) mask_factory = NullMaskFactory() return self.render( matches[0], project_info, filter_, target_handler, mask_factory, self.directive_args ) class DoxygenClassDirective(_DoxygenClassLikeDirective): kind = "class" class DoxygenStructDirective(_DoxygenClassLikeDirective): kind = "struct" class DoxygenInterfaceDirective(_DoxygenClassLikeDirective): kind = "interface" ================================================ FILE: breathe/directives/content_block.py ================================================ from __future__ import annotations from typing import TYPE_CHECKING, cast from docutils.parsers.rst.directives import flag, unchanged_required from breathe import parser from breathe.directives import BaseDirective from breathe.file_state_cache import MTimeError from breathe.project import ProjectError from breathe.renderer import RenderContext, filter from breathe.renderer.mask import NullMaskFactory from breathe.renderer.sphinxrenderer import SphinxRenderer from breathe.renderer.target import create_target_handler if TYPE_CHECKING: import sys from typing import ClassVar, Literal if sys.version_info >= (3, 11): from typing import NotRequired, TypedDict else: from typing_extensions import NotRequired, TypedDict from docutils.nodes import Node from sphinx.application import Sphinx from breathe.finder.factory import FinderRoot from breathe.project import ProjectOptions DoxContentBlockOptions = TypedDict( "DoxContentBlockOptions", { "path": str, "project": str, "content-only": NotRequired[None], "members": NotRequired[str], "protected-members": NotRequired[None], "private-members": NotRequired[None], "undoc-members": NotRequired[None], "show": str, "outline": NotRequired[None], "no-link": NotRequired[None], "desc-only": NotRequired[None], "sort": NotRequired[None], }, ) else: DoxContentBlockOptions = None ProjectOptions = None FinderRoot = None def create_render_filter( app: Sphinx, kind: Literal["group", "page", "namespace"], options: DoxContentBlockOptions ) -> filter.DoxFilter: """Render filter for group & namespace blocks""" filter_options = filter.set_defaults(app, options) if "desc-only" in filter_options: return filter.create_description_filter(True, parser.Node_compounddefType) cm_filter = filter.create_class_member_filter(filter_options) ic_filter = filter.create_innerclass_filter(filter_options) o_filter = filter.create_outline_filter(filter_options) def filter_(nstack: filter.NodeStack) -> bool: grandparent = nstack.ancestor(2) return ( ( cm_filter(nstack) or ( isinstance(grandparent, parser.Node_compounddefType) and grandparent.kind not in filter.CLASS_LIKE_COMPOUNDDEF and isinstance(nstack.node, parser.Node_memberdefType) ) ) and ic_filter(nstack) and o_filter(nstack) ) return filter_ def create_content_filter(kind: Literal["group", "page", "namespace"]) -> filter.DoxFilter: """Returns a filter which matches the contents of the or namespace but not the group or namepace name or description. This allows the groups to be used to structure sections of the documentation rather than to structure and further document groups of documentation As a finder/content filter we only need to match exactly what we're interested in. """ def filter_(nstack: filter.NodeStack) -> bool: node = nstack.node parent = nstack.parent if isinstance(node, parser.Node_memberdefType): return node.prot == parser.DoxProtectionKind.public return ( isinstance(node, parser.Node_refType) and isinstance(parent, parser.Node_compounddefType) and parent.kind.value == kind and nstack.tag == "innerclass" and node.prot == parser.DoxProtectionKind.public ) return filter_ class _DoxygenContentBlockDirective(BaseDirective): """Base class for namespace and group directives which have very similar behaviours""" kind: ClassVar[Literal["group", "page", "namespace"]] required_arguments = 1 optional_arguments = 1 option_spec = { "path": unchanged_required, "project": unchanged_required, "content-only": flag, "outline": flag, "members": flag, "protected-members": flag, "private-members": flag, "undoc-members": flag, "no-link": flag, "desc-only": flag, "sort": flag, } has_content = False def run(self) -> list[Node]: name = self.arguments[0] options = cast("DoxContentBlockOptions", self.options) try: project_info = self.project_info_factory.create_project_info( cast("ProjectOptions", options) ) except ProjectError as e: warning = self.create_warning(None, kind=self.kind) return warning.warn("doxygen{kind}: %s" % e) try: d_index = self.get_doxygen_index(project_info) except MTimeError as e: warning = self.create_warning(None, kind=self.kind) return warning.warn("doxygen{kind}: %s" % e) matches: list[filter.FinderMatch] = list( filter.compound_finder_filter(name, self.kind, d_index) ) # It shouldn't be possible to have too many matches as namespaces & groups in their nature # are merged together if there are multiple declarations, so we only check for no matches if not matches: warning = self.create_warning(project_info, name=name, kind=self.kind) return warning.warn('doxygen{kind}: Cannot find {kind} "{name}" {tail}') if "content-only" in options and self.kind != "page": # Unpack the single entry in the matches list (node_stack,) = matches filter_ = create_content_filter(self.kind) # Having found the compound node for the namespace or group in the index we want to grab # the contents of it which match the filter contents_finder = self.create_finder_from_root( cast("FinderRoot", node_stack[0].value), project_info ) contents: list[filter.FinderMatch] = [] contents_finder.filter_(filter_, contents) # Replaces matches with our new starting points matches = contents target_handler = create_target_handler(options, self.env) filter_ = create_render_filter(self.app, self.kind, options) node_list: list[Node] = [] for node_stack in matches: object_renderer = SphinxRenderer( self.dox_parser.app, project_info, [item.value for item in node_stack], self.state, self.state.document, target_handler, self.dox_parser, filter_, ) mask_factory = NullMaskFactory() context = RenderContext(node_stack, mask_factory, self.directive_args) value = context.node_stack[0].value assert isinstance(value, parser.Node) node_list.extend(object_renderer.render(value, context)) return node_list class DoxygenNamespaceDirective(_DoxygenContentBlockDirective): kind = "namespace" class DoxygenGroupDirective(_DoxygenContentBlockDirective): kind = "group" option_spec = _DoxygenContentBlockDirective.option_spec.copy() option_spec.update({ "inner": flag, "no-title": flag, }) class DoxygenPageDirective(_DoxygenContentBlockDirective): kind = "page" option_spec = { "path": unchanged_required, "project": unchanged_required, "content-only": flag, "no-title": flag, } ================================================ FILE: breathe/directives/file.py ================================================ from __future__ import annotations import os.path from typing import TYPE_CHECKING from docutils.parsers.rst.directives import flag, unchanged_required from breathe import parser, path_handler, project, renderer from breathe.cpp_util import split_name from breathe.directives import BaseDirective from breathe.renderer import filter from breathe.renderer.mask import NullMaskFactory from breathe.renderer.sphinxrenderer import SphinxRenderer from breathe.renderer.target import create_target_handler if TYPE_CHECKING: from collections.abc import Iterable, Mapping from typing import Any, ClassVar from docutils.nodes import Node def path_matches(location: str, target_file: str) -> bool: if path_handler.includes_directory(target_file): # If the target_file contains directory separators then # match against the same length at the end of the location # location_match = location[-len(target_file) :] return location_match == target_file # If there are no separators, match against the whole filename # at the end of the location # # This is to prevent "Util.cpp" matching "PathUtil.cpp" # location_basename = os.path.basename(location) return location_basename == target_file def location_matches(location: parser.Node_locationType | None, target_file: str) -> bool: return location is not None and path_matches(location.file, target_file) def namespace_matches(name: str, node: parser.Node_compounddefType): to_find = "::".join(split_name(name)[:-1]) return any(to_find == "".join(ns) for ns in node.innernamespace) or any( to_find == "".join(ns) for ns in node.innerclass ) def create_file_filter( filename: str, options: Mapping[str, Any], *, init_valid_names: Iterable[str] | None = None, ) -> filter.DoxFilter: valid_names: set[str] = set() if init_valid_names: valid_names.update(init_valid_names) outline_filter = filter.create_outline_filter(options) def filter_(nstack: filter.NodeStack) -> bool: if not outline_filter(nstack): return False node = nstack.node parent = nstack.parent if isinstance(node, parser.Node_compounddefType): if node.kind == parser.DoxCompoundKind.file: # Gather the "namespaces" attribute from the # compounddef for the file we're rendering and # store the information in the "valid_names" list if location_matches(node.location, filename): valid_names.update("".join(ns) for ns in node.innernamespace) valid_names.update("".join(ns) for ns in node.innerclass) if node.kind != parser.DoxCompoundKind.namespace: # Ignore compounddefs which are from another file # (normally means classes and structs which are in a # namespace that we have other interests in) but only # check it if the compounddef is not a namespace # itself, as for some reason compounddefs for # namespaces are registered with just a single file # location even if they namespace is spread over # multiple files return location_matches(node.location, filename) elif isinstance(node, parser.Node_refType): name = "".join(node) if isinstance(parent, parser.Node_compounddefType) and nstack.tag in { "innerclass", "innernamespace", }: # Take the valid_names and every time we handle an # innerclass or innernamespace, check that its name # was one of those initial valid names so that we # never end up rendering a namespace or class that # wasn't in the initial file. Notably this is # required as the location attribute for the # namespace in the xml is unreliable. if name not in valid_names: return False # Ignore innerclasses and innernamespaces that are inside a # namespace that is going to be rendered as they will be # rendered with that namespace and we don't want them twice if namespace_matches(name, parent): return False elif isinstance(node, parser.Node_memberdefType): # Ignore memberdefs from files which are different to # the one we're rendering. This happens when we have to # cross into a namespace xml file which has entries # from multiple files in it return path_matches(node.location.file, filename) return True return filter_ def file_finder_filter( filename: str, d_parser: parser.DoxygenParser, project_info: project.ProjectInfo, index: parser.DoxygenIndex, matches: list[filter.FinderMatch], ) -> None: for c in index.file_compounds: if not path_matches(c.name, filename): continue for cd in d_parser.parse_compound(c.refid, project_info).root.compounddef: if cd.kind != parser.DoxCompoundKind.file: continue matches.append([renderer.TaggedNode(None, cd)]) class _BaseFileDirective(BaseDirective): """Base class handle the main work when given the appropriate file and project info to work from. """ directive_name: ClassVar[str] # We use inheritance here rather than a separate object and composition, because so much # information is present in the Directive class from the docutils framework that we'd have to # pass way too much stuff to a helper object to be reasonable. def handle_contents(self, file_: str, project_info: project.ProjectInfo) -> list[Node]: d_index = self.get_doxygen_index(project_info) matches: list[filter.FinderMatch] = [] file_finder_filter(file_, self.dox_parser, project_info, d_index, matches) if len(matches) > 1: warning = self.create_warning(None, file=file_, directivename=self.directive_name) return warning.warn('{directivename}: Found multiple matches for file "{file} {tail}') elif not matches: warning = self.create_warning(None, file=file_, directivename=self.directive_name) return warning.warn('{directivename}: Cannot find file "{file} {tail}') target_handler = create_target_handler(self.options, self.env) filter_ = create_file_filter(file_, self.options) node_list: list[Node] = [] for node_stack in matches: object_renderer = SphinxRenderer( self.dox_parser.app, project_info, [tv.value for tv in node_stack], self.state, self.state.document, target_handler, self.dox_parser, filter_, ) mask_factory = NullMaskFactory() context = renderer.RenderContext(node_stack, mask_factory, self.directive_args) value = node_stack[0].value assert isinstance(value, parser.Node) node_list.extend(object_renderer.render(value, context)) return node_list class DoxygenFileDirective(_BaseFileDirective): directive_name = "doxygenfile" required_arguments = 0 optional_arguments = 3 option_spec = { "path": unchanged_required, "project": unchanged_required, "outline": flag, "no-link": flag, "allow-dot-graphs": flag, "sections": unchanged_required, } has_content = False def run(self): """Get the file from the argument and the project info from the factory.""" file_ = self.arguments[0] try: project_info = self.project_info_factory.create_project_info(self.options) except project.ProjectError as e: warning = self.create_warning(None) return warning.warn("doxygenfile: %s" % e) return self.handle_contents(file_, project_info) class AutoDoxygenFileDirective(_BaseFileDirective): directive_name = "autodoxygenfile" required_arguments = 1 option_spec = { "project": unchanged_required, "outline": flag, "no-link": flag, "allow-dot-graphs": flag, "sections": unchanged_required, } has_content = False def run(self): """Get the file from the argument and extract the associated project info for the named project given that it is an auto-project. """ file_ = self.arguments[0] try: project_info = self.project_info_factory.retrieve_project_info_for_auto(self.options) except project.ProjectError as e: warning = self.create_warning(None) return warning.warn("autodoxygenfile: %s" % e) return self.handle_contents(file_, project_info) ================================================ FILE: breathe/directives/function.py ================================================ from __future__ import annotations import re from typing import TYPE_CHECKING, cast from docutils import nodes from docutils.parsers.rst.directives import flag, unchanged_required from sphinx.domains import cpp from breathe import parser from breathe.directives import BaseDirective from breathe.exception import BreatheError from breathe.file_state_cache import MTimeError from breathe.project import ProjectError from breathe.renderer import RenderContext, filter, mask from breathe.renderer.sphinxrenderer import SphinxRenderer, WithContext from breathe.renderer.target import create_target_handler if TYPE_CHECKING: import sys from types import ModuleType if sys.version_info >= (3, 11): from typing import NotRequired, TypedDict else: from typing_extensions import NotRequired, TypedDict from docutils.nodes import Node from sphinx.application import Sphinx from breathe import project from breathe.project import ProjectOptions from breathe.renderer import TaggedNode cppast: ModuleType DoxFunctionOptions = TypedDict( "DoxFunctionOptions", {"path": str, "project": str, "outline": NotRequired[None], "no-link": NotRequired[None]}, ) else: DoxFunctionOptions = None try: from sphinx.domains.cpp import _ast as cppast except ImportError: cppast = cpp class _NoMatchingFunctionError(BreatheError): pass class _UnableToResolveFunctionError(BreatheError): def __init__(self, signatures: list[str]) -> None: self.signatures = signatures def function_and_all_friend_finder_filter( app: Sphinx, namespace: str, name: str, d_parser: parser.DoxygenParser, project_info: project.ProjectInfo, index: parser.DoxygenIndex, matches: list[filter.FinderMatch], ) -> None: for f_match in filter.member_finder_filter( app, namespace, name, d_parser, project_info, (parser.MemberKind.function, parser.MemberKind.friend), index, ): cd = f_match[2].value assert isinstance(cd, parser.Node_compounddefType) matches.append(f_match) class DoxygenFunctionDirective(BaseDirective): required_arguments = 1 option_spec = { "path": unchanged_required, "project": unchanged_required, "outline": flag, "no-link": flag, } has_content = False final_argument_whitespace = True def run(self) -> list[Node]: # Extract namespace, function name, and parameters # Regex explanation: # 1. (?:::)? # Optional namespace prefix, including template arguments if a specialization. # The is group 1: # 1. [^:(<]+, basically an identifier # definitely not a scope operator, ::, or template argument list, < # 2. (?:::[^:(<]+)*, (?:) for anon match group, # so a namespace delimiter and then another identifier # 3. ::, another namespace delimiter before the function name # 2. ([^(]+), group 2, the function name, whatever remains after the optional prefix, # until a (. # 3. (.*), group 3, the parameters. # Note: for template argument lists, the spacing is important for the Doxygen lookup. # TODO: we should really do this parsing differently, e.g., using the Sphinx C++ domain. # TODO: the Doxygen lookup should not be whitespace sensitive. match = re.match(r"(?:([^:(<]+(?:::[^:(<]+)*)::)?([^(]+)(.*)", self.arguments[0]) assert match is not None # TODO: this is probably not appropriate, for now it fixes typing namespace = (match.group(1) or "").strip() function_name = match.group(2).strip() argsStr = match.group(3) options = cast("DoxFunctionOptions", self.options) try: project_info = self.project_info_factory.create_project_info( cast("ProjectOptions", options) ) except ProjectError as e: warning = self.create_warning(None) return warning.warn("doxygenfunction: %s" % e) try: d_index = self.get_doxygen_index(project_info) except MTimeError as e: warning = self.create_warning(None) return warning.warn("doxygenfunction: %s" % e) # Extract arguments from the function name. try: args = self._parse_args(argsStr) except cpp.DefinitionError as e: return self.create_warning( project_info, namespace="%s::" % namespace if namespace else "", function=function_name, args=argsStr, cpperror=str(e), ).warn( "doxygenfunction: Unable to resolve function " '"{namespace}{function}" with arguments "{args}".\n' "Could not parse arguments. Parsing error is\n{cpperror}" ) matchesAll: list[filter.FinderMatch] = [] function_and_all_friend_finder_filter( self.app, namespace, function_name, self.dox_parser, project_info, d_index, matchesAll ) matches: list[filter.FinderMatch] = [] for m in matchesAll: # only take functions and friend functions # ignore friend classes node = m[0].value assert isinstance(node, parser.Node_memberdefType) if node.kind == "friend" and not node.argsstring: continue matches.append(m) # Create it ahead of time as it is cheap and it is ugly to declare it for both exception # clauses below warning = self.create_warning( project_info, namespace="%s::" % namespace if namespace else "", function=function_name, args=str(args), ) try: node_stack = self._resolve_function(matches, args, project_info) except _NoMatchingFunctionError: return warning.warn( 'doxygenfunction: Cannot find function "{namespace}{function}" {tail}' ) except _UnableToResolveFunctionError as error: message = ( "doxygenfunction: Unable to resolve function " '"{namespace}{function}" with arguments {args} {tail}.\n' "Potential matches:\n" ) text = "" for i, entry in enumerate(sorted(error.signatures)): text += "- %s\n" % entry block = nodes.literal_block("", "", nodes.Text(text)) formatted_message = warning.format(message) warning_nodes = [nodes.paragraph("", "", nodes.Text(formatted_message)), block] result = warning.warn(message, rendered_nodes=warning_nodes, unformatted_suffix=text) return result except cpp.DefinitionError as error: warning.context["cpperror"] = str(error) return warning.warn( "doxygenfunction: Unable to resolve function " '"{namespace}{function}" with arguments "{args}".\n' "Candidate function could not be parsed. Parsing error is\n{cpperror}" ) target_handler = create_target_handler(options, self.env) filter_ = filter.create_outline_filter(options) return self.render( node_stack, project_info, filter_, target_handler, mask.NullMaskFactory(), self.directive_args, ) def _parse_args(self, function_description: str) -> cppast.ASTParametersQualifiers | None: # type: ignore[name-defined] # Note: the caller must catch cpp.DefinitionError if function_description == "": return None parser = cpp.DefinitionParser( function_description, location=self.get_source_info(), config=self.config ) paramQual = parser._parse_parameters_and_qualifiers("function") # strip everything that doesn't contribute to overloading def stripParamQual(paramQual): paramQual.exceptionSpec = None paramQual.final = None paramQual.override = None # TODO: strip attrs when Doxygen handles them paramQual.initializer = None paramQual.trailingReturn = None for p in paramQual.args: if p.arg is None: assert p.ellipsis continue p.arg.init = None declarator = p.arg.type.decl def stripDeclarator(declarator): if hasattr(declarator, "next"): stripDeclarator(declarator.next) if isinstance(declarator, cppast.ASTDeclaratorParen): assert hasattr(declarator, "inner") stripDeclarator(declarator.inner) else: assert isinstance(declarator, cppast.ASTDeclaratorNameParamQual) assert hasattr(declarator, "declId") declarator.declId = None if declarator.paramQual is not None: stripParamQual(declarator.paramQual) stripDeclarator(declarator) stripParamQual(paramQual) return paramQual def _create_function_signature( self, node_stack: list[TaggedNode], project_info, filter_, target_handler, mask_factory, directive_args, ) -> str: """Standard render process used by subclasses.""" object_renderer = SphinxRenderer( self.dox_parser.app, project_info, [tn.value for tn in node_stack], self.state, self.state.document, target_handler, self.dox_parser, filter_, ) context = RenderContext(node_stack, mask_factory, directive_args) node = node_stack[0].value with WithContext(object_renderer, context): assert isinstance(node, parser.Node_memberdefType) # this part should be kept in sync with visit_function in sphinxrenderer name = node.name # assume we are only doing this for C++ declarations declaration = " ".join([ object_renderer.create_template_prefix(node), "".join(n.astext() for n in object_renderer.render(node.type)), name, node.argsstring or "", ]) cpp_parser = cpp.DefinitionParser( declaration, location=self.get_source_info(), config=self.config ) ast = cpp_parser.parse_declaration("function", "function") return str(ast) def _resolve_function( self, matches: list[filter.FinderMatch], args: cppast.ASTParametersQualifiers | None, # type: ignore[name-defined] project_info: project.ProjectInfo, ): if not matches: raise _NoMatchingFunctionError() res = [] candSignatures = [] for entry in matches: text_options = {"no-link": "", "outline": ""} # Render the matches to docutils nodes target_handler = create_target_handler({"no-link": ""}, self.env) filter_ = filter.create_outline_filter(text_options) mask_factory = mask.MaskFactory({parser.Node_paramType: mask.no_parameter_names}) # Override the directive args for this render directive_args = self.directive_args[:] directive_args[2] = text_options signature = self._create_function_signature( entry, project_info, filter_, target_handler, mask_factory, directive_args ) candSignatures.append(signature) if args is not None: match = re.match(r"([^(]*)(.*)", signature) assert match _match_args = match.group(2) # Parse the text to find the arguments # This one should succeed as it came from _create_function_signature match_args = self._parse_args(_match_args) # Match them against the arg spec if args != match_args: continue res.append((entry, signature)) if len(res) == 1 or (len(res) > 1 and all(x[1] == res[0][1] for x in res)): return res[0][0] else: raise _UnableToResolveFunctionError(candSignatures) ================================================ FILE: breathe/directives/index.py ================================================ from __future__ import annotations from typing import TYPE_CHECKING from docutils.parsers.rst.directives import flag, unchanged_required from breathe import parser from breathe.directives import BaseDirective from breathe.project import ProjectError from breathe.renderer import RenderContext, TaggedNode, filter, format_parser_error from breathe.renderer.mask import NullMaskFactory from breathe.renderer.sphinxrenderer import SphinxRenderer from breathe.renderer.target import create_target_handler if TYPE_CHECKING: from collections.abc import Mapping from typing import Any from docutils.nodes import Node class RootDataObject: pass def create_index_filter(options: Mapping[str, Any]) -> filter.DoxFilter: outline_filter = filter.create_outline_filter(options) def filter_(nstack: filter.NodeStack) -> bool: if not outline_filter(nstack): return False node = nstack.node parent = nstack.parent return not ( isinstance(parent, parser.Node_compounddefType) and ( ( isinstance(node, parser.Node_refType) and nstack.tag in ("innerclass", "innernamespace") ) or ( parent.kind == parser.DoxCompoundKind.group and isinstance(node, parser.Node_sectiondefType) and node.kind == parser.DoxSectionKind.func ) ) ) return filter_ class _BaseIndexDirective(BaseDirective): """Base class handle the main work when given the appropriate project info to work from.""" # We use inheritance here rather than a separate object and composition, because so much # information is present in the Directive class from the docutils framework that we'd have to # pass way too much stuff to a helper object to be reasonable. def handle_contents(self, project_info) -> list[Node]: try: d_index = self.get_doxygen_index(project_info) except parser.ParserError as e: return format_parser_error( self.name, e.message, e.filename, self.state, self.lineno, True ) except parser.FileIOError as e: return format_parser_error(self.name, e.error, e.filename, self.state, self.lineno) target_handler = create_target_handler(self.options, self.env) filter_ = create_index_filter(self.options) object_renderer = SphinxRenderer( self.dox_parser.app, project_info, [d_index.root], self.state, self.state.document, target_handler, self.dox_parser, filter_, ) mask_factory = NullMaskFactory() context = RenderContext( [TaggedNode(None, d_index.root), TaggedNode(None, RootDataObject())], mask_factory, self.directive_args, ) value = context.node_stack[0].value assert isinstance(value, parser.Node) try: node_list = object_renderer.render(value, context) except parser.ParserError as e: return format_parser_error( self.name, e.message, e.filename, self.state, self.lineno, True ) except parser.FileIOError as e: return format_parser_error(self.name, e.error, e.filename, self.state, self.lineno) return node_list class DoxygenIndexDirective(_BaseIndexDirective): required_arguments = 0 optional_arguments = 2 option_spec = { "path": unchanged_required, "project": unchanged_required, "outline": flag, "no-link": flag, "allow-dot-graphs": flag, } has_content = False def run(self): """Extract the project info and pass it to the helper method""" try: project_info = self.project_info_factory.create_project_info(self.options) except ProjectError as e: warning = self.create_warning(None) return warning.warn("doxygenindex: %s" % e) return self.handle_contents(project_info) class AutoDoxygenIndexDirective(_BaseIndexDirective): required_arguments = 0 final_argument_whitespace = True option_spec = { "project": unchanged_required, "outline": flag, "no-link": flag, "allow-dot-graphs": flag, } has_content = False def run(self) -> list[Node]: """Extract the project info from the auto project info store and pass it to the helper method. """ try: project_info = self.project_info_factory.retrieve_project_info_for_auto(self.options) except ProjectError as e: warning = self.create_warning(None) return warning.warn("autodoxygenindex: %s" % e) return self.handle_contents(project_info) ================================================ FILE: breathe/directives/item.py ================================================ from __future__ import annotations from typing import TYPE_CHECKING, cast from docutils.parsers.rst.directives import flag, unchanged_required from breathe import parser from breathe.directives import BaseDirective from breathe.file_state_cache import MTimeError from breathe.project import ProjectError from breathe.renderer import TaggedNode, filter from breathe.renderer.mask import NullMaskFactory from breathe.renderer.target import create_target_handler if TYPE_CHECKING: import sys from typing import ClassVar from docutils.nodes import Node if sys.version_info >= (3, 11): from typing import NotRequired, TypedDict else: from typing_extensions import NotRequired, TypedDict from breathe.project import ProjectInfo, ProjectOptions DoxBaseItemOptions = TypedDict( "DoxBaseItemOptions", {"path": str, "project": str, "outline": NotRequired[None], "no-link": NotRequired[None]}, ) else: DoxBaseItemOptions = None def enumvalue_finder_filter( name: str, d_parser: parser.DoxygenParser, project_info: ProjectInfo, index: parser.DoxygenIndex, ) -> filter.FinderMatchItr: """Looks for an enumvalue with the specified name.""" for m, c in index.members[name]: if m.kind != parser.MemberKind.enumvalue: continue dc = d_parser.parse_compound(c.refid, project_info) ev, mdef, sdef, cdef = dc.enumvalue_by_id[m.refid] TN = TaggedNode yield [ TN("enumvalue", ev), TN("memberdef", mdef), TN("sectiondef", sdef), TN("compounddef", cdef), TN("doxygen", dc.root), TN("compound", c), TN("doxygenindex", index.root), ] class _DoxygenBaseItemDirective(BaseDirective): kind: ClassVar[str] required_arguments = 1 optional_arguments = 1 option_spec = { "path": unchanged_required, "project": unchanged_required, "outline": flag, "no-link": flag, } has_content = False def finder_filter( self, namespace: str, name: str, project_info: ProjectInfo, index: parser.DoxygenIndex, matches: list[filter.FinderMatch], ) -> None: """A filter to find the node corresponding to this item.""" matches.extend( filter.member_finder_filter( self.app, namespace, name, self.dox_parser, project_info, self.kind, index ) ) def run(self) -> list[Node]: options = cast("DoxBaseItemOptions", self.options) namespace, _, name = self.arguments[0].rpartition("::") try: project_info = self.project_info_factory.create_project_info( cast("ProjectOptions", options) ) except ProjectError as e: warning = self.create_warning(None, kind=self.kind) return warning.warn("doxygen{kind}: %s" % e) try: d_index = self.get_doxygen_index(project_info) except MTimeError as e: warning = self.create_warning(None, kind=self.kind) return warning.warn("doxygen{kind}: %s" % e) matches: list[filter.FinderMatch] = [] self.finder_filter(namespace, name, project_info, d_index, matches) if len(matches) == 0: display_name = "%s::%s" % (namespace, name) if namespace else name warning = self.create_warning(project_info, kind=self.kind, display_name=display_name) return warning.warn('doxygen{kind}: Cannot find {kind} "{display_name}" {tail}') target_handler = create_target_handler(options, self.env) filter_ = filter.create_outline_filter(options) node_stack = matches[0] mask_factory = NullMaskFactory() return self.render( node_stack, project_info, filter_, target_handler, mask_factory, self.directive_args ) class DoxygenVariableDirective(_DoxygenBaseItemDirective): kind = "variable" class DoxygenDefineDirective(_DoxygenBaseItemDirective): kind = "define" class DoxygenConceptDirective(_DoxygenBaseItemDirective): kind = "concept" def finder_filter( self, namespace: str, name: str, project_info: ProjectInfo, index: parser.DoxygenIndex, matches: list[filter.FinderMatch], ) -> None: # Unions are stored in the xml file with their fully namespaced name # We're using C++ namespaces here, it might be best to make this file # type dependent # xml_name = "%s::%s" % (namespace, name) if namespace else name matches.extend(filter.compound_finder_filter(xml_name, "concept", index)) class DoxygenEnumDirective(_DoxygenBaseItemDirective): kind = "enum" class DoxygenEnumValueDirective(_DoxygenBaseItemDirective): kind = "enumvalue" def finder_filter( self, namespace: str, name: str, project_info: ProjectInfo, index: parser.DoxygenIndex, matches: list[filter.FinderMatch], ) -> None: for m, c in index.members[name]: if m.kind != parser.MemberKind.enumvalue: continue dc = self.dox_parser.parse_compound(c.refid, project_info) ev, mdef, sdef, cdef = dc.enumvalue_by_id[m.refid] TN = TaggedNode matches.append([ TN("enumvalue", ev), TN("memberdef", mdef), TN("sectiondef", sdef), TN("compounddef", cdef), TN("doxygen", dc.root), TN("compound", c), TN("doxygenindex", index.root), ]) class DoxygenTypedefDirective(_DoxygenBaseItemDirective): kind = "typedef" class DoxygenUnionDirective(_DoxygenBaseItemDirective): kind = "union" def finder_filter( self, namespace: str, name: str, project_info: ProjectInfo, index: parser.DoxygenIndex, matches: list[filter.FinderMatch], ) -> None: # Unions are stored in the xml file with their fully namespaced name # We're using C++ namespaces here, it might be best to make this file # type dependent # xml_name = "%s::%s" % (namespace, name) if namespace else name matches.extend(filter.compound_finder_filter(xml_name, "union", index)) ================================================ FILE: breathe/directives/setup.py ================================================ from __future__ import annotations import subprocess from pathlib import Path from typing import TYPE_CHECKING from breathe.directives.class_like import ( DoxygenClassDirective, DoxygenInterfaceDirective, DoxygenStructDirective, ) from breathe.directives.content_block import ( DoxygenGroupDirective, DoxygenNamespaceDirective, DoxygenPageDirective, ) from breathe.directives.file import AutoDoxygenFileDirective, DoxygenFileDirective from breathe.directives.function import DoxygenFunctionDirective from breathe.directives.index import AutoDoxygenIndexDirective, DoxygenIndexDirective from breathe.directives.item import ( DoxygenConceptDirective, DoxygenDefineDirective, DoxygenEnumDirective, DoxygenEnumValueDirective, DoxygenTypedefDirective, DoxygenUnionDirective, DoxygenVariableDirective, ) from breathe.parser import DoxygenParser from breathe.process import AutoDoxygenProcessHandle from breathe.project import ProjectInfoFactory if TYPE_CHECKING: from sphinx.application import Sphinx def setup(app: Sphinx) -> None: directives = { "doxygenindex": DoxygenIndexDirective, "autodoxygenindex": AutoDoxygenIndexDirective, "doxygenfunction": DoxygenFunctionDirective, "doxygenstruct": DoxygenStructDirective, "doxygenclass": DoxygenClassDirective, "doxygeninterface": DoxygenInterfaceDirective, "doxygenvariable": DoxygenVariableDirective, "doxygendefine": DoxygenDefineDirective, "doxygenconcept": DoxygenConceptDirective, "doxygenenum": DoxygenEnumDirective, "doxygenenumvalue": DoxygenEnumValueDirective, "doxygentypedef": DoxygenTypedefDirective, "doxygenunion": DoxygenUnionDirective, "doxygennamespace": DoxygenNamespaceDirective, "doxygengroup": DoxygenGroupDirective, "doxygenfile": DoxygenFileDirective, "autodoxygenfile": AutoDoxygenFileDirective, "doxygenpage": DoxygenPageDirective, } # The directives need these global objects, so in order to smuggle # them in, we use env.temp_data. But it is cleared after each document # has been read, we use the source-read event to set them. # note: the parser factory contains a cache of the parsed XML # note: the project_info_factory also contains some caching stuff # TODO: is that actually safe for when reading in parallel? project_info_factory = ProjectInfoFactory(app) dox_parser = DoxygenParser(app) def set_temp_data( app: Sphinx, project_info_factory=project_info_factory, parser_factory=dox_parser ): assert app.env is not None app.env.temp_data["breathe_project_info_factory"] = project_info_factory app.env.temp_data["breathe_dox_parser"] = parser_factory app.connect("source-read", lambda app, docname, source: set_temp_data(app)) for name, directive in directives.items(): app.add_directive(name, directive) app.add_config_value("breathe_projects", {}, "env") # Dict[str, str] app.add_config_value("breathe_default_project", "", "env") # str # Provide reasonable defaults for domain_by_extension mapping. Can be overridden by users. app.add_config_value( "breathe_domain_by_extension", {"py": "py", "cs": "cs"}, "env" ) # Dict[str, str] app.add_config_value("breathe_domain_by_file_pattern", {}, "env") # Dict[str, str] app.add_config_value("breathe_projects_source", {}, "env") app.add_config_value("breathe_build_directory", "", "env") app.add_config_value("breathe_default_members", (), "env") app.add_config_value("breathe_show_define_initializer", False, "env") app.add_config_value("breathe_show_enumvalue_initializer", False, "env") app.add_config_value("breathe_show_include", True, "env") app.add_config_value("breathe_implementation_filename_extensions", [".c", ".cc", ".cpp"], "env") app.add_config_value("breathe_doxygen_config_options", {}, "env") app.add_config_value("breathe_doxygen_aliases", {}, "env") app.add_config_value("breathe_use_project_refids", False, "env") app.add_config_value("breathe_order_parameters_first", False, "env") app.add_config_value("breathe_separate_member_pages", False, "env") breathe_css = "breathe.css" if Path(app.confdir, "_static", breathe_css).exists(): app.add_css_file(breathe_css) def write_file(directory, filename, content): # Ensure that the directory exists directory = Path(directory) directory.mkdir(parents=True, exist_ok=True) # Write the file with the provided contents (directory / filename).write_text(content, encoding="utf-8") doxygen_handle = AutoDoxygenProcessHandle( subprocess.check_call, write_file, project_info_factory ) def doxygen_hook(app: Sphinx): doxygen_handle.generate_xml( app.config.breathe_projects_source, app.config.breathe_doxygen_config_options, app.config.breathe_doxygen_aliases, ) app.connect("builder-inited", doxygen_hook) ================================================ FILE: breathe/exception.py ================================================ from __future__ import annotations class BreatheError(Exception): pass ================================================ FILE: breathe/file_state_cache.py ================================================ from __future__ import annotations import os from pathlib import Path from typing import TYPE_CHECKING if TYPE_CHECKING: from sphinx.application import Sphinx from sphinx.environment import BuildEnvironment """ Store the modified time of the various doxygen xml files against the reStructuredText file that they are referenced from so that we know which reStructuredText files to rebuild if the doxygen xml is modified. We store the information in the environment object as 'breathe_file_state' so that it is pickled down and stored between builds as Sphinx is designed to do. (mypy doesn't like dynamically added attributes, hence all references to it are ignored) """ class MTimeError(Exception): pass def _getmtime(filename: str): try: return os.path.getmtime(filename) except OSError: raise MTimeError("Cannot find file: %s" % os.path.realpath(filename)) def update(app: Sphinx, source_file: str | os.PathLike[str]) -> None: if not hasattr(app.env, "breathe_file_state"): app.env.breathe_file_state = {} # type: ignore[attr-defined] norm_source_file = Path(source_file).resolve().as_posix() new_mtime = _getmtime(norm_source_file) _mtime, docnames = app.env.breathe_file_state.setdefault( # type: ignore[attr-defined] norm_source_file, (new_mtime, set()) ) assert app.env is not None docnames.add(app.env.docname) app.env.breathe_file_state[norm_source_file] = (new_mtime, docnames) # type: ignore[attr-defined] def _get_outdated( app: Sphinx, env: BuildEnvironment, added: set[str], changed: set[str], removed: set[str] ) -> list[str]: if not hasattr(app.env, "breathe_file_state"): return [] stale = [] for filename, info in app.env.breathe_file_state.items(): old_mtime, docnames = info if _getmtime(filename) > old_mtime: stale.extend(docnames) return list(set(stale).difference(removed)) def _purge_doc(app: Sphinx, env: BuildEnvironment, docname: str) -> None: if not hasattr(app.env, "breathe_file_state"): return toremove = [] for filename, info in app.env.breathe_file_state.items(): _, docnames = info docnames.discard(docname) if not docnames: toremove.append(filename) for filename in toremove: del app.env.breathe_file_state[filename] def setup(app: Sphinx): app.connect("env-get-outdated", _get_outdated) app.connect("env-purge-doc", _purge_doc) ================================================ FILE: breathe/filetypes.py ================================================ """ A module to house the methods for resolving a code-blocks language based on filename (and extension). """ from __future__ import annotations import os.path from pygments.lexers import get_lexer_for_filename from pygments.util import ClassNotFound def get_pygments_alias(filename: str) -> str | None: "Find first pygments alias from filename" try: lexer_cls = get_lexer_for_filename(filename) return lexer_cls.aliases[0] # type: ignore[attr-defined] except ClassNotFound: return None def get_extension(filename: str) -> str: """Get extension from filename.""" # If the filename is just '.ext' then we get ('.ext', '') so we fall back to first part if # the second isn't there (first, second) = os.path.splitext(filename) # Doxygen allows users to specify the file extension ".unparsed" to disable syntax highlighting. # We translate it into the pygments un-highlighted 'text' type return (second or first).lstrip(".").replace("unparsed", "text") ================================================ FILE: breathe/finder/__init__.py ================================================ from __future__ import annotations from typing import TYPE_CHECKING, Generic, TypeVar if TYPE_CHECKING: from breathe import parser from breathe.finder.factory import FinderCreatorMap from breathe.project import ProjectInfo from breathe.renderer import T_data_object, TaggedNode from breathe.renderer.filter import DoxFilter, FinderMatch else: T_data_object = TypeVar("T_data_object", covariant=True) class ItemFinder(Generic[T_data_object]): def __init__( self, project_info: ProjectInfo, node: TaggedNode[T_data_object], finders: FinderCreatorMap, ): self.node = node self.finders: FinderCreatorMap = finders self.project_info = project_info def run_filter( self, filter_: DoxFilter, matches: list[FinderMatch], node_stack: list[TaggedNode], item: parser.NodeOrValue, tag: str | None = None, ) -> None: """Adds all nodes which match the filter into the matches list""" item_finder = factory.create_item_finder(self.finders, self.project_info, item, tag) item_finder.filter_(node_stack, filter_, matches) def filter_( self, ancestors: list[TaggedNode], filter_: DoxFilter, matches: list[FinderMatch] ) -> None: raise NotImplementedError # ItemFinder needs to be defined before we can import any submodules from breathe.finder import factory # noqa: E402 ================================================ FILE: breathe/finder/compound.py ================================================ from __future__ import annotations from typing import TYPE_CHECKING from breathe import parser from breathe.finder import ItemFinder from breathe.renderer import TaggedNode from breathe.renderer.filter import NodeStack if TYPE_CHECKING: from breathe.renderer.filter import DoxFilter, FinderMatch class DoxygenTypeSubItemFinder(ItemFinder[parser.Node_DoxygenType]): def filter_(self, ancestors, filter_: DoxFilter, matches: list[FinderMatch]) -> None: """Find nodes which match the filter. Doesn't test this node, only its children""" node_stack = [self.node] + ancestors assert len(self.node.value.compounddef) == 1 self.run_filter(filter_, matches, node_stack, self.node.value.compounddef[0]) class CompoundDefTypeSubItemFinder(ItemFinder[parser.Node_compounddefType]): def filter_(self, ancestors, filter_: DoxFilter, matches: list[FinderMatch]) -> None: """Finds nodes which match the filter and continues checks to children""" node_stack = [self.node] + ancestors if filter_(NodeStack(node_stack)): matches.append(node_stack) for sectiondef in self.node.value.sectiondef: self.run_filter(filter_, matches, node_stack, sectiondef) for innerclass in self.node.value.innerclass: self.run_filter(filter_, matches, node_stack, innerclass, "innerclass") class SectionDefTypeSubItemFinder(ItemFinder[parser.Node_sectiondefType]): def filter_(self, ancestors, filter_: DoxFilter, matches: list[FinderMatch]) -> None: """Find nodes which match the filter. Doesn't test this node, only its children""" node_stack = [self.node] + ancestors if filter_(NodeStack(node_stack)): matches.append(node_stack) for memberdef in self.node.value.memberdef: self.run_filter(filter_, matches, node_stack, memberdef) for member in self.node.value.member: self.run_filter(filter_, matches, node_stack, member) class MemberDefTypeSubItemFinder(ItemFinder[parser.Node_memberdefType]): def filter_(self, ancestors, filter_: DoxFilter, matches: list[FinderMatch]) -> None: data_object = self.node.value node_stack = [self.node] + ancestors if filter_(NodeStack(node_stack)): matches.append(node_stack) if data_object.kind == parser.DoxMemberKind.enum: for value in data_object.enumvalue: value_stack = [TaggedNode("enumvalue", value)] + node_stack if filter_(NodeStack(value_stack)): matches.append(value_stack) class RefTypeSubItemFinder(ItemFinder[parser.Node_refType]): def filter_(self, ancestors, filter_: DoxFilter, matches: list[FinderMatch]) -> None: node_stack = [self.node] + ancestors if filter_(NodeStack(node_stack)): matches.append(node_stack) ================================================ FILE: breathe/finder/factory.py ================================================ from __future__ import annotations from typing import TYPE_CHECKING from breathe import parser from breathe.finder import compound as compoundfinder from breathe.finder import index as indexfinder from breathe.renderer import TaggedNode if TYPE_CHECKING: from typing import Callable, Union from sphinx.application import Sphinx from breathe.finder import ItemFinder from breathe.project import ProjectInfo from breathe.renderer.filter import DoxFilter, FinderMatch ItemFinderCreator = Callable[[ProjectInfo, TaggedNode, "FinderCreatorMap"], ItemFinder] FinderCreatorMap = dict[type[parser.NodeOrValue], ItemFinderCreator] FinderRoot = Union[ parser.Node_CompoundType, parser.Node_MemberType, parser.Node_DoxygenType, parser.Node_compounddefType, parser.Node_sectiondefType, parser.Node_memberdefType, parser.Node_refType, ] class _CreateCompoundTypeSubFinder: def __init__(self, app: Sphinx, dox_parser: parser.DoxygenParser): self.app = app self.dox_parser = dox_parser def __call__(self, project_info: ProjectInfo, *args) -> indexfinder.CompoundTypeSubItemFinder: return indexfinder.CompoundTypeSubItemFinder(self.app, self.dox_parser, project_info, *args) def create_item_finder( finders: dict[type[parser.NodeOrValue], ItemFinderCreator], project_info: ProjectInfo, data_object: parser.NodeOrValue, tag: str | None = None, ) -> ItemFinder: return finders[type(data_object)](project_info, TaggedNode(tag, data_object), finders) class Finder: def __init__( self, root: parser.NodeOrValue, project_info: ProjectInfo, finders: FinderCreatorMap ): self._root = root self.project_info = project_info self.finders = finders def filter_(self, filter_: DoxFilter, matches: list[FinderMatch]) -> None: """Adds all nodes which match the filter into the matches list""" item_finder = create_item_finder(self.finders, self.project_info, self._root) item_finder.filter_([], filter_, matches) def root(self): return self._root def create_finder_from_root( app: Sphinx, dox_parser: parser.DoxygenParser, root: FinderRoot, project_info: ProjectInfo ) -> Finder: finders: FinderCreatorMap = { parser.Node_CompoundType: _CreateCompoundTypeSubFinder(app, dox_parser), parser.Node_MemberType: indexfinder.MemberTypeSubItemFinder, parser.Node_DoxygenType: compoundfinder.DoxygenTypeSubItemFinder, parser.Node_compounddefType: compoundfinder.CompoundDefTypeSubItemFinder, parser.Node_sectiondefType: compoundfinder.SectionDefTypeSubItemFinder, parser.Node_memberdefType: compoundfinder.MemberDefTypeSubItemFinder, parser.Node_refType: compoundfinder.RefTypeSubItemFinder, } return Finder(root, project_info, finders) ================================================ FILE: breathe/finder/index.py ================================================ from __future__ import annotations from typing import TYPE_CHECKING from breathe import parser from breathe.finder import ItemFinder from breathe.renderer.filter import NodeStack if TYPE_CHECKING: from sphinx.application import Sphinx from breathe.renderer import TaggedNode from breathe.renderer.filter import DoxFilter, FinderMatch class DoxygenTypeSubItemFinder(ItemFinder[parser.Node_DoxygenTypeIndex]): def filter_(self, ancestors, filter_: DoxFilter, matches) -> None: """Find nodes which match the filter. Doesn't test this node, only its children""" compounds = self.node.value.compound node_stack = [self.node] + ancestors for compound in compounds: self.run_filter(filter_, matches, node_stack, compound) class CompoundTypeSubItemFinder(ItemFinder[parser.Node_CompoundType]): def __init__(self, app: Sphinx, dox_parser: parser.DoxygenParser, *args): super().__init__(*args) self.dox_parser = dox_parser def filter_(self, ancestors: list[TaggedNode], filter_: DoxFilter, matches) -> None: """Finds nodes which match the filter and continues checks to children Requires parsing the xml files referenced by the children for which we use the compound parser and continue at the top level of that pretending that this node is the parent of the top level node of the compound file. """ node_stack = [self.node] + ancestors # Match against compound object if filter_(NodeStack(node_stack)): matches.append(node_stack) # Descend to member children members = self.node.value.member member_matches: list[FinderMatch] = [] for member in members: self.run_filter(filter_, member_matches, node_stack, member) # If there are members in this compound that match the criteria # then load up the file for this compound and get the member data objects if member_matches: file_data = self.dox_parser.parse_compound( self.node.value.refid, self.project_info ).root for member_stack in member_matches: mem = member_stack[0].value assert isinstance(mem, parser.Node_MemberType) refid = mem.refid def ref_filter(nstack): node = nstack.node return isinstance(node, parser.Node_memberdefType) and node.id == refid self.run_filter(ref_filter, matches, node_stack, file_data) else: # Read in the xml file referenced by the compound and descend into that as well file_data = self.dox_parser.parse_compound( self.node.value.refid, self.project_info ).root self.run_filter(filter_, matches, node_stack, file_data) class MemberTypeSubItemFinder(ItemFinder[parser.Node_memberdefType]): def filter_(self, ancestors, filter_: DoxFilter, matches) -> None: node_stack = [self.node] + ancestors # Match against member object if filter_(NodeStack(node_stack)): matches.append(node_stack) ================================================ FILE: breathe/parser.py ================================================ # ruff: noqa: F403, F405, PGH003, UP024 from __future__ import annotations import collections import reprlib from typing import TYPE_CHECKING, NamedTuple, overload from breathe import file_state_cache, path_handler from breathe._parser import * if TYPE_CHECKING: import sys from typing import Union from sphinx.application import Sphinx from breathe.project import ProjectInfo if sys.version_info >= (3, 10): from typing import TypeAlias else: from typing_extensions import TypeAlias NodeOrValue: TypeAlias = Union[Node, str, None] @reprlib.recursive_repr() def node_repr(self: Node) -> str: # pragma: no cover cls = type(self) fields = [] if isinstance(self, list): pos = ", ".join(map(repr, self)) fields.append(f"[{pos}]") fields.extend(f"{field}={getattr(self, field)!r}" for field in cls._fields) inner = ", ".join(fields) return f"{cls.__name__}({inner})" Node.__repr__ = node_repr # type: ignore def description_has_content(node: Node_descriptionType | None) -> bool: if node is None: return False if bool(node.title) or len(node) > 1: return True if not len(node): return False item = node[0] return not isinstance(item, str) or (len(item) > 0 and not item.isspace()) class ParserError(RuntimeError): def __init__(self, message: str, filename: str, lineno: int | None = None): super().__init__(message, lineno, filename) @property def message(self) -> str: return self.args[0] @property def lineno(self) -> int | None: return self.args[1] @property def filename(self) -> str: return self.args[2] def __str__(self): if self.lineno is None: return f"{self.filename}: {self.message}" return f"{self.filename}:{self.lineno}: {self.message}" class FileIOError(RuntimeError): def __init__(self, error: str, filename: str): super().__init__(error) self.error = error self.filename = filename class DoxygenIndex: def __init__(self, root: Node_DoxygenTypeIndex): self.root = root self.compounds: collections.defaultdict[str, list[Node_CompoundType]] = ( collections.defaultdict(list) ) self.members: collections.defaultdict[ str, list[tuple[Node_MemberType, Node_CompoundType]] ] = collections.defaultdict(list) self.file_compounds: list[Node_CompoundType] = [] for c in root.compound: self.compounds[c.name].append(c) if c.kind == CompoundKind.file: self.file_compounds.append(c) for m in c.member: self.members[m.name].append((m, c)) class DoxygenCompound: def __init__(self, root: Node_DoxygenType, parser: DoxygenParser, project_info: ProjectInfo): self.root = root self.members_by_id: dict[ str, tuple[Node_memberdefType, Node_sectiondefType, Node_compounddefType] ] = {} self.enumvalue_by_id: dict[ str, tuple[ Node_enumvalueType, Node_memberdefType, Node_sectiondefType, Node_compounddefType ], ] = {} for c in root.compounddef: for s in c.sectiondef: for m in s.memberdef: self.members_by_id[m.id] = (m, s, c) for ev in m.enumvalue: self.enumvalue_by_id[ev.id] = (ev, m, s, c) for m in s.member: # Parse the referenced compound (group) XML group_compound = parser.parse_compound( m.refid.rsplit(sep="_", maxsplit=1)[0], project_info ) # Get the memberdef from the group compound self.members_by_id.update(group_compound.members_by_id) def _parse_common(filename: str, right_tag: str) -> Node_DoxygenType | Node_DoxygenTypeIndex: try: with open(filename, "rb") as file: result = parse_file(file) if result.name != right_tag: raise ParserError(f'expected "{right_tag}" root element, not "{result.name}"', filename) return result.value except ParseError as e: raise ParserError(e.message, filename, e.lineno) except IOError as e: raise FileIOError(str(e), filename) class ProjectData(NamedTuple): d_index: DoxygenIndex compound_cache: dict[str, DoxygenCompound] class DoxygenParser: def __init__(self, app: Sphinx) -> None: self.app = app self.parsed_data: dict[str, ProjectData] = {} def _get_project_data(self, project_info: ProjectInfo) -> ProjectData: key = project_info.project_path() r = self.parsed_data.get(key) if r is None: filename = path_handler.resolve_path(self.app, project_info.project_path(), "index.xml") file_state_cache.update(self.app, filename) n = _parse_common(filename, "doxygenindex") assert isinstance(n, Node_DoxygenTypeIndex) r = ProjectData(DoxygenIndex(n), {}) self.parsed_data[key] = r return r def parse_index(self, project_info: ProjectInfo) -> DoxygenIndex: return self._get_project_data(project_info).d_index def parse_compound(self, refid: str, project_info: ProjectInfo) -> DoxygenCompound: cache = self._get_project_data(project_info).compound_cache r = cache.get(refid) if r is None: filename = path_handler.resolve_path( self.app, project_info.project_path(), f"{refid}.xml" ) file_state_cache.update(self.app, filename) n = _parse_common(filename, "doxygen") assert isinstance(n, Node_DoxygenType) # Pass parser instance to allow looking up referenced compounds r = DoxygenCompound(n, self, project_info) cache[refid] = r return r @overload def tag_name_value(x: TaggedValue[T_covar, U_covar]) -> tuple[T_covar, U_covar]: ... @overload def tag_name_value(x: str) -> tuple[None, str]: ... def tag_name_value(x: TaggedValue[T_covar, U_covar] | str) -> tuple[T_covar | None, U_covar | str]: if isinstance(x, str): return None, x return x.name, x.value ================================================ FILE: breathe/path_handler.py ================================================ from __future__ import annotations from pathlib import Path from typing import TYPE_CHECKING if TYPE_CHECKING: from sphinx.application import Sphinx def includes_directory(file_path: str): # Check for backslash or forward slash as we don't know what platform we're on and sometimes # the doxygen paths will have forward slash even on Windows. return bool(str(file_path).count("\\")) or bool(str(file_path).count("/")) def resolve_path(app: Sphinx, directory: str, filename: str): """Returns a full path to the filename in the given directory assuming that if the directory path is relative, then it is relative to the conf.py directory. """ return Path(app.confdir, directory, filename).resolve() ================================================ FILE: breathe/process.py ================================================ from __future__ import annotations import os from pathlib import Path from shlex import quote from typing import TYPE_CHECKING from breathe.project import AutoProjectInfo, ProjectInfoFactory if TYPE_CHECKING: from collections.abc import Mapping from typing import Callable from breathe.project import AutoProjectInfo, ProjectInfoFactory AUTOCFG_TEMPLATE = r""" PROJECT_NAME = "{project_name}" OUTPUT_DIRECTORY = {output_dir} GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = {input} ENABLE_PREPROCESSING = YES QUIET = YES JAVADOC_AUTOBRIEF = YES JAVADOC_AUTOBRIEF = NO GENERATE_HTML = NO GENERATE_XML = YES ALIASES = rst="\verbatim embed:rst" ALIASES += endrst="\endverbatim" ALIASES += inlinerst="\verbatim embed:rst:inline" {extra} """.strip() class ProjectData: """Simple handler for the files and project_info for each project.""" def __init__(self, auto_project_info: AutoProjectInfo, files: list[str]) -> None: self.auto_project_info = auto_project_info self.files = files class AutoDoxygenProcessHandle: def __init__( self, run_process: Callable, write_file: Callable[[str | os.PathLike[str], str, str], None], project_info_factory: ProjectInfoFactory, ) -> None: self.run_process = run_process self.write_file = write_file self.project_info_factory = project_info_factory def generate_xml( self, projects_source: Mapping[str, tuple[str, list[str]]], doxygen_options: Mapping[str, str], doxygen_aliases: Mapping[str, str], ) -> None: project_files: dict[str, ProjectData] = {} # First collect together all the files which need to be doxygen processed for each project for project_name, file_structure in projects_source.items(): folder, contents = file_structure auto_project_info = self.project_info_factory.create_auto_project_info( project_name, folder ) project_files[project_name] = ProjectData(auto_project_info, contents) # Iterate over the projects and generate doxygen xml output for the files for each one into # a directory in the Sphinx build area for project_name, data in project_files.items(): project_path = self.process( data.auto_project_info, data.files, doxygen_options, doxygen_aliases ) project_info = data.auto_project_info.create_project_info(project_path) self.project_info_factory.store_project_info_for_auto(project_name, project_info) def process( self, auto_project_info: AutoProjectInfo, files: list[str], doxygen_options: Mapping[str, str], doxygen_aliases: Mapping[str, str], ) -> str: name = auto_project_info.name() full_paths = [str(auto_project_info.abs_path_to_source_file(f)) for f in files] options = "\n".join("%s=%s" % pair for pair in doxygen_options.items()) aliases = "\n".join( f'ALIASES += {name}="{value}"' for name, value in doxygen_aliases.items() ) cfg = AUTOCFG_TEMPLATE.format( project_name=name, output_dir=name, input=" ".join(full_paths), extra=f"{options}\n{aliases}", ) build_dir = Path(auto_project_info.build_dir(), "breathe", "doxygen") cfgfile = f"{name}.cfg" self.write_file(build_dir, cfgfile, cfg) # Shell-escape the cfg file name to try to avoid any issue where the name might include # malicious shell character - We have to use the shell=True option to make it work on # Windows. See issue #271 self.run_process(f"doxygen {quote(cfgfile)}", cwd=build_dir, shell=True) return os.path.join(build_dir, name, "xml") ================================================ FILE: breathe/project.py ================================================ from __future__ import annotations import fnmatch import os import os.path from pathlib import Path from typing import TYPE_CHECKING from breathe.exception import BreatheError if TYPE_CHECKING: from collections import UserDict from sphinx.application import Sphinx class ProjectOptions(UserDict): path: str project: str class ProjectError(BreatheError): pass class NoDefaultProjectError(ProjectError): pass class AutoProjectInfo: """Created as a temporary step in the automatic xml generation process""" def __init__(self, app: Sphinx, name: str, source_path: str, build_dir: str, reference: str): self.app = app self._name = name self._source_path = source_path self._build_dir = build_dir self._reference = reference def name(self): return self._name def build_dir(self): return self._build_dir def abs_path_to_source_file(self, file_): """ Returns full path to the provide file assuming that the provided path is relative to the projects conf.py directory as specified in the breathe_projects_source config variable. """ return Path(self.app.confdir, self._source_path, file_).resolve() def create_project_info(self, project_path): """Creates a proper ProjectInfo object based on the information in this AutoProjectInfo""" return ProjectInfo(self.app, self._name, project_path, self._source_path, self._reference) class ProjectInfo: def __init__(self, app: Sphinx, name: str, path: str, source_path: str, reference: str): self.app = app self._name = name self._project_path = path self._source_path = source_path self._reference = reference def name(self) -> str: return self._name def project_path(self): return self._project_path def source_path(self): return self._source_path def relative_path_to_xml_file(self, file_): """ Returns relative path from Sphinx documentation top-level source directory to the specified file assuming that the specified file is a path relative to the doxygen xml output directory. """ # os.path.join does the appropriate handling if _project_path is an absolute path full_xml_project_path = os.path.join(self.app.confdir, self._project_path, file_) return os.path.relpath(full_xml_project_path, self.app.srcdir) def sphinx_abs_path_to_file(self, file_): """ Prepends os.path.sep to the value returned by relative_path_to_file. This is to match Sphinx's concept of an absolute path which starts from the top-level source directory of the project. """ return os.path.sep + self.relative_path_to_xml_file(file_) def reference(self): return self._reference def domain_for_file(self, file_: str) -> str: extension = file_.split(".")[-1] try: domain = self.app.config.breathe_domain_by_extension[extension] except KeyError: domain = "" domainFromFilePattern = self.app.config.breathe_domain_by_file_pattern for pattern, pattern_domain in domainFromFilePattern.items(): if fnmatch.fnmatch(file_, pattern): domain = pattern_domain return domain class ProjectInfoFactory: _default_build_dir: str def __init__(self, app: Sphinx): self.app = app # note: don't access self.app.config now, as we are instantiated at setup-time. # Assume general build directory is the parent of the doctree directory. # This can be overridden with the breathe_build_directory config variable self._default_build_dir = os.path.dirname(os.path.normpath(app.doctreedir)) self.project_count = 0 self.project_info_store: dict[str, ProjectInfo] = {} self.project_info_for_auto_store: dict[str, ProjectInfo] = {} self.auto_project_info_store: dict[str, AutoProjectInfo] = {} @property def build_dir(self) -> str: config = self.app.config if config.breathe_build_directory: return config.breathe_build_directory else: return self._default_build_dir def default_path(self) -> str: config = self.app.config if not config.breathe_default_project: raise NoDefaultProjectError( "No breathe_default_project config setting to fall back on " "for directive with no 'project' or 'path' specified." ) try: return config.breathe_projects[config.breathe_default_project] except KeyError: raise ProjectError( ( "breathe_default_project value '%s' does not seem to be a valid key for the " "breathe_projects dictionary" ) % config.breathe_default_project ) def create_project_info(self, options: ProjectOptions) -> ProjectInfo: config = self.app.config name = config.breathe_default_project if "project" in options: try: path = config.breathe_projects[options["project"]] name = options["project"] except KeyError: raise ProjectError( "Unable to find project '%s' in breathe_projects dictionary" % options["project"] ) elif "path" in options: path = options["path"] else: path = self.default_path() try: return self.project_info_store[path] except KeyError: reference = name if not name: name = "project%s" % self.project_count reference = path self.project_count += 1 project_info = ProjectInfo(self.app, name, path, "NoSourcePath", reference) self.project_info_store[path] = project_info return project_info def store_project_info_for_auto(self, name: str, project_info: ProjectInfo) -> None: """Stores the project info by name for later extraction by the auto directives. Stored separately to the non-auto project info objects as they should never overlap. """ self.project_info_for_auto_store[name] = project_info def retrieve_project_info_for_auto(self, options) -> ProjectInfo: """Retrieves the project info by name for later extraction by the auto directives. Looks for the 'project' entry in the options dictionary. This is a less than ideal API but it is designed to match the use of 'create_project_info' above for which it makes much more sense. """ name = options.get("project", self.app.config.breathe_default_project) if name is None: raise NoDefaultProjectError( "No breathe_default_project config setting to fall back on " "for directive with no 'project' or 'path' specified." ) return self.project_info_for_auto_store[name] def create_auto_project_info(self, name: str, source_path) -> AutoProjectInfo: key = source_path try: return self.auto_project_info_store[key] except KeyError: reference = name if not name: name = "project%s" % self.project_count reference = source_path self.project_count += 1 auto_project_info = AutoProjectInfo( self.app, name, source_path, self.build_dir, reference ) self.auto_project_info_store[key] = auto_project_info return auto_project_info ================================================ FILE: breathe/renderer/__init__.py ================================================ from __future__ import annotations import textwrap from typing import TYPE_CHECKING, NamedTuple, TypeVar from docutils import nodes if TYPE_CHECKING: from typing import Generic, Union from breathe import parser from breathe.directives.index import RootDataObject from breathe.renderer import mask DataObject = Union[parser.NodeOrValue, RootDataObject] T_data_object = TypeVar("T_data_object", bound=DataObject, covariant=True) else: T_data_object = TypeVar("T_data_object", covariant=True) def format_parser_error( name: str, error: str, filename: str, state, lineno: int, do_unicode_warning: bool = False ) -> list[nodes.Node]: warning = '%s: Unable to parse xml file "%s". ' % (name, filename) explanation = "Reported error: %s. " % error unicode_explanation_text = "" unicode_explanation = [] if do_unicode_warning: unicode_explanation_text = ( textwrap.dedent( """ Parsing errors are often due to unicode errors associated with the encoding of the original source files. Doxygen propagates invalid characters from the input source files to the output xml.""" ) .strip() .replace("\n", " ") ) unicode_explanation = [nodes.paragraph("", "", nodes.Text(unicode_explanation_text))] return [ nodes.warning( "", nodes.paragraph("", "", nodes.Text(warning)), nodes.paragraph("", "", nodes.Text(explanation)), *unicode_explanation, ), state.document.reporter.warning( warning + explanation + unicode_explanation_text, line=lineno ), ] if TYPE_CHECKING: class TaggedNode(NamedTuple, Generic[T_data_object]): tag: str | None value: T_data_object else: # Python 3.9 and 3.10 don't allow multiple-inheritance with NamedTuple class TaggedNode(NamedTuple): tag: str | None value: ... class RenderContext: def __init__( self, node_stack: list[TaggedNode], mask_factory: mask.MaskFactoryBase, directive_args, domain: str = "", child: bool = False, ) -> None: self.node_stack = node_stack self.mask_factory = mask_factory self.directive_args = directive_args self.domain = domain self.child = child def create_child_context( self, data_object: parser.NodeOrValue, tag: str | None = None ) -> RenderContext: node_stack = self.node_stack[:] node_stack.insert(0, TaggedNode(tag, self.mask_factory.mask(data_object))) return RenderContext(node_stack, self.mask_factory, self.directive_args, self.domain, True) ================================================ FILE: breathe/renderer/filter.py ================================================ """ Filters ------- Filters are an interesting and somewhat challenging part of the code base. They are used for two different purposes: - To figure out which nodes in the xml hierarchy to start rendering from. These are called 'finder filters' or 'content filters'. This is done before rendering starts. - To figure out which nodes under a selected nodes in the xml hierarchy should be rendered. These are called 'render filters'. This is done during the render process with a test in the DoxygenToRstRendererFactory. Finder Filters ~~~~~~~~~~~~~~ The implementation of the filters can change a little depending on how they are called. Finder filters are called from the breathe.finder.doxygen.index and breathe.finder.doxygen.compound files. They are called like this: # Descend down the hierarchy # ... if filter_(node_stack): matches.append(self.data_object) # Keep on descending # ... This means that the result of the filter does not stop us descending down the hierarchy and testing more nodes. This simplifies the filters as they only have to return true for the exact nodes they are interested in and they don't have to worry about allowing the iteration down the hierarchy to continue for nodes which don't match. Content Filters ~~~~~~~~~~~~~~~ Content filters are harder than the finder filters as they are responsible for halting the iteration down the hierarchy if they return false. This means that if you're interested in Node_memberdefType nodes with a particular attribute then you have to check for that but also include a clause which allows all other non-Node_memberdefType nodes to pass through as you don't want to interrupt them. """ from __future__ import annotations from typing import TYPE_CHECKING from breathe import parser, renderer if TYPE_CHECKING: import sys from collections.abc import Container, Iterable, Mapping from typing import Any, Callable, SupportsIndex, Union from sphinx.application import Sphinx if sys.version_info >= (3, 11): from typing import Any, TypeAlias else: from typing_extensions import TypeAlias from breathe.directives.class_like import DoxClassOptions from breathe.directives.content_block import DoxContentBlockOptions from breathe.project import ProjectInfo DoxFilter: TypeAlias = Callable[["NodeStack"], bool] FinderMatch: TypeAlias = list[renderer.TaggedNode] FinderMatchItr: TypeAlias = Iterable[FinderMatch] DoxIndexFilter: TypeAlias = Callable[[parser.DoxygenIndex], FinderMatchItr] DoxNamespaceOptions: TypeAlias = Union[DoxClassOptions, DoxContentBlockOptions] T_options = Union[DoxClassOptions, DoxContentBlockOptions] CLASS_LIKE_COMPOUNDDEF = ( parser.DoxCompoundKind.class_, parser.DoxCompoundKind.struct, parser.DoxCompoundKind.interface, ) class NodeStack: def __init__(self, stack: list[renderer.TaggedNode]): self.stack = stack def ancestor(self, generations: SupportsIndex) -> renderer.DataObject | None: i = generations.__index__() return self.stack[i].value if len(self.stack) > i else None @property def parent(self) -> renderer.DataObject | None: return self.stack[1].value if len(self.stack) > 1 else None @property def node(self) -> renderer.DataObject: return self.stack[0].value @property def tag(self) -> str: tag = self.stack[0].tag assert tag is not None return tag def set_defaults(app: Sphinx, options: T_options) -> T_options: r: Any = options.copy() for m in app.config.breathe_default_members: r.setdefault(m, "") return r def create_show_filter(options: Mapping[str, Any]) -> DoxFilter: """Currently only handles the header-file entry""" if options.get("show") == "header-file": return lambda nstack: True # Allow through everything except the header-file includes nodes def filter_(nstack: NodeStack) -> bool: return not ( isinstance(nstack.parent, parser.Node_compounddefType) and isinstance(nstack.node, parser.Node_incType) ) return filter_ def _create_undoc_members_filter(options: DoxNamespaceOptions) -> DoxFilter: if "undoc-members" in options: return lambda nstack: True def filter_(nstack: NodeStack) -> bool: node = nstack.node # Allow anything that isn't a Node_memberdefType, or if it is only # allow the ones with a description return (not isinstance(node, parser.Node_memberdefType)) or bool( parser.description_has_content(node.briefdescription) or parser.description_has_content(node.detaileddescription) ) return filter_ def _create_public_members_filter( options: DoxNamespaceOptions, ) -> Callable[[parser.Node_memberdefType], bool]: if "members" in options: # If the user has specified the 'members' option with arguments then # we only pay attention to that and not to any other member settings members_str = options["members"] if members_str and not members_str.isspace(): # Matches sphinx-autodoc behaviour of comma separated values members = frozenset([x.strip() for x in members_str.split(",")]) # Accept any nodes which don't have a "sectiondef" as a parent # or, if they do, only accept them if their names are in the # members list def filter_(node: parser.Node_memberdefType) -> bool: return node.name in members else: # Select anything that doesn't have a parent which is a # sectiondef, or, if it does, only select the public ones def filter_(node: parser.Node_memberdefType) -> bool: return node.prot == parser.DoxProtectionKind.public else: # Nothing with a parent that's a sectiondef def filter_(node: parser.Node_memberdefType) -> bool: return False return filter_ def create_description_filter(allow: bool, level: type[parser.Node]) -> DoxFilter: """Whether or not we allow descriptions is determined by the calling function and we just do whatever the 'allow' function parameter tells us. """ if allow: # Let through any description children of sectiondefs if we output any kind members def filter_(nstack: NodeStack) -> bool: return not isinstance(nstack.parent, level) or isinstance( nstack.node, parser.Node_descriptionType ) else: # Nothing with a parent that's a sectiondef def filter_(nstack: NodeStack) -> bool: return not isinstance(nstack.parent, level) return filter_ def create_class_member_filter(options: DoxNamespaceOptions) -> DoxFilter: """Content filter based on :members: and :private-members: classes""" # I can't fully explain the filtering of descriptions here. More testing needed to figure # out when it is needed. This approach reflects the old code that was here but it wasn't # commented (my fault.) I wonder if maybe the public and private declarations themselves can # be documented and we need to let them through. Not sure. allow = "members" in options or "protected-members" in options or "private-members" in options description = create_description_filter(allow, parser.Node_sectiondefType) # Create all necessary filters and combine them public_members = _create_public_members_filter(options) undoc_members = _create_undoc_members_filter(options) prot_filter: tuple[parser.DoxProtectionKind, ...] = () if "protected-members" in options: prot_filter += (parser.DoxProtectionKind.protected,) if "private-members" in options: prot_filter += (parser.DoxProtectionKind.private,) # Allow anything that isn't a memberdef, or if it is, and 'prot' is not # empty, allow the ones with an equal 'prot' attribute def filter_(nstack: NodeStack) -> bool: node = nstack.node return ( ( not ( isinstance(node, parser.Node_memberdefType) and isinstance(nstack.parent, parser.Node_sectiondefType) ) or (bool(prot_filter) and node.prot in prot_filter) or public_members(node) ) and undoc_members(nstack) ) or description(nstack) return filter_ def create_innerclass_filter(options: DoxNamespaceOptions, outerclass: str = "") -> DoxFilter: """ :param outerclass: Should be the class/struct being target by the directive calling this code. If it is a group or namespace directive then it should be left blank. It is used when looking for names listed in the :members: option. The name should include any additional namespaces that the target class is in. """ allowed: set[parser.DoxProtectionKind] = set() if "protected-members" in options: allowed.add(parser.DoxProtectionKind.protected) if "private-members" in options: allowed.add(parser.DoxProtectionKind.private) description = create_description_filter(True, parser.Node_compounddefType) members: set[str] | None = None if "members" in options: members_str = options["members"] if members_str and members_str.strip(): prefix = ("%s::" % outerclass) if outerclass else "" # Matches sphinx-autodoc behaviour of comma separated values members = {"%s%s" % (prefix, x.strip()) for x in members_str.split(",")} else: allowed.add(parser.DoxProtectionKind.public) def filter_(nstack: NodeStack) -> bool: node = nstack.node parent = nstack.parent return ( not ( isinstance(node, parser.Node_refType) and nstack.tag == "innerclass" and isinstance(parent, parser.Node_compounddefType) and parent.kind in CLASS_LIKE_COMPOUNDDEF ) or node.prot in allowed or (members is not None and "".join(node) in members) or description(nstack) ) return filter_ def create_class_filter(app: Sphinx, target: str, options: DoxClassOptions) -> DoxFilter: """Content filter for classes based on various directive options""" filter_options = set_defaults(app, options) cm_filter = create_class_member_filter(filter_options) ic_filter = create_innerclass_filter(filter_options, outerclass=target) o_filter = create_outline_filter(filter_options) s_filter = create_show_filter(filter_options) return ( lambda nstack: cm_filter(nstack) and ic_filter(nstack) and o_filter(nstack) and s_filter(nstack) ) def create_outline_filter(options: Mapping[str, Any]) -> DoxFilter: if "outline" in options: return lambda nstack: not isinstance( nstack.node, (parser.Node_descriptionType, parser.Node_incType) ) return lambda nstack: True def extend_member_with_compound( d_parser: parser.DoxygenParser, project_info: ProjectInfo, m: parser.Node_MemberType, c: parser.Node_CompoundType, index: parser.Node_DoxygenTypeIndex, ) -> FinderMatch: dc = d_parser.parse_compound(c.refid, project_info) mdef, sdef, cdef = dc.members_by_id[m.refid] TN = renderer.TaggedNode return [ TN("memberdef", mdef), TN("sectiondef", sdef), TN("compounddef", cdef), TN("doxygen", dc.root), TN("compound", c), TN("doxygenindex", index), ] def member_finder_filter( app: Sphinx, namespace: str, name: str, d_parser: parser.DoxygenParser, project_info: ProjectInfo, kinds: Container[parser.MemberKind] | str, index: parser.DoxygenIndex, ) -> FinderMatchItr: """Looks for a member with the specified name and kind.""" if isinstance(kinds, str): kinds = (parser.MemberKind(kinds),) if namespace: c_kinds = { parser.CompoundKind.namespace, parser.CompoundKind.class_, parser.CompoundKind.struct, parser.CompoundKind.interface, } for m, c in index.members[name]: if c.kind in c_kinds and c.name == namespace: if m.kind in kinds: yield extend_member_with_compound(d_parser, project_info, m, c, index.root) else: ext = tuple(app.config.breathe_implementation_filename_extensions) for m, c in index.members[name]: if c.kind != parser.CompoundKind.file or not c.name.endswith(ext): if m.kind in kinds: yield extend_member_with_compound(d_parser, project_info, m, c, index.root) def compound_finder_filter( name: str, kind: str, index: parser.DoxygenIndex, ) -> FinderMatchItr: """Looks for a compound with the specified name and kind.""" for c in index.compounds[name]: if c.kind.value != kind: continue yield [ renderer.TaggedNode("compound", c), renderer.TaggedNode("doxygenindex", index.root), ] ================================================ FILE: breathe/renderer/mask.py ================================================ """ Masks ===== Masks are related to filters. Filters can block the processing of particular parts of the xml hierarchy but they can only work on node level. If the part of the xml hierarchy that you want to filter out is read in as an instance of one of the classes in parser/doxygen/*.py then you can use the filters. However, if you want to filter out an attribute from one of the nodes (and some of the xml child nodes are read in as attributes on their parents) then you can't use a filter. We introduce the Mask's to fulfil this need. The masks are designed to be applied to a particular node type and to limit the access to particular attributes on the node. For example, then NoParameterNamesMask wraps a node a returns all its standard attributes but returns None for the 'declname' and 'defname' attributes. Currently the Mask functionality is only used for the text signature rendering for doing function matching. """ from __future__ import annotations from typing import TYPE_CHECKING from breathe import parser if TYPE_CHECKING: from typing import Callable def no_parameter_names(node: parser.NodeOrValue) -> parser.Node_paramType: assert isinstance(node, parser.Node_paramType) return parser.Node_paramType( array=node.array, attributes=node.attributes, briefdescription=node.briefdescription, declname=None, defname=None, defval=None, type=node.type, typeconstraint=node.typeconstraint, ) class MaskFactoryBase: def mask(self, data_object): raise NotImplementedError class MaskFactory(MaskFactoryBase): def __init__( self, lookup: dict[type[parser.NodeOrValue], Callable[[parser.NodeOrValue], parser.NodeOrValue]], ): self.lookup = lookup def mask(self, data_object: parser.NodeOrValue) -> parser.NodeOrValue: m = self.lookup.get(type(data_object)) if m is None: return data_object return m(data_object) class NullMaskFactory(MaskFactoryBase): def mask(self, data_object: parser.NodeOrValue) -> parser.NodeOrValue: return data_object ================================================ FILE: breathe/renderer/sphinxrenderer.py ================================================ from __future__ import annotations import re import textwrap from collections import defaultdict from pathlib import Path from typing import ( TYPE_CHECKING, Callable, Generic, Literal, TypeVar, Union, cast, ) from docutils import nodes from docutils.parsers.rst.states import Text from docutils.statemachine import StringList, UnexpectedIndentationError from sphinx import addnodes from sphinx.domains import c, cpp, python from sphinx.ext.graphviz import graphviz from sphinx.util import url_re from sphinx.util.nodes import nested_parse_with_titles from breathe import filetypes, parser from breathe._parser import DoxCompoundKind from breathe.cpp_util import split_name from breathe.renderer.filter import NodeStack php: Any try: from sphinxcontrib import phpdomain as php # type: ignore[import-untyped, no-redef] except ImportError: php = None cs: Any try: # The only valid types for sphinx_csharp are in a git repo so we can't easily rely # on them as we can't publish with a git repo dependency so we tell mypy to ignore # an import failure here from sphinx_csharp import csharp as cs # type: ignore[import-not-found, no-redef] except ImportError: cs = None T = TypeVar("T") if TYPE_CHECKING: from collections.abc import Iterable, Sequence from typing import ( Any, ClassVar, Protocol, ) from sphinx.application import Sphinx from sphinx.directives import ObjectDescription from breathe.project import ProjectInfo from breathe.renderer import DataObject, RenderContext from breathe.renderer.filter import DoxFilter from breathe.renderer.target import TargetHandler class HasRefID(Protocol): @property def refid(self) -> str: ... class HasTemplateParamList(Protocol): @property def templateparamlist(self) -> parser.Node_templateparamlistType | None: ... class HasDescriptions(Protocol): @property def briefdescription(self) -> parser.Node_descriptionType | None: ... @property def detaileddescription(self) -> parser.Node_descriptionType | None: ... ContentCallback = Callable[[addnodes.desc_content], None] Declarator = Union[addnodes.desc_signature, addnodes.desc_signature_line] DeclaratorCallback = Callable[[Declarator], None] _debug_indent = 0 _findall_compat = cast( "Callable", getattr(nodes.Node, "findall", getattr(nodes.Node, "traverse", None)) ) # Doxygen sometimes leaves 'static' in the type, e.g., for "constexpr static # auto f()" # In Doxygen up to somewhere between 1.8.17 to exclusive 1.9.1 the 'friend' part # is also left in the type. See also #767. # Until version 1.11, Doxygen left constexpr (I haven't checked consteval or # constinit) in the type. QUALIFIERS_TO_REMOVE = re.compile(r"\b(static|friend|constexpr|consteval|constinit) ") def strip_legacy_qualifiers(x): return QUALIFIERS_TO_REMOVE.sub("", x) class WithContext: def __init__(self, parent: SphinxRenderer, context: RenderContext): self.context = context self.parent = parent self.previous = None def __enter__(self): assert self.previous is None self.previous = self.parent.context self.parent.set_context(self.context) return self def __exit__(self, et, ev, bt): self.parent.context = self.previous self.previous = None class BaseObject: # Use this class as the first base class to make sure the overrides are used. # Set the content_callback attribute to a function taking a docutils node. breathe_content_callback: ContentCallback | None = None def transform_content(self, contentnode: addnodes.desc_content) -> None: super().transform_content(contentnode) # type: ignore[misc] if self.breathe_content_callback is None: return self.breathe_content_callback(contentnode) # ---------------------------------------------------------------------------- class CPPClassObject(BaseObject, cpp.CPPClassObject): pass class CPPUnionObject(BaseObject, cpp.CPPUnionObject): pass class CPPFunctionObject(BaseObject, cpp.CPPFunctionObject): pass class CPPMemberObject(BaseObject, cpp.CPPMemberObject): pass class CPPTypeObject(BaseObject, cpp.CPPTypeObject): pass class CPPConceptObject(BaseObject, cpp.CPPConceptObject): pass class CPPEnumObject(BaseObject, cpp.CPPEnumObject): pass class CPPEnumeratorObject(BaseObject, cpp.CPPEnumeratorObject): pass # ---------------------------------------------------------------------------- class CStructObject(BaseObject, c.CStructObject): pass class CUnionObject(BaseObject, c.CUnionObject): pass class CFunctionObject(BaseObject, c.CFunctionObject): pass class CMemberObject(BaseObject, c.CMemberObject): pass class CTypeObject(BaseObject, c.CTypeObject): pass class CEnumObject(BaseObject, c.CEnumObject): pass class CEnumeratorObject(BaseObject, c.CEnumeratorObject): pass class CMacroObject(BaseObject, c.CMacroObject): pass # ---------------------------------------------------------------------------- class PyFunction(BaseObject, python.PyFunction): pass class PyAttribute(BaseObject, python.PyAttribute): pass class PyClasslike(BaseObject, python.PyClasslike): pass # ---------------------------------------------------------------------------- # Create multi-inheritance classes to merge BaseObject from Breathe with # classes from phpdomain. # We use capitalization (and the namespace) to differentiate between the two if php is not None or TYPE_CHECKING: class PHPNamespaceLevel(BaseObject, php.PhpNamespacelevel): """Description of a PHP item *in* a namespace (not the space itself).""" pass class PHPClassLike(BaseObject, php.PhpClasslike): pass class PHPClassMember(BaseObject, php.PhpClassmember): pass class PHPGlobalLevel(BaseObject, php.PhpGloballevel): pass # ---------------------------------------------------------------------------- if cs is not None or TYPE_CHECKING: class CSharpCurrentNamespace(BaseObject, cs.CSharpCurrentNamespace): pass class CSharpNamespacePlain(BaseObject, cs.CSharpNamespacePlain): pass class CSharpClass(BaseObject, cs.CSharpClass): pass class CSharpStruct(BaseObject, cs.CSharpStruct): pass class CSharpInterface(BaseObject, cs.CSharpInterface): pass class CSharpInherits(BaseObject, cs.CSharpInherits): pass class CSharpMethod(BaseObject, cs.CSharpMethod): pass class CSharpVariable(BaseObject, cs.CSharpVariable): pass class CSharpProperty(BaseObject, cs.CSharpProperty): pass class CSharpEvent(BaseObject, cs.CSharpEvent): pass class CSharpEnum(BaseObject, cs.CSharpEnum): pass class CSharpEnumValue(BaseObject, cs.CSharpEnumValue): pass class CSharpAttribute(BaseObject, cs.CSharpAttribute): pass class CSharpIndexer(BaseObject, cs.CSharpIndexer): pass class CSharpXRefRole(BaseObject, cs.CSharpXRefRole): pass # ---------------------------------------------------------------------------- class DomainDirectiveFactory: # A mapping from node kinds to domain directives and their names. cpp_classes: dict[str, tuple[type[ObjectDescription], str]] = { "variable": (CPPMemberObject, "var"), "class": (CPPClassObject, "class"), "struct": (CPPClassObject, "struct"), "interface": (CPPClassObject, "class"), "function": (CPPFunctionObject, "function"), "friend": (CPPFunctionObject, "function"), "signal": (CPPFunctionObject, "function"), "slot": (CPPFunctionObject, "function"), "concept": (CPPConceptObject, "concept"), "enum": (CPPEnumObject, "enum"), "enum-class": (CPPEnumObject, "enum-class"), "typedef": (CPPTypeObject, "type"), "using": (CPPTypeObject, "type"), "union": (CPPUnionObject, "union"), "namespace": (CPPTypeObject, "type"), "enumvalue": (CPPEnumeratorObject, "enumerator"), "define": (CMacroObject, "macro"), } c_classes: dict[str, tuple[type[ObjectDescription], str]] = { "variable": (CMemberObject, "var"), "function": (CFunctionObject, "function"), "define": (CMacroObject, "macro"), "struct": (CStructObject, "struct"), "union": (CUnionObject, "union"), "enum": (CEnumObject, "enum"), "enumvalue": (CEnumeratorObject, "enumerator"), "typedef": (CTypeObject, "type"), } python_classes: dict[str, tuple[type[ObjectDescription], str]] = { # TODO: PyFunction is meant for module-level functions # and PyAttribute is meant for class attributes, not module-level variables. # Somehow there should be made a distinction at some point to get the correct # index-text and whatever other things are different. "function": (PyFunction, "function"), "variable": (PyAttribute, "attribute"), "class": (PyClasslike, "class"), "namespace": (PyClasslike, "class"), } php_classes: dict[str, tuple[type[ObjectDescription], str]] if php is not None: php_classes = { "function": (PHPNamespaceLevel, "function"), "class": (PHPClassLike, "class"), "attr": (PHPClassMember, "attr"), "method": (PHPClassMember, "method"), "global": (PHPGlobalLevel, "global"), } php_classes_default = php_classes["class"] # Directive when no matching ones were found cs_classes: dict[str, tuple[type[ObjectDescription], str]] if cs is not None: cs_classes = { # 'doxygen-name': (CSharp class, key in CSharpDomain.object_types) "namespace": (CSharpNamespacePlain, "namespace"), "class": (CSharpClass, "class"), "struct": (CSharpStruct, "struct"), "interface": (CSharpInterface, "interface"), "function": (CSharpMethod, "function"), "method": (CSharpMethod, "method"), "variable": (CSharpVariable, "var"), "property": (CSharpProperty, "property"), "event": (CSharpEvent, "event"), "enum": (CSharpEnum, "enum"), "enumvalue": (CSharpEnumValue, "enumerator"), "attribute": (CSharpAttribute, "attr"), # Fallback to cpp domain "typedef": (CPPTypeObject, "type"), } @staticmethod def create(domain: str, args) -> ObjectDescription: cls: type[ObjectDescription] name: str if domain == "c": cls, name = DomainDirectiveFactory.c_classes[args[0]] elif domain == "py": cls, name = DomainDirectiveFactory.python_classes[args[0]] elif php is not None and domain == "php": separators = php.separators arg_0 = args[0] if any(separators["method"] in n for n in args[1]): if any(separators["attr"] in n for n in args[1]): arg_0 = "attr" else: arg_0 = "method" else: if arg_0 == "variable": arg_0 = "global" if arg_0 in DomainDirectiveFactory.php_classes: cls, name = DomainDirectiveFactory.php_classes[arg_0] else: cls, name = DomainDirectiveFactory.php_classes_default elif cs is not None and domain == "cs": cls, name = DomainDirectiveFactory.cs_classes[args[0]] else: domain = "cpp" cls, name = DomainDirectiveFactory.cpp_classes[args[0]] # Replace the directive name because domain directives don't know how to handle # Breathe's "doxygen" directives. assert ":" not in name args = [domain + ":" + name] + args[1:] return cls(*args) class NodeFinder(nodes.SparseNodeVisitor): """Find the Docutils desc_signature declarator and desc_content nodes.""" def __init__(self, document: nodes.document): super().__init__(document) self.declarator: Declarator | None = None self.content: addnodes.desc_content | None = None def visit_desc_signature(self, node: addnodes.desc_signature): # Find the last signature node because it contains the actual declarator # rather than "template <...>". In Sphinx 1.4.1 we'll be able to use sphinx_cpp_tagname: # https://github.com/breathe-doc/breathe/issues/242 self.declarator = node def visit_desc_signature_line(self, node: addnodes.desc_signature_line): # In sphinx 1.5, there is now a desc_signature_line node within the desc_signature # This should be used instead self.declarator = node def visit_desc_content(self, node: addnodes.desc_content): self.content = node # The SparseNodeVisitor seems to not actually be universally Sparse, # but only for nodes known to Docutils. # So if there are extensions with new node types in the content, # then the visitation will fail. # We anyway don't need to visit the actual content, so skip it. raise nodes.SkipChildren def intersperse(iterable, delimiter): it = iter(iterable) yield next(it) for x in it: yield delimiter yield x def get_param_decl(param: parser.Node_paramType) -> str: def to_string(node: parser.Node_linkedTextType | None) -> str: """Convert Doxygen node content to a string.""" result: list[str] = [] if node is not None: for p in node: if isinstance(p, str): result.append(p) else: result.append(p.value[0]) return " ".join(result) param_type = to_string(param.type) param_name = param.declname or param.defname if not param_name: param_decl = param_type else: param_decl, number_of_subs = re.subn( r"(\((?:\w+::)*[*&]+)(\))", r"\g<1>" + param_name + r"\g<2>", param_type ) if number_of_subs == 0: param_decl = param_type + " " + param_name if param.array: param_decl += param.array if param.defval: param_decl += " = " + to_string(param.defval) return param_decl def get_definition_without_template_args(data_object): """ Return data_object.definition removing any template arguments from the class name in the member function. Otherwise links to classes defined in the same template are not generated correctly. For example in 'Result A< B >::f' we want to remove the '< B >' part. """ definition = data_object.definition if len(data_object.bitfield) > 0: definition += " : " + data_object.bitfield qual_name = "::" + data_object.name if definition.endswith(qual_name): qual_name_start = len(definition) - len(qual_name) pos = qual_name_start - 1 if definition[pos] == ">": bracket_count = 0 # Iterate back through the characters of the definition counting matching braces and # then remove all braces and everything between while pos > 0: if definition[pos] == ">": bracket_count += 1 elif definition[pos] == "<": bracket_count -= 1 if bracket_count == 0: definition = definition[:pos] + definition[qual_name_start:] break pos -= 1 return definition class InlineText(Text): """ Add a custom docutils class to allow parsing inline text. This is to be used inside a @verbatim/@endverbatim block but only the first line is consumed and a inline element is generated as the parent, instead of the paragraph used by Text. """ patterns = {"inlinetext": r""} initial_transitions = [("inlinetext", "text")] def indent(self, match, context, next_state): """ Avoid Text's indent from detecting space prefixed text and doing "funny" stuff; always rely on inlinetext for parsing. """ return self.inlinetext(match, context, next_state) def eof(self, context): """ Text.eof() inserts a paragraph, so override it to skip adding elements. """ return [] def inlinetext(self, match, context, next_state): """ Called by the StateMachine when an inline element is found (which is any text when this class is added as the single transition. """ startline = self.state_machine.abs_line_number() - 1 msg = None try: block = self.state_machine.get_text_block() except UnexpectedIndentationError as err: block, src, srcline = err.args msg = self.reporter.error("Unexpected indentation.", source=src, line=srcline) lines = context + list(block) text, _ = self.inline_text(lines[0], startline) self.parent += text self.parent += msg return [], next_state, [] def get_content(node: parser.Node_docParaType): # Add programlisting nodes to content rather than a separate list, # because programlisting and content nodes can interleave as shown in # https://www.stack.nl/~dimitri/doxygen/manual/examples/include/html/example.html. return ( item for item in node if parser.tag_name_value(item)[0] not in {"parameterlist", "simplesect", "image"} ) def get_parameterlists(node: parser.Node_docParaType) -> Iterable[parser.Node_docParamListType]: pairs = map(parser.tag_name_value, node) # type: ignore[arg-type] return (value for name, value in pairs if name == "parameterlist") # type: ignore[misc] def get_simplesects(node: parser.Node_docParaType) -> Iterable[parser.Node_docSimpleSectType]: pairs = map(parser.tag_name_value, node) # type: ignore[arg-type] return (value for name, value in pairs if name == "simplesect") # type: ignore[misc] def get_images(node: parser.Node_docParaType) -> Iterable[parser.Node_docImageType]: pairs = map(parser.tag_name_value, node) # type: ignore[arg-type] return (value for name, value in pairs if name == "image") # type: ignore[misc] def namespace_strip(config, nodes_: list[nodes.Node]): # In some cases of errors with a declaration there are no nodes # (e.g., variable in function), so perhaps skip (see #671). # If there are nodes, there should be at least 2. if len(nodes_) != 0: assert len(nodes_) >= 2 rst_node = nodes_[1] doc = rst_node.document assert doc is not None finder = NodeFinder(doc) rst_node.walk(finder) assert finder.declarator # the type is set to "Any" to get around missing typing info in # docutils 0.20.1 signode: Any = finder.declarator signode.children = [n for n in signode.children if n.tagname != "desc_addname"] T_sphinxrenderer = TypeVar("T_sphinxrenderer", bound="SphinxRenderer") class NodeHandler(Generic[T_sphinxrenderer, T]): """Dummy callable that associates a set of nodes to a function. This gets unwrapped by NodeVisitor and is never actually called.""" def __init__(self, handler: Callable[[T_sphinxrenderer, T], list[nodes.Node]]): self.handler = handler self.nodes: set[type[parser.NodeOrValue]] = set() def __call__(self, r: T_sphinxrenderer, node: T, /) -> list[nodes.Node]: # pragma: no cover raise TypeError() class TaggedNodeHandler(Generic[T_sphinxrenderer, T]): """Dummy callable that associates a set of nodes to a function. This gets unwrapped by NodeVisitor and is never actually called.""" def __init__(self, handler: Callable[[T_sphinxrenderer, str, T], list[nodes.Node]]): self.handler = handler self.nodes: set[type[parser.NodeOrValue]] = set() def __call__( self, r: T_sphinxrenderer, tag: str, node: T, / ) -> list[nodes.Node]: # pragma: no cover raise TypeError() def node_handler(node: type[parser.NodeOrValue]): def inner( f: Callable[[T_sphinxrenderer, T], list[nodes.Node]], ) -> Callable[[T_sphinxrenderer, T], list[nodes.Node]]: handler: NodeHandler = f if isinstance(f, NodeHandler) else NodeHandler(f) handler.nodes.add(node) return handler return inner def tagged_node_handler(node: type[parser.NodeOrValue]): def inner( f: Callable[[T_sphinxrenderer, str, T], list[nodes.Node]], ) -> Callable[[T_sphinxrenderer, str, T], list[nodes.Node]]: handler: TaggedNodeHandler = f if isinstance(f, TaggedNodeHandler) else TaggedNodeHandler(f) handler.nodes.add(node) return handler return inner class NodeVisitor(type): """Metaclass that collects all methods marked as @node_handler and @tagged_node_handler into the dicts 'node_handlers' and 'tagged_node_handlers' respectively, and assigns the dicts to the class""" def __new__(cls, name, bases, members): handlers = {} tagged_handlers = {} for key, value in members.items(): if isinstance(value, NodeHandler): for n in value.nodes: handlers[n] = value.handler members[key] = value.handler elif isinstance(value, TaggedNodeHandler): for n in value.nodes: tagged_handlers[n] = value.handler members[key] = value.handler members["node_handlers"] = handlers members["tagged_node_handlers"] = tagged_handlers return type.__new__(cls, name, bases, members) # class RenderDebugPrint: # def __init__(self,renderer,node): # self.renderer = renderer # renderer._debug_print_depth = 1 + getattr(renderer,'_debug_print_depth',0) # print(' '*renderer._debug_print_depth,type(node)) # # def __enter__(self): # return self # # def __exit__(self,*args): # self.renderer._debug_print_depth -= 1 class SphinxRenderer(metaclass=NodeVisitor): """ Doxygen node visitor that converts input into Sphinx/RST representation. Each visit method takes a Doxygen node as an argument and returns a list of RST nodes. """ node_handlers: ClassVar[ dict[ type[parser.NodeOrValue], Callable[[SphinxRenderer, parser.NodeOrValue], list[nodes.Node]], ] ] tagged_node_handlers: ClassVar[ dict[ type[parser.NodeOrValue], Callable[[SphinxRenderer, str, parser.NodeOrValue], list[nodes.Node]], ] ] def __init__( self, app: Sphinx, project_info: ProjectInfo, node_stack: list[DataObject], state, document: nodes.document, target_handler: TargetHandler, dox_parser: parser.DoxygenParser, filter_: DoxFilter, ): self.app = app self.project_info = project_info self.qualification_stack = node_stack self.nesting_level = 0 self.state = state self.document = document self.target_handler = target_handler self.dox_parser = dox_parser self.filter_ = filter_ self.context: RenderContext | None = None self.output_defname = True # Nesting level for lists. self.nesting_level = 0 def set_context(self, context: RenderContext) -> None: self.context = context if self.context.domain == "": self.context.domain = self.get_domain() # XXX: fix broken links in XML generated by Doxygen when Doxygen's # SEPARATE_MEMBER_PAGES is set to YES; this function should be harmless # when SEPARATE_MEMBER_PAGES is NO! # # The issue was discussed here: https://github.com/doxygen/doxygen/pull/7971 # # A Doxygen anchor consists of a 32-byte string version of the results of # passing in the stringified identifier or prototype that is being "hashed". # An "a" character is then prefixed to mark it as an anchor. Depending on how # the identifier is linked, it can also get a "g" prefix to mean it is part # of a Doxygen group. This results in an id having either 33 or 34 bytes # (containing a "g" or not). Some identifiers, eg enumerators, get twice that # length to have both a unique enum + unique enumerator, and sometimes they # get two "g" characters as prefix instead of one. def _fixup_separate_member_pages(self, refid: str) -> str: if refid: parts = refid.rsplit("_", 1) if len(parts) == 2 and parts[1].startswith("1"): anchorid = parts[1][1:] if len(anchorid) in {33, 34} and parts[0].endswith(anchorid): return parts[0][: -len(anchorid)] + parts[1] elif len(anchorid) > 34: index = 0 if anchorid.startswith("gg"): index = 1 _len = 35 elif anchorid.startswith("g"): _len = 34 else: _len = 33 if parts[0].endswith(anchorid[index:_len]): return parts[0][: -(_len - index)] + parts[1] return refid def get_refid(self, refid: str) -> str: if self.app.config.breathe_separate_member_pages: refid = self._fixup_separate_member_pages(refid) if self.app.config.breathe_use_project_refids: return "%s%s" % (self.project_info.name(), refid) else: return refid def parse_compound(self, refid: str) -> parser.Node_DoxygenType: return self.dox_parser.parse_compound(refid, self.project_info).root def get_domain(self) -> str: """Returns the domain for the current node.""" def get_filename(node) -> str | None: """Returns the name of a file where the declaration represented by node is located.""" try: return node.location.file except AttributeError: return None assert self.context is not None node_stack = self.context.node_stack node = node_stack[0].value # An enumvalueType node doesn't have location, so use its parent node # for detecting the domain instead. if isinstance(node, (str, parser.Node_enumvalueType)): node = node_stack[1].value filename = get_filename(node) if not filename and isinstance(node, parser.Node_CompoundType): file_data = self.parse_compound(node.refid) filename = get_filename(file_data.compounddef) return self.project_info.domain_for_file(filename) if filename else "" def join_nested_name(self, names: list[str]) -> str: dom = self.get_domain() sep = "::" if not dom or dom == "cpp" else "." return sep.join(names) def run_directive( self, obj_type: str, declaration: str, contentCallback: ContentCallback, options={} ) -> list[nodes.Node]: assert self.context is not None args = [obj_type, [declaration]] + self.context.directive_args[2:] directive = DomainDirectiveFactory.create(self.context.domain, args) assert isinstance(directive, BaseObject) directive.breathe_content_callback = contentCallback # Translate Breathe's no-link option into the standard noindex option. if "no-link" in self.context.directive_args[2]: directive.options["noindex"] = True for k, v in options.items(): directive.options[k] = v assert self.app.env is not None config = self.app.env.config if config.breathe_debug_trace_directives: # pragma: no cover global _debug_indent print( "{}Running directive: .. {}:: {}".format( " " * _debug_indent, directive.name, declaration ) ) _debug_indent += 1 self.nesting_level += 1 nodes_ = directive.run() self.nesting_level -= 1 # TODO: the directive_args seems to be reused between different run_directives # so for now, reset the options. # Remove this once the args are given in a different manner. for k, v in options.items(): del directive.options[k] if config.breathe_debug_trace_directives: # pragma: no cover _debug_indent -= 1 # Filter out outer class names if we are rendering a member as a part of a class content. if self.context.child: namespace_strip(config, nodes_) return nodes_ def handle_compounddef_declaration( self, node: parser.Node_compounddefType, obj_type: str, declaration: str, file_data, new_context, parent_context, display_obj_type: str | None = None, ) -> list[nodes.Node]: def content(contentnode) -> None: if node.includes: for include in node.includes: contentnode.extend( self.render(include, new_context.create_child_context(include)) ) rendered_data = self.render(file_data, parent_context) contentnode.extend(rendered_data) return self.handle_declaration( node, obj_type, declaration, content_callback=content, display_obj_type=display_obj_type ) def handle_declaration( self, node: parser.Node_compounddefType | parser.Node_memberdefType | parser.Node_enumvalueType, obj_type: str, declaration: str, *, content_callback: ContentCallback | None = None, display_obj_type: str | None = None, declarator_callback: DeclaratorCallback | None = None, options={}, ) -> list[nodes.Node]: if content_callback is None: def content(contentnode: addnodes.desc_content): contentnode.extend(self.description(node)) content_callback = content declaration = declaration.replace("\n", " ") nodes_ = self.run_directive(obj_type, declaration, content_callback, options) assert self.app.env is not None target = None if self.app.env.config.breathe_debug_trace_doxygen_ids: target = self.create_doxygen_target(node) if len(target) == 0: print("{}Doxygen target: (none)".format(" " * _debug_indent)) else: print("{}Doxygen target: {}".format(" " * _debug_indent, target[0]["ids"])) # and then one or more # each has a sphinx_line_type which hints what is present in that line # In some cases of errors with a declaration there are no nodes # (e.g., variable in function), so perhaps skip (see #671). if len(nodes_) == 0: return [] assert len(nodes_) >= 2, nodes_ desc = nodes_[1] assert isinstance(desc, addnodes.desc) assert len(desc) >= 1 sig = desc[0] assert isinstance(sig, addnodes.desc_signature) # Insert the member name for use in Sphinx-generated table of contents. if isinstance(node, parser.Node_compounddefType): member_name = node.compoundname else: member_name = node.name if obj_type == "function": member_name += "()" sig.attributes["_toc_name"] = member_name sig.attributes["_toc_parts"] = member_name # if may or may not be a multiline signature isMultiline = sig.get("is_multiline", False) declarator: Declarator | None = None if isMultiline: for line in sig: assert isinstance(line, addnodes.desc_signature_line) if line.sphinx_line_type == "declarator": declarator = line else: declarator = sig assert declarator is not None if display_obj_type is not None: n = declarator[0] if self.get_domain() and self.get_domain() not in ("c", "cpp"): assert isinstance(n, addnodes.desc_annotation) assert n.astext()[-1] == " " txt = display_obj_type + " " declarator[0] = addnodes.desc_annotation(txt, txt) else: assert isinstance(n, addnodes.desc_sig_keyword) declarator[0] = addnodes.desc_sig_keyword(display_obj_type, display_obj_type) if target is None: target = self.create_doxygen_target(node) declarator.insert(0, target) if declarator_callback: declarator_callback(declarator) return nodes_ def get_qualification(self) -> list[str]: if self.nesting_level > 0: return [] assert self.app.env is not None config = self.app.env.config if config.breathe_debug_trace_qualification: def debug_print_node(n): return f"node_type={n.node_type}" global _debug_indent print( "{}{}".format(_debug_indent * " ", debug_print_node(self.qualification_stack[0])) ) _debug_indent += 1 names: list[str] = [] for node in self.qualification_stack[1:]: if config.breathe_debug_trace_qualification: print("{}{}".format(_debug_indent * " ", debug_print_node(node))) if isinstance(node, parser.Node_refType) and len(names) == 0: if config.breathe_debug_trace_qualification: print("{}{}".format(_debug_indent * " ", "res=")) return [] if ( isinstance(node, parser.Node_CompoundType) and node.kind not in [ parser.CompoundKind.file, parser.CompoundKind.namespace, parser.CompoundKind.group, ] ) or isinstance(node, parser.Node_memberdefType): # We skip the 'file' entries because the file name doesn't form part of the # qualified name for the identifier. We skip the 'namespace' entries because if we # find an object through the namespace 'compound' entry in the index.xml then we'll # also have the 'compounddef' entry in our node stack and we'll get it from that. We # need the 'compounddef' entry because if we find the object through the 'file' # entry in the index.xml file then we need to get the namespace name from somewhere names.append(node.name) if ( isinstance(node, parser.Node_compounddefType) and node.kind == parser.DoxCompoundKind.namespace ): # Nested namespaces include their parent namespace(s) in compoundname. ie, # compoundname is 'foo::bar' instead of just 'bar' for namespace 'bar' nested in # namespace 'foo'. We need full compoundname because node_stack doesn't necessarily # include parent namespaces and we stop here in case it does. names.extend(reversed(node.compoundname.split("::"))) break names.reverse() if config.breathe_debug_trace_qualification: print("{}res={}".format(_debug_indent * " ", names)) _debug_indent -= 1 return names # =================================================================================== def get_fully_qualified_name(self): assert self.context names = [] node_stack = self.context.node_stack node = node_stack[0] # If the node is a namespace, use its name because namespaces are skipped in the main loop. if ( isinstance(node.value, parser.Node_CompoundType) and node.value.kind == parser.CompoundKind.namespace ): names.append(node.value.name) for tval in node_stack: node = tval.value if isinstance(node, parser.Node_refType) and len(names) == 0: return "".join(node) if ( isinstance(node, parser.Node_CompoundType) and node.kind not in [ parser.CompoundKind.file, parser.CompoundKind.namespace, parser.CompoundKind.group, ] ) or isinstance(node, parser.Node_memberdefType): # We skip the 'file' entries because the file name doesn't form part of the # qualified name for the identifier. We skip the 'namespace' entries because if we # find an object through the namespace 'compound' entry in the index.xml then we'll # also have the 'compounddef' entry in our node stack and we'll get it from that. We # need the 'compounddef' entry because if we find the object through the 'file' # entry in the index.xml file then we need to get the namespace name from somewhere names.insert(0, node.name) if ( isinstance(node, parser.Node_compounddefType) and node.kind == parser.DoxCompoundKind.namespace ): # Nested namespaces include their parent namespace(s) in compoundname. ie, # compoundname is 'foo::bar' instead of just 'bar' for namespace 'bar' nested in # namespace 'foo'. We need full compoundname because node_stack doesn't necessarily # include parent namespaces and we stop here in case it does. names.insert(0, node.compoundname) break return "::".join(names) def create_template_prefix(self, decl: HasTemplateParamList) -> str: if not decl.templateparamlist: return "" nodes_ = self.render(decl.templateparamlist) return "template<" + "".join(n.astext() for n in nodes_) + ">" def run_domain_directive(self, kind, names): assert self.context domain_directive = DomainDirectiveFactory.create( self.context.domain, [kind, names] + self.context.directive_args[2:] ) # Translate Breathe's no-link option into the standard noindex option. if "no-link" in self.context.directive_args[2]: domain_directive.options["noindex"] = True config = self.app.env.config if config.breathe_debug_trace_directives: # pragma: no cover global _debug_indent print( "{}Running directive (old): .. {}:: {}".format( " " * _debug_indent, domain_directive.name, "".join(names) ) ) _debug_indent += 1 nodes_ = domain_directive.run() if config.breathe_debug_trace_directives: # pragma: no cover _debug_indent -= 1 # Filter out outer class names if we are rendering a member as a part of a class content. if self.context.child: namespace_strip(config, nodes_) return nodes_ def create_doxygen_target(self, node): """Can be overridden to create a target node which uses the doxygen refid information which can be used for creating links between internal doxygen elements. The default implementation should suffice most of the time. """ refid = self.get_refid(node.id) return self.target_handler(self.document, refid) def title(self, node) -> list[nodes.Node]: nodes_ = [] # Variable type or function return type nodes_.extend(self.render_optional(node.type_)) if nodes_: nodes_.append(nodes.Text(" ")) nodes_.append(addnodes.desc_name(text=node.name)) return nodes_ def description(self, node: HasDescriptions) -> list[nodes.Node]: brief = self.render_optional(node.briefdescription) descr = node.detaileddescription if isinstance(node, parser.Node_memberdefType): params = [ parser.Node_docParamListItem( parameterdescription=p.briefdescription, parameternamelist=[ parser.Node_docParamNameList( parametername=[parser.Node_docParamName([p.declname or ""])] ) ], ) for p in node.param if p.briefdescription ] if params: content: list[parser.ListItem_descriptionType] = [] content.append( parser.TaggedValue[Literal["para"], parser.Node_docParaType]( "para", parser.Node_docParaType([ parser.TaggedValue[ Literal["parameterlist"], parser.Node_docParamListType ]( "parameterlist", parser.Node_docParamListType( params, kind=parser.DoxParamListKind.param ), ) ]), ) ) title = None if descr is not None: content.extend(descr) title = descr.title descr = parser.Node_descriptionType(content, title=title) detailed = self.detaileddescription(descr) return brief + detailed def detaileddescription(self, descr: parser.Node_descriptionType | None) -> list[nodes.Node]: detailedCand = self.render_optional(descr) # all field_lists must be at the top-level of the desc_content, so pull them up fieldLists: list[nodes.field_list] = [] admonitions: list[nodes.Node] = [] def pullup(node, typ, dest): for n in list(_findall_compat(node, typ)): del n.parent[n.parent.index(n)] dest.append(n) detailed: list[nodes.Node] = [] for candNode in detailedCand: pullup(candNode, nodes.field_list, fieldLists) pullup(candNode, nodes.note, admonitions) pullup(candNode, nodes.warning, admonitions) # and collapse paragraphs for para in _findall_compat(candNode, nodes.paragraph): parent = para.parent assert parent is None or isinstance(parent, nodes.Element) if parent and len(parent) == 1 and isinstance(parent, nodes.paragraph): para.replace_self(para.children) # and remove empty top-level paragraphs if isinstance(candNode, nodes.paragraph) and len(candNode) == 0: continue detailed.append(candNode) # make one big field list instead to the Sphinx transformer can make it pretty if len(fieldLists) > 1: fieldList = nodes.field_list() for fl in fieldLists: fieldList.extend(fl) fieldLists = [fieldList] # using "extend" instead of addition is slightly more verbose but is # needed to get around the mypy issue # https://github.com/python/mypy/issues/3933 if self.app.config.breathe_order_parameters_first: detailed.extend(fieldLists) detailed.extend(admonitions) else: detailed.extend(admonitions) detailed.extend(fieldLists) return detailed def update_signature(self, signature, obj_type): """Update the signature node if necessary, e.g. add qualifiers.""" prefix = obj_type + " " annotation = addnodes.desc_annotation(prefix, prefix) if signature[0].tagname != "desc_name": signature[0] = annotation else: signature.insert(0, annotation) def render_declaration( self, node: parser.Node_memberdefType, declaration=None, description=None, **kwargs ): if declaration is None: declaration = self.get_fully_qualified_name() obj_type = kwargs.get("objtype", None) if obj_type is None: obj_type = node.kind.value nodes_ = self.run_domain_directive(obj_type, [declaration.replace("\n", " ")]) target = None if self.app.env.config.breathe_debug_trace_doxygen_ids: target = self.create_doxygen_target(node) if len(target) == 0: print("{}Doxygen target (old): (none)".format(" " * _debug_indent)) else: print("{}Doxygen target (old): {}".format(" " * _debug_indent, target[0]["ids"])) rst_node = nodes_[1] doc = rst_node.document assert doc is not None finder = NodeFinder(doc) rst_node.walk(finder) assert finder.declarator is not None assert finder.content is not None signode = finder.declarator contentnode = finder.content update_signature = kwargs.get("update_signature", None) if update_signature is not None: update_signature(signode, obj_type) if description is None: description = self.description(node) if not self.app.env.config.breathe_debug_trace_doxygen_ids: target = self.create_doxygen_target(node) assert target is not None signode.insert(0, target) contentnode.extend(description) return nodes_ @node_handler(parser.Node_DoxygenTypeIndex) def visit_doxygen(self, node: parser.Node_DoxygenTypeIndex) -> list[nodes.Node]: nodelist: list[nodes.Node] = [] # Process all the compound children for n in node.compound: nodelist.extend(self.render(n)) return nodelist @node_handler(parser.Node_DoxygenType) def visit_doxygendef(self, node: parser.Node_DoxygenType) -> list[nodes.Node]: assert len(node.compounddef) == 1 return self.render(node.compounddef[0]) def visit_union(self, node: HasRefID) -> list[nodes.Node]: # Read in the corresponding xml file and process file_data = self.parse_compound(node.refid) assert len(file_data.compounddef) == 1 nodeDef = file_data.compounddef[0] assert self.context is not None parent_context = self.context.create_child_context(file_data) new_context = parent_context.create_child_context(nodeDef) with WithContext(self, new_context): names = self.get_qualification() if self.nesting_level == 0: names.extend(nodeDef.compoundname.split("::")) else: names.append(nodeDef.compoundname.split("::")[-1]) declaration = self.join_nested_name(names) nodes_ = self.handle_compounddef_declaration( nodeDef, nodeDef.kind.value, declaration, file_data, new_context, parent_context ) return nodes_ def visit_class(self, node: HasRefID) -> list[nodes.Node]: # Read in the corresponding xml file and process file_data = self.parse_compound(node.refid) assert len(file_data.compounddef) == 1 nodeDef = file_data.compounddef[0] assert self.context is not None parent_context = self.context.create_child_context(file_data) new_context = parent_context.create_child_context(nodeDef) domain = self.get_domain() with WithContext(self, new_context): # Pretend that the signature is being rendered in context of the # definition, for proper domain detection kind = nodeDef.kind # Defer to domains specific directive. names = self.get_qualification() # strip out any template arguments before splitting on '::', to # avoid errors if a template specialization has qualified arguments # (see examples/specific/cpp_ns_template_specialization) cleaned_name, _sep, _rest = nodeDef.compoundname.partition("<") cname = split_name(cleaned_name) if self.nesting_level == 0: names.extend(cname) else: names.append(cname[-1]) decls = [ self.create_template_prefix(nodeDef), self.join_nested_name(names), ] # add base classes if len(nodeDef.basecompoundref) != 0: decls.append(":") first = True for base in nodeDef.basecompoundref: if not first: decls.append(",") else: first = False if base.prot is not None and domain != "cs": decls.append(base.prot.value) if base.virt == parser.DoxVirtualKind.virtual: decls.append("virtual") decls.append(base[0]) declaration = " ".join(decls) assert kind in ( parser.DoxCompoundKind.class_, parser.DoxCompoundKind.struct, parser.DoxCompoundKind.interface, ) display_obj_type = "interface" if kind == parser.DoxCompoundKind.interface else None nodes_ = self.handle_compounddef_declaration( nodeDef, nodeDef.kind.value, declaration, file_data, new_context, parent_context, display_obj_type, ) if "members-only" in self.context.directive_args[2]: assert len(nodes_) >= 2 assert isinstance(nodes_[1], addnodes.desc) assert len(nodes_[1]) >= 2 assert isinstance(nodes_[1][1], addnodes.desc_content) return list(nodes_[1][1].children) return nodes_ def visit_namespace(self, node: HasRefID) -> list[nodes.Node]: # Read in the corresponding xml file and process file_data = self.parse_compound(node.refid) assert len(file_data.compounddef) == 1 nodeDef = file_data.compounddef[0] assert self.context is not None parent_context = self.context.create_child_context(file_data) new_context = parent_context.create_child_context(nodeDef) with WithContext(self, new_context): # Pretend that the signature is being rendered in context of the # definition, for proper domain detection names = self.get_qualification() if self.nesting_level == 0: names.extend(nodeDef.compoundname.split("::")) else: names.append(nodeDef.compoundname.split("::")[-1]) declaration = self.join_nested_name(names) display_obj_type = "namespace" if self.get_domain() != "py" else "module" nodes_ = self.handle_compounddef_declaration( nodeDef, nodeDef.kind.value, declaration, file_data, new_context, parent_context, display_obj_type, ) return nodes_ def visit_compound( self, node: HasRefID, render_empty_node=True, *, get_node_info: Callable[[parser.Node_DoxygenType], tuple[str, parser.DoxCompoundKind]] | None = None, render_signature: Callable[ [parser.Node_DoxygenType, Sequence[nodes.Element], str, parser.DoxCompoundKind], tuple[list[nodes.Node], addnodes.desc_content], ] | None = None, ) -> list[nodes.Node]: # Read in the corresponding xml file and process file_data = self.parse_compound(node.refid) assert len(file_data.compounddef) == 1 def def_get_node_info(file_data) -> tuple[str, parser.DoxCompoundKind]: assert isinstance(node, parser.Node_CompoundType) return node.name, parser.DoxCompoundKind(node.kind.value) if get_node_info is None: get_node_info = def_get_node_info name, kind = get_node_info(file_data) if kind == parser.DoxCompoundKind.union: dom = self.get_domain() assert not dom or dom in ("c", "cpp") return self.visit_union(node) elif kind in ( parser.DoxCompoundKind.struct, parser.DoxCompoundKind.class_, parser.DoxCompoundKind.interface, ): dom = self.get_domain() if not dom or dom in ("c", "cpp", "py", "cs"): return self.visit_class(node) elif kind == parser.DoxCompoundKind.namespace: dom = self.get_domain() if not dom or dom in ("c", "cpp", "py", "cs"): return self.visit_namespace(node) assert self.context is not None parent_context = self.context.create_child_context(file_data) new_context = parent_context.create_child_context(file_data.compounddef[0]) rendered_data = self.render(file_data, parent_context) if not rendered_data and not render_empty_node: return [] def def_render_signature( file_data: parser.Node_DoxygenType, doxygen_target, name, kind: parser.DoxCompoundKind ) -> tuple[list[nodes.Node], addnodes.desc_content]: # Defer to domains specific directive. assert len(file_data.compounddef) == 1 templatePrefix = self.create_template_prefix(file_data.compounddef[0]) arg = "%s %s" % (templatePrefix, self.get_fully_qualified_name()) # add base classes if kind in (parser.DoxCompoundKind.class_, parser.DoxCompoundKind.struct): bs: list[str] = [] for base in file_data.compounddef[0].basecompoundref: b: list[str] = [] if base.prot is not None: b.append(base.prot.value) if base.virt == parser.DoxVirtualKind.virtual: b.append("virtual") b.append(base[0]) bs.append(" ".join(b)) if len(bs) != 0: arg += " : " arg += ", ".join(bs) assert self.context is not None self.context.directive_args[1] = [arg] nodes_ = self.run_domain_directive(kind.value, self.context.directive_args[1]) rst_node = nodes_[1] doc = rst_node.document assert doc is not None finder = NodeFinder(doc) rst_node.walk(finder) assert finder.declarator is not None assert finder.content is not None if kind in (parser.CompoundKind.interface, parser.CompoundKind.namespace): # This is not a real C++ declaration type that Sphinx supports, # so we hax the replacement of it. finder.declarator[0] = addnodes.desc_annotation(kind.value + " ", kind.value + " ") rst_node.children[0].insert(0, doxygen_target) return nodes_, finder.content if render_signature is None: render_signature = def_render_signature refid = self.get_refid(node.refid) with WithContext(self, new_context): # Pretend that the signature is being rendered in context of the # definition, for proper domain detection nodes_, contentnode = render_signature( file_data, self.target_handler(self.document, refid), name, kind ) if file_data.compounddef[0].includes: for include in file_data.compounddef[0].includes: contentnode.extend(self.render(include, new_context.create_child_context(include))) contentnode.extend(rendered_data) return nodes_ def visit_file(self, node: parser.Node_CompoundType) -> list[nodes.Node]: def render_signature( file_data, doxygen_target, name, kind ) -> tuple[list[nodes.Node], addnodes.desc_content]: assert self.context is not None options = self.context.directive_args[2] rst_node: nodes.container | addnodes.desc if "content-only" in options: rst_node = nodes.container() else: rst_node = addnodes.desc() # Build targets for linking targets = [] targets.extend(doxygen_target) title_signode = addnodes.desc_signature() title_signode.extend(targets) # Set up the title # # For groups & pages we render the 'title' instead of the 'name' # as it more human friendly if kind in [DoxCompoundKind.group, DoxCompoundKind.page] and file_data.compounddef: if "no-title" not in options: title_signode.append(nodes.emphasis(text=kind.value)) title_signode.append(nodes.Text(" ")) title_signode.append( addnodes.desc_name(text=file_data.compounddef[0].title) ) else: title_signode.append(nodes.emphasis(text=kind.value)) title_signode.append(nodes.Text(" ")) title_signode.append(addnodes.desc_name(text=name)) rst_node.append(title_signode) rst_node.document = self.state.document rst_node["objtype"] = kind.value rst_node["domain"] = self.get_domain() or "cpp" contentnode = addnodes.desc_content() rst_node.append(contentnode) return [rst_node], contentnode return self.visit_compound(node, render_signature=render_signature) # We store both the identified and appropriate title text here as we want to define the order # here and the titles for the SectionDefTypeSubRenderer but we don't want the repetition of # having two lists in case they fall out of sync # # If this list is edited, also change the sections option documentation for # the doxygen(auto)file directive in documentation/source/file.rst. sections = [ (parser.DoxSectionKind.user_defined, "User Defined"), (parser.DoxSectionKind.public_type, "Public Types"), (parser.DoxSectionKind.public_func, "Public Functions"), (parser.DoxSectionKind.public_attrib, "Public Members"), (parser.DoxSectionKind.public_slot, "Public Slots"), (parser.DoxSectionKind.signal, "Signals"), (parser.DoxSectionKind.dcop_func, "DCOP Function"), (parser.DoxSectionKind.property, "Properties"), (parser.DoxSectionKind.event, "Events"), (parser.DoxSectionKind.public_static_func, "Public Static Functions"), (parser.DoxSectionKind.public_static_attrib, "Public Static Attributes"), (parser.DoxSectionKind.protected_type, "Protected Types"), (parser.DoxSectionKind.protected_func, "Protected Functions"), (parser.DoxSectionKind.protected_attrib, "Protected Attributes"), (parser.DoxSectionKind.protected_slot, "Protected Slots"), (parser.DoxSectionKind.protected_static_func, "Protected Static Functions"), (parser.DoxSectionKind.protected_static_attrib, "Protected Static Attributes"), (parser.DoxSectionKind.package_type, "Package Types"), (parser.DoxSectionKind.package_func, "Package Functions"), (parser.DoxSectionKind.package_attrib, "Package Attributes"), (parser.DoxSectionKind.package_static_func, "Package Static Functions"), (parser.DoxSectionKind.package_static_attrib, "Package Static Attributes"), (parser.DoxSectionKind.private_type, "Private Types"), (parser.DoxSectionKind.private_func, "Private Functions"), (parser.DoxSectionKind.private_attrib, "Private Members"), (parser.DoxSectionKind.private_slot, "Private Slots"), (parser.DoxSectionKind.private_static_func, "Private Static Functions"), (parser.DoxSectionKind.private_static_attrib, "Private Static Attributes"), (parser.DoxSectionKind.friend, "Friends"), (parser.DoxSectionKind.related, "Related"), (parser.DoxSectionKind.define, "Defines"), (parser.DoxSectionKind.prototype, "Prototypes"), (parser.DoxSectionKind.typedef, "Typedefs"), # (parser.DoxSectionKind.concept, "Concepts"), (parser.DoxSectionKind.enum, "Enums"), (parser.DoxSectionKind.func, "Functions"), (parser.DoxSectionKind.var, "Variables"), ] def render_iterable( self, iterable: Iterable[parser.NodeOrValue], tag: str | None = None ) -> list[nodes.Node]: output: list[nodes.Node] = [] for entry in iterable: output.extend(self.render(entry, tag=tag)) return output def render_tagged_iterable( self, iterable: Iterable[parser.TaggedValue[str, parser.NodeOrValue] | str] ) -> list[nodes.Node]: output: list[nodes.Node] = [] for entry in iterable: output.extend(self.render_tagged(entry)) return output @node_handler(parser.Node_compounddefType) def visit_compounddef(self, node: parser.Node_compounddefType) -> list[nodes.Node]: assert self.context is not None options = self.context.directive_args[2] section_order = None if "sections" in options: section_order = {sec: i for i, sec in enumerate(options["sections"].split(" "))} membergroup_order = None if "membergroups" in options: membergroup_order = {sec: i for i, sec in enumerate(options["membergroups"].split(" "))} nodemap: dict[int, list[nodes.Node]] = {} def addnode(kind: str, lam): if section_order is None: nodemap[len(nodemap)] = lam() elif kind in section_order: nodemap.setdefault(section_order[kind], []).extend(lam()) if "members-only" not in options: if "allow-dot-graphs" in options: addnode( "incdepgraph", lambda: self.render_optional(node.incdepgraph, "incdepgraph") ) addnode( "invincdepgraph", lambda: self.render_optional(node.invincdepgraph, "invincdepgraph"), ) addnode( "inheritancegraph", lambda: self.render_optional(node.inheritancegraph, "inheritancegraph"), ) addnode( "collaborationgraph", lambda: self.render_optional(node.collaborationgraph, "collaborationgraph"), ) addnode("briefdescription", lambda: self.render_optional(node.briefdescription)) addnode( "detaileddescription", lambda: self.detaileddescription(node.detaileddescription) ) def render_derivedcompoundref(node): if node is None: return [] output = self.render_iterable(node) if not output: return [] return [ nodes.paragraph( "", "", nodes.Text("Subclassed by "), *intersperse(output, nodes.Text(", ")) ) ] addnode( "derivedcompoundref", lambda: render_derivedcompoundref(node.derivedcompoundref) ) section_nodelists: dict[str, list[nodes.Node]] = {} # Get all sub sections for sectiondef in node.sectiondef: kind = sectiondef.kind if section_order is not None and kind.value not in section_order: continue header = sectiondef.header if membergroup_order is not None and header not in membergroup_order: continue child_nodes = self.render(sectiondef) if not child_nodes: # Skip empty section continue rst_node = nodes.container(classes=["breathe-sectiondef"]) rst_node.document = self.state.document rst_node["objtype"] = kind.value rst_node.extend(child_nodes) # We store the nodes as a list against the kind in a dictionary as the kind can be # 'user-edited' and that can repeat so this allows us to collect all the 'user-edited' # entries together section_nodelists.setdefault(kind.value, []).append(rst_node) # Order the results in an appropriate manner for kind, _ in self.sections: addnode(kind.value, lambda: section_nodelists.get(kind.value, [])) # Take care of innerclasses addnode("innerclass", lambda: self.render_iterable(node.innerclass, "innerclass")) addnode( "innernamespace", lambda: self.render_iterable(node.innernamespace, "innernamespace") ) if "inner" in options: for cnode in node.innergroup: file_data = self.parse_compound(cnode.refid) assert len(file_data.compounddef) == 1 inner = file_data.compounddef[0] addnode("innergroup", lambda: self.visit_compounddef(inner)) nodelist = [] for _, nodes_ in sorted(nodemap.items()): nodelist += nodes_ return nodelist section_titles = dict(sections) @node_handler(parser.Node_sectiondefType) def visit_sectiondef(self, node: parser.Node_sectiondefType) -> list[nodes.Node]: assert self.context is not None options = self.context.directive_args[2] node_list = [] node_list.extend(self.render_optional(node.description)) # Get all the memberdef info member_def: Iterable[parser.Node_memberdefType] if "sort" in options: member_def = sorted(node.memberdef, key=lambda x: x.name) else: member_def = node.memberdef node_list.extend(self.render_iterable(member_def)) if node_list: if "members-only" in options: return node_list text = self.section_titles[node.kind] # Override default name for user-defined sections. Use "Unnamed # Group" if the user didn't name the section # This is different to Doxygen which will track the groups and name # them Group1, Group2, Group3, etc. if node.kind == parser.DoxSectionKind.user_defined: if node.header: text = node.header else: text = "Unnamed Group" # Use rubric for the title because, unlike the docutils element "section", # it doesn't interfere with the document structure. idtext = text.replace(" ", "-").lower() rubric = nodes.rubric( text=text, classes=["breathe-sectiondef-title"], ids=["breathe-section-title-" + idtext], ) res: list[nodes.Node] = [rubric] return res + node_list return [] @node_handler(parser.Node_docRefTextType) @node_handler(parser.Node_refTextType) def visit_docreftext( self, node: parser.Node_docRefTextType | parser.Node_incType | parser.Node_refTextType ) -> list[nodes.Node]: nodelist: list[nodes.Node] if isinstance(node, parser.Node_incType): nodelist = self.render_iterable(node) else: nodelist = self.render_tagged_iterable(node) # TODO: "para" in compound.xsd is an empty tag; figure out what this # is supposed to do # for name, value in map(parser.tag_name_value, node): # if name == "para": # nodelist.extend(self.render(value)) refid = self.get_refid(node.refid or "") assert nodelist nodelist = [ addnodes.pending_xref( "", reftype="ref", refdomain="std", refexplicit=True, refid=refid, reftarget=refid, *nodelist, ) ] return nodelist @node_handler(parser.Node_docHeadingType) def visit_docheading(self, node: parser.Node_docHeadingType) -> list[nodes.Node]: """Heading renderer. Renders embedded headlines as emphasized text. Different heading levels are not supported. """ nodelist = self.render_tagged_iterable(node) return [nodes.emphasis("", "", *nodelist)] @node_handler(parser.Node_docParaType) def visit_docpara(self, node: parser.Node_docParaType) -> list[nodes.Node]: """ tags in the Doxygen output tend to contain either text or a single other tag of interest. So whilst it looks like we're combined descriptions and program listings and other things, in the end we generally only deal with one per para tag. Multiple neighbouring instances of these things tend to each be in a separate neighbouring para tag. """ nodelist = [] if self.context and self.context.directive_args[0] == "doxygenpage": nodelist.extend(self.render_tagged_iterable(node)) else: contentNodeCands = [] for item in get_content(node): contentNodeCands.extend(self.render_tagged(item)) # if there are consecutive nodes.Text we should collapse them # and rerender them to ensure the right paragraphifaction contentNodes: list[nodes.Node] = [] for n in contentNodeCands: if len(contentNodes) != 0 and isinstance(contentNodes[-1], nodes.Text): if isinstance(n, nodes.Text): prev = contentNodes.pop() contentNodes.extend(self.render_string(prev.astext() + n.astext())) continue # we have handled this node contentNodes.append(n) nodelist.extend(contentNodes) nodelist.extend(self.render_iterable(get_images(node))) paramList = self.render_iterable(get_parameterlists(node)) defs = [] fields = [] for n in self.render_iterable(get_simplesects(node)): if isinstance(n, nodes.definition_list_item): defs.append(n) elif isinstance(n, nodes.field_list): fields.append(n) else: nodelist.append(n) # note: all these gets pulled up and reordered in description() if len(defs) != 0: deflist = nodes.definition_list("", *defs) nodelist.append(deflist) nodelist.extend(paramList) nodelist.extend(fields) # And now all kinds of cleanup steps # ---------------------------------- # trim trailing whitespace while len(nodelist) != 0: last = nodelist[-1] if not isinstance(last, nodes.Text): break if last.astext().strip() != "": break nodelist.pop() # https://github.com/breathe-doc/breathe/issues/827 # verbatim nodes should not be in a paragraph: if len(nodelist) == 1 and isinstance(nodelist[0], nodes.literal_block): return nodelist return [nodes.paragraph("", "", *nodelist)] visit_docparblock = node_handler(parser.Node_docParBlockType)(render_iterable) @node_handler(parser.Node_docBlockQuoteType) def visit_docblockquote(self, node: parser.Node_docBlockQuoteType) -> list[nodes.Node]: nodelist = self.render_iterable(node) # catch block quote attributions here; the tag is the only identifier, # and it is nested within a subsequent tag if nodelist and nodelist[-1].astext().startswith("—"): # nodes.attribution prepends the author with an emphasized dash. # replace the — placeholder and strip any leading whitespace. text = nodelist[-1].astext().replace("—", "").lstrip() nodelist[-1] = nodes.attribution("", text) return [nodes.block_quote("", classes=[], *nodelist)] @node_handler(parser.Node_docImageType) def visit_docimage(self, node: parser.Node_docImageType) -> list[nodes.Node]: """Output docutils image node using name attribute from xml as the uri""" path_to_image = node.name if path_to_image is None: path_to_image = "" elif not url_re.match(path_to_image): path_to_image = self.project_info.sphinx_abs_path_to_file(path_to_image) options = {"uri": path_to_image} return [nodes.image("", **options)] @node_handler(parser.Node_docURLLink) def visit_docurllink(self, node: parser.Node_docURLLink) -> list[nodes.Node]: """Url Link Renderer""" nodelist = self.render_tagged_iterable(node) return [nodes.reference("", "", refuri=node.url, *nodelist)] @tagged_node_handler(parser.Node_docMarkupType) def visit_docmarkup(self, tag: str, node: parser.Node_docMarkupType) -> list[nodes.Node]: nodelist = self.render_tagged_iterable(node) creator: type[nodes.TextElement] = nodes.inline if tag == "emphasis": creator = nodes.emphasis elif tag == "computeroutput": creator = nodes.literal elif tag == "bold": creator = nodes.strong elif tag == "superscript": creator = nodes.superscript elif tag == "subscript": creator = nodes.subscript elif tag == "center": print("Warning: does not currently handle 'center' text display") elif tag == "small": print("Warning: does not currently handle 'small' text display") return [creator("", "", *nodelist)] @node_handler(parser.Node_docSect1Type) def visit_docsect1(self, node: parser.Node_docSect1Type) -> list[nodes.Node]: return self.visit_docsectN(node, 0) @node_handler(parser.Node_docSect2Type) def visit_docsect2(self, node: parser.Node_docSect2Type) -> list[nodes.Node]: return self.visit_docsectN(node, 1) @node_handler(parser.Node_docSect3Type) def visit_docsect3(self, node: parser.Node_docSect3Type) -> list[nodes.Node]: return self.visit_docsectN(node, 2) def visit_docsectN( self, node: parser.Node_docSect1Type | parser.Node_docSect2Type | parser.Node_docSect3Type, depth: int, ) -> list[nodes.Node]: """ Docutils titles are defined by their level inside the document. Doxygen command mapping to XML element name: @section == sect1, @subsection == sect2, @subsubsection == sect3 """ # sect2 and sect3 elements can appear outside of sect1/sect2 elements so # we need to check how deep we actually are actual_d = 0 assert self.context for n in self.context.node_stack[1:]: if isinstance( n.value, (parser.Node_docSect1Type, parser.Node_docSect2Type, parser.Node_docSect3Type), ): actual_d += 1 title_nodes = self.render_tagged_iterable(node.title) if node.title else [] if actual_d == depth: section = nodes.section() section["ids"].append(self.get_refid(node.id)) section += nodes.title("", "", *title_nodes) section += self.create_doxygen_target(node) section += self.render_tagged_iterable(node) return [section] else: # If the actual depth doesn't match the specified depth, don't # create a section element, just use an emphasis element as the # title. # # This is probably not the best way to handle such a case. I chose # it because it's what visit_docheading does. It shouldn't come up # often, anyway. # -- Rouslan content: list[nodes.Node] = [nodes.emphasis("", "", *title_nodes)] content.extend(self.create_doxygen_target(node)) content.extend(self.render_tagged_iterable(node)) return content @node_handler(parser.Node_docSimpleSectType) def visit_docsimplesect(self, node: parser.Node_docSimpleSectType) -> list[nodes.Node]: """Other Type documentation such as Warning, Note, Returns, etc""" # for those that should go into a field list, just render them as that, # and it will be pulled up later nodelist = self.render_iterable(node.para) if node.kind in ( parser.DoxSimpleSectKind.pre, parser.DoxSimpleSectKind.post, parser.DoxSimpleSectKind.return_, ): return [ nodes.field_list( "", nodes.field( "", nodes.field_name("", nodes.Text(node.kind.value)), nodes.field_body("", *nodelist), ), ) ] elif node.kind == parser.DoxSimpleSectKind.warning: return [nodes.warning("", *nodelist)] elif node.kind == parser.DoxSimpleSectKind.note: return [nodes.note("", *nodelist)] elif node.kind == parser.DoxSimpleSectKind.see: return [addnodes.seealso("", *nodelist)] elif node.kind == parser.DoxSimpleSectKind.remark: nodelist.insert(0, nodes.title("", nodes.Text(node.kind.value.capitalize()))) return [nodes.admonition("", classes=[node.kind.value], *nodelist)] if node.kind == parser.DoxSimpleSectKind.par: text = self.render(node.title) else: text = [nodes.Text(node.kind.value.capitalize())] # TODO: is this working as intended? there is something strange with the types title = nodes.strong("", "", *text) term = nodes.term("", "", title) definition = nodes.definition("", *nodelist) return [nodes.definition_list_item("", term, definition)] visit_doctitle = node_handler(parser.Node_docTitleType)(render_tagged_iterable) @node_handler(parser.Node_docFormulaType) def visit_docformula(self, node: parser.Node_docFormulaType) -> list[nodes.Node]: nodelist: list[nodes.Node] = [] for latex in node: docname = self.state.document.settings.env.docname # Strip out the doxygen markup that slips through # Either inline if latex.startswith("$") and latex.endswith("$"): latex = latex[1:-1] nodelist.append( nodes.math(text=latex, label=None, nowrap=False, docname=docname, number=None) ) # Else we're multiline else: if latex.startswith("\\[") and latex.endswith("\\]"): latex = latex[2:-2:] nodelist.append( nodes.math_block( text=latex, label=None, nowrap=False, docname=docname, number=None ) ) return nodelist @node_handler(parser.Node_listingType) def visit_listing(self, node: parser.Node_listingType) -> list[nodes.Node]: nodelist: list[nodes.Node] = [] for i, item in enumerate(node.codeline): # Put new lines between the lines if i: nodelist.append(nodes.Text("\n")) nodelist.extend(self.render(item)) # Add blank string at the start otherwise for some reason it renders # the pending_xref tags around the kind in plain text block = nodes.literal_block("", "", *nodelist) domain = filetypes.get_pygments_alias(node.filename or "") or filetypes.get_extension( node.filename or "" ) if domain: block["language"] = domain return [block] @node_handler(parser.Node_codelineType) def visit_codeline(self, node: parser.Node_codelineType) -> list[nodes.Node]: return self.render_iterable(node.highlight) visit_highlight = node_handler(parser.Node_highlightType)(render_tagged_iterable) def _nested_inline_parse_with_titles(self, content, node) -> str: """ This code is basically a customized nested_parse_with_titles from docutils, using the InlineText class on the statemachine. """ surrounding_title_styles = self.state.memo.title_styles surrounding_section_level = self.state.memo.section_level self.state.memo.title_styles = [] self.state.memo.section_level = 0 try: return self.state.nested_parse( content, 0, node, match_titles=1, state_machine_kwargs={ "state_classes": (InlineText,), "initial_state": "InlineText", }, ) finally: self.state.memo.title_styles = surrounding_title_styles self.state.memo.section_level = surrounding_section_level def visit_verbatim(self, node: str) -> list[nodes.Node]: if not node.strip().startswith("embed:rst"): # Remove trailing new lines. Purely subjective call from viewing results text = node.rstrip() # Handle has a preformatted text return [nodes.literal_block(text, text)] is_inline = False # do we need to strip leading asterisks? # NOTE: We could choose to guess this based on every line starting with '*'. # However This would have a side-effect for any users who have an rst-block # consisting of a simple bullet list. # For now we just look for an extended embed tag if node.strip().startswith("embed:rst:leading-asterisk"): lines: Iterable[str] = node.splitlines() # Replace the first * on each line with a blank space lines = [text.replace("*", " ", 1) for text in lines] node = "\n".join(lines) # do we need to strip leading ///? elif node.strip().startswith("embed:rst:leading-slashes"): lines = node.splitlines() # Replace the /// on each line with three blank spaces lines = [text.replace("///", " ", 1) for text in lines] node = "\n".join(lines) elif node.strip().startswith("embed:rst:inline"): # Inline all text inside the verbatim node = "".join(node.splitlines()) is_inline = True if is_inline: node = node.replace("embed:rst:inline", "", 1) else: # Remove the first line which is "embed:rst[:leading-asterisk]" node = "\n".join(node.split("\n")[1:]) # Remove starting whitespace node = textwrap.dedent(node) # Inspired by autodoc.py in Sphinx rst = StringList() for line in node.split("\n"): rst.append(line, "") # Parent node for the generated node subtree rst_node: nodes.Node if is_inline: rst_node = nodes.inline() else: rst_node = nodes.paragraph() rst_node.document = self.state.document # Generate node subtree if is_inline: self._nested_inline_parse_with_titles(rst, rst_node) else: nested_parse_with_titles(self.state, rst, rst_node) return [rst_node] @node_handler(parser.Node_incType) def visit_inc(self, node: parser.Node_incType) -> list[nodes.Node]: if not self.app.config.breathe_show_include: return [] compound_link: list[nodes.Node] = [nodes.Text("".join(node))] if node.refid: compound_link = self.visit_docreftext(node) if node.local: text = [nodes.Text('#include "'), *compound_link, nodes.Text('"')] else: text = [nodes.Text("#include <"), *compound_link, nodes.Text(">")] return [nodes.container("", nodes.emphasis("", "", *text))] @node_handler(parser.Node_refType) def visit_ref(self, node: parser.Node_refType) -> list[nodes.Node]: def get_node_info(file_data: parser.Node_DoxygenType): name = "".join(node) name = name.rsplit("::", 1)[-1] assert len(file_data.compounddef) == 1 return name, file_data.compounddef[0].kind return self.visit_compound(node, False, get_node_info=get_node_info) @node_handler(parser.Node_docListItemType) def visit_doclistitem(self, node: parser.Node_docListItemType) -> list[nodes.Node]: """List item renderer. Render all the children depth-first. Upon return expand the children node list into a docutils list-item. """ nodelist = self.render_iterable(node) return [nodes.list_item("", *nodelist)] numeral_kind = ["arabic", "loweralpha", "lowerroman", "upperalpha", "upperroman"] def render_unordered(self, children) -> list[nodes.Node]: nodelist_list = nodes.bullet_list("", *children) return [nodelist_list] def render_enumerated(self, children, nesting_level) -> list[nodes.Node]: nodelist_list = nodes.enumerated_list("", *children) idx = nesting_level % len(SphinxRenderer.numeral_kind) nodelist_list["enumtype"] = SphinxRenderer.numeral_kind[idx] nodelist_list["prefix"] = "" nodelist_list["suffix"] = "." return [nodelist_list] @tagged_node_handler(parser.Node_docListType) def visit_doclist(self, tag: str, node: parser.Node_docListType) -> list[nodes.Node]: """List renderer The specifics of the actual list rendering are handled by the decorator around the generic render function. Render all the children depth-first.""" """ Call the wrapped render function. Update the nesting level for the enumerated lists. """ if tag == "itemizedlist": val = self.render_iterable(node) return self.render_unordered(children=val) elif tag == "orderedlist": self.nesting_level += 1 val = self.render_iterable(node) self.nesting_level -= 1 return self.render_enumerated(children=val, nesting_level=self.nesting_level) return [] @node_handler(parser.Node_compoundRefType) def visit_compoundref(self, node: parser.Node_compoundRefType) -> list[nodes.Node]: nodelist: list[nodes.Node] = self.render_iterable(node) refid = None if node.refid is not None: refid = self.get_refid(node.refid) if refid is not None: assert nodelist nodelist = [ addnodes.pending_xref( "", reftype="ref", refdomain="std", refexplicit=True, refid=refid, reftarget=refid, *nodelist, ) ] return nodelist @node_handler(parser.Node_docXRefSectType) def visit_docxrefsect(self, node: parser.Node_docXRefSectType) -> list[nodes.Node]: assert self.app.env is not None signode = addnodes.desc_signature() title = node.xreftitle[0] + ":" titlenode = nodes.emphasis(text=title) ref = addnodes.pending_xref( "", reftype="ref", refdomain="std", refexplicit=True, reftarget=node.id, refdoc=self.app.env.docname, *[titlenode], ) signode += ref nodelist = self.render(node.xrefdescription) contentnode = addnodes.desc_content() contentnode += nodelist descnode = addnodes.desc() descnode["objtype"] = "xrefsect" descnode["domain"] = self.get_domain() or "cpp" descnode += signode descnode += contentnode return [descnode] @node_handler(parser.Node_docVariableListType) def visit_docvariablelist(self, node: parser.Node_docVariableListType) -> list[nodes.Node]: output: list[nodes.Node] = [] for n in node: descnode = addnodes.desc() descnode["objtype"] = "varentry" descnode["domain"] = self.get_domain() or "cpp" signode = addnodes.desc_signature() signode += self.render_optional(n.varlistentry) descnode += signode contentnode = addnodes.desc_content() contentnode += self.render_iterable(n.listitem) descnode += contentnode output.append(descnode) return output @node_handler(parser.Node_docVarListEntryType) def visit_docvarlistentry(self, node: parser.Node_docVarListEntryType) -> list[nodes.Node]: return self.render_tagged_iterable(node.term) @node_handler(parser.Node_docAnchorType) def visit_docanchor(self, node: parser.Node_docAnchorType) -> list[nodes.Node]: return list(self.create_doxygen_target(node)) @node_handler(parser.Node_docEntryType) def visit_docentry(self, node: parser.Node_docEntryType) -> list[nodes.Node]: col = nodes.entry() col += self.render_iterable(node.para) if node.thead: col["heading"] = True if node.rowspan: col["morerows"] = int(node.rowspan) - 1 if node.colspan: col["morecols"] = int(node.colspan) - 1 return [col] @node_handler(parser.Node_docRowType) def visit_docrow(self, node: parser.Node_docRowType) -> list[nodes.Node]: row = nodes.row() cols = self.render_iterable(node.entry) elem: nodes.thead | nodes.tbody if all(cast("nodes.Element", col).get("heading", False) for col in cols): elem = nodes.thead() else: elem = nodes.tbody() row += cols elem.append(row) return [elem] @node_handler(parser.Node_docTableType) def visit_doctable(self, node: parser.Node_docTableType) -> list[nodes.Node]: table = nodes.table() table["classes"] += ["colwidths-auto"] tgroup = nodes.tgroup(cols=node.cols) for _ in range(node.cols): colspec = nodes.colspec() colspec.attributes["colwidth"] = "auto" tgroup += colspec table += tgroup rows = self.render_iterable(node.row) # this code depends on visit_docrow(), and expects the same elements used to # "envelop" rows there, namely thead and tbody (eg it will need to be updated # if Doxygen one day adds support for tfoot) tags: defaultdict[str, list] = defaultdict(list) for row in rows: assert isinstance(row, nodes.Element) tags[row.starttag()].append(row.next_node()) def merge_row_types(root, elem, elems): for node in elems: elem += node root += elem for klass in [nodes.thead, nodes.tbody]: obj = klass() if obj.starttag() in tags: merge_row_types(tgroup, obj, tags[obj.starttag()]) return [table] visit_description = node_handler(parser.Node_descriptionType)(render_tagged_iterable) visit_linkedtext = node_handler(parser.Node_linkedTextType)(render_tagged_iterable) def visit_function(self, node: parser.Node_memberdefType) -> list[nodes.Node]: dom = self.get_domain() if not dom or dom in ("c", "cpp", "py", "cs"): names = self.get_qualification() names.append(node.name) name = self.join_nested_name(names) if dom == "py": declaration = name + (node.argsstring or "") elif dom == "cs": declaration = " ".join([ self.create_template_prefix(node), "".join(n.astext() for n in self.render(node.type)), name, node.argsstring or "", ]) else: elements = [self.create_template_prefix(node)] if node.static: elements.append("static") if node.inline: elements.append("inline") if node.kind == parser.DoxMemberKind.friend: elements.append("friend") if node.virt in (parser.DoxVirtualKind.virtual, parser.DoxVirtualKind.pure_virtual): elements.append("virtual") if node.explicit: elements.append("explicit") if node.constexpr: elements.append("constexpr") if node.consteval: elements.append("consteval") typ = strip_legacy_qualifiers("".join(n.astext() for n in self.render(node.type))) elements.extend((typ, name, node.argsstring or "")) declaration = " ".join(elements) return self.handle_declaration(node, node.kind.value, declaration) else: # Get full function signature for the domain directive. param_list = [] for param in node.param: assert self.context is not None param = self.context.mask_factory.mask(param) param_decl = get_param_decl(param) param_list.append(param_decl) templatePrefix = self.create_template_prefix(node) sig_def = get_definition_without_template_args(node) signature = f"{templatePrefix}{sig_def}({', '.join(param_list)})" # Add CV-qualifiers. if node.const: signature += " const" # The doxygen xml output doesn't register 'volatile' as the xml attribute for functions # until version 1.8.8 so we also check argsstring: # https://bugzilla.gnome.org/show_bug.cgi?id=733451 if node.volatile or (node.argsstring and node.argsstring.endswith("volatile")): signature += " volatile" if node.refqual == parser.DoxRefQualifierKind.lvalue: signature += "&" elif node.refqual == parser.DoxRefQualifierKind.rvalue: signature += "&&" # Add `= 0` for pure virtual members. if node.virt == parser.DoxVirtualKind.pure_virtual: signature += "= 0" assert self.context is not None self.context.directive_args[1] = [signature] nodes_ = self.run_domain_directive(node.kind, self.context.directive_args[1]) assert self.app.env is not None target = None if self.app.env.config.breathe_debug_trace_doxygen_ids: target = self.create_doxygen_target(node) if len(target) == 0: print("{}Doxygen target (old): (none)".format(" " * _debug_indent)) else: print( "{}Doxygen target (old): {}".format(" " * _debug_indent, target[0]["ids"]) ) rst_node = nodes_[1] assert isinstance(rst_node, nodes.Element) doc = rst_node.document assert doc is not None finder = NodeFinder(doc) rst_node.walk(finder) assert finder.content is not None # Templates have multiple signature nodes in recent versions of Sphinx. # Insert Doxygen target into the first signature node. if not self.app.env.config.breathe_debug_trace_doxygen_ids: target = self.create_doxygen_target(node) assert target is not None # the type is cast to "Any" to get around missing typing info in # docutils 0.20.1 cast("Any", rst_node.children[0]).insert(0, target) finder.content.extend(self.description(node)) return nodes_ def visit_define(self, node: parser.Node_memberdefType) -> list[nodes.Node]: declaration = node.name if node.param: declaration += "(" for i, parameter in enumerate(node.param): if i: declaration += ", " if parameter.defname: declaration += parameter.defname declaration += ")" # TODO: remove this once Sphinx supports definitions for macros def add_definition(declarator: Declarator) -> None: if node.initializer and self.app.config.breathe_show_define_initializer: declarator.append(nodes.Text(" ")) declarator.extend(self.render(node.initializer)) return self.handle_declaration( node, node.kind.value, declaration, declarator_callback=add_definition ) def visit_enum(self, node: parser.Node_memberdefType) -> list[nodes.Node]: def content(contentnode): contentnode.extend(self.description(node)) values = nodes.emphasis("", nodes.Text("Values:")) title = nodes.paragraph("", "", values) contentnode += title enums = self.render_iterable(node.enumvalue) contentnode.extend(enums) names = self.get_qualification() names.append(node.name) declaration = self.join_nested_name(names) dom = self.get_domain() if (not dom or dom == "cpp") and node.strong: # It looks like Doxygen does not make a difference # between 'enum class' and 'enum struct', # so render them both as 'enum class'. obj_type = "enum-class" underlying_type = "".join(n.astext() for n in self.render(node.type)) if len(underlying_type.strip()) != 0: declaration += " : " declaration += underlying_type else: obj_type = "enum" return self.handle_declaration(node, obj_type, declaration, content_callback=content) @node_handler(parser.Node_enumvalueType) def visit_enumvalue(self, node: parser.Node_enumvalueType) -> list[nodes.Node]: if self.app.config.breathe_show_enumvalue_initializer: declaration = node.name + self.make_initializer(node) else: declaration = node.name def content(contentnode: addnodes.desc_content): contentnode.extend(self.description(node)) return self.handle_declaration(node, "enumvalue", declaration, content_callback=content) def visit_typedef(self, node: parser.Node_memberdefType) -> list[nodes.Node]: type_ = "".join(n.astext() for n in self.render(node.type)) names = self.get_qualification() names.append(node.name) name = self.join_nested_name(names) if node.definition and node.definition.startswith("using "): # TODO: looks like Doxygen does not generate the proper XML # for the template parameter list declaration = self.create_template_prefix(node) declaration += " " + name + " = " + type_ else: # TODO: Both "using" and "typedef" keywords get into this function, # and if no @typedef comment was added, the definition should # contain the full text. If a @typedef was used instead, the # definition has only the typename, which makes it impossible to # distinguish between them so fallback to "typedef" behavior here. declaration = " ".join([type_, name, node.argsstring or ""]) return self.handle_declaration(node, node.kind.value, declaration) def make_initializer(self, node) -> str: initializer = node.initializer signature: list[nodes.Node] = [] if initializer: render_nodes = self.render(initializer) # Do not append separators for paragraphs. if not isinstance(render_nodes[0], nodes.paragraph): separator = " " assert isinstance(render_nodes[0], nodes.Text) if not render_nodes[0].startswith("="): separator += "= " signature.append(nodes.Text(separator)) signature.extend(render_nodes) return "".join(n.astext() for n in signature) def visit_variable(self, node: parser.Node_memberdefType) -> list[nodes.Node]: names = self.get_qualification() names.append(node.name) name = self.join_nested_name(names) dom = self.get_domain() options = {} if dom == "py": declaration = name initializer = self.make_initializer(node).strip().lstrip("=").strip() if len(initializer) != 0: options["value"] = initializer elif dom == "cs": declaration = " ".join([ self.create_template_prefix(node), "".join(n.astext() for n in self.render(node.type)), name, node.argsstring or "", ]) if node.gettable or node.settable: declaration += "{" if node.gettable: declaration += "get;" if node.settable: declaration += "set;" declaration += "}" declaration += self.make_initializer(node) else: elements = [self.create_template_prefix(node)] if node.static: elements.append("static") if node.mutable: elements.append("mutable") if node.constexpr: elements.append("constexpr") if node.consteval: elements.append("consteval") if node.constinit: elements.append("constinit") typename = strip_legacy_qualifiers("".join(n.astext() for n in self.render(node.type))) if dom == "c" and "::" in typename: typename = typename.replace("::", ".") elements.extend((typename, name, node.argsstring or "", self.make_initializer(node))) declaration = " ".join(elements) if not dom or dom in ("c", "cpp", "py", "cs"): return self.handle_declaration(node, node.kind.value, declaration, options=options) else: return self.render_declaration(node, declaration) def visit_friendclass(self, node: parser.Node_memberdefType) -> list[nodes.Node]: dom = self.get_domain() assert not dom or dom == "cpp" desc = addnodes.desc() desc["objtype"] = "friendclass" desc["domain"] = self.get_domain() or "cpp" signode = addnodes.desc_signature() desc += signode typ = "".join(n.astext() for n in self.render(node.type)) # in Doxygen < 1.9 the 'friend' part is there, but afterwards not # https://github.com/breathe-doc/breathe/issues/616 assert typ in ("friend class", "friend struct", "class", "struct") if not typ.startswith("friend "): typ = "friend " + typ signode += addnodes.desc_annotation(typ, typ) signode += nodes.Text(" ") # expr = cpp.CPPExprRole(asCode=False) # expr.text = node.name # TODO: set most of the things that SphinxRole.__call__ sets # signode.extend(expr.run()) signode += nodes.Text(node.name) return [desc] def visit_templateparam( self, node: parser.Node_paramType, *, insertDeclNameByParsing: bool = False ) -> list[nodes.Node]: nodelist: list[nodes.Node] = [] # Parameter type if node.type: type_nodes = self.render(node.type) # Render keywords as annotations for consistency with the cpp domain. if len(type_nodes) > 0 and isinstance(type_nodes[0], str): first_node = type_nodes[0] for keyword in ["typename", "class"]: if first_node.startswith(keyword + " "): type_nodes[0] = nodes.Text(first_node.replace(keyword, "", 1)) type_nodes.insert(0, addnodes.desc_annotation(keyword, keyword)) break nodelist.extend(type_nodes) # Parameter name if node.declname: dom = self.get_domain() if not dom: dom = "cpp" appendDeclName = True if insertDeclNameByParsing: if dom == "cpp": parser = cpp.DefinitionParser( "".join(n.astext() for n in nodelist), location=self.state.state_machine.get_source_and_line(), config=self.app.config, ) try: # we really should use _parse_template_parameter() # but setting a name there is non-trivial, so we use type ast = parser._parse_type(named="single", outer="templateParam") assert ast.name is None nn = cpp.ASTNestedName( names=[ cpp.ASTNestedNameElement(cpp.ASTIdentifier(node.declname), None) ], templates=[False], rooted=False, ) ast.name = nn # the actual nodes don't matter, as it is astext()-ed later nodelist = [nodes.Text(str(ast))] appendDeclName = False except cpp.DefinitionError: # happens with "typename ...Args", so for now, just append pass if appendDeclName: if nodelist: nodelist.append(nodes.Text(" ")) nodelist.append(nodes.emphasis(text=node.declname)) elif self.output_defname and node.defname: # We only want to output the definition name (from the cpp file) if the declaration name # (from header file) isn't present if nodelist: nodelist.append(nodes.Text(" ")) nodelist.append(nodes.emphasis(text=node.defname)) # array information if node.array: nodelist.append(nodes.Text(node.array)) # Default value if node.defval: nodelist.append(nodes.Text(" = ")) nodelist.extend(self.render(node.defval)) return nodelist @node_handler(parser.Node_templateparamlistType) def visit_templateparamlist(self, node: parser.Node_templateparamlistType) -> list[nodes.Node]: nodelist: list[nodes.Node] = [] self.output_defname = False for i, item in enumerate(node.param): if i: nodelist.append(nodes.Text(", ")) nodelist.extend(self.visit_templateparam(item, insertDeclNameByParsing=True)) self.output_defname = True return nodelist @node_handler(parser.Node_docParamListType) def visit_docparamlist(self, node: parser.Node_docParamListType) -> list[nodes.Node]: """Parameter/Exception/TemplateParameter documentation""" fieldListName = { parser.DoxParamListKind.param: "param", parser.DoxParamListKind.exception: "throws", parser.DoxParamListKind.templateparam: "tparam", parser.DoxParamListKind.retval: "retval", } # https://docutils.sourceforge.io/docs/ref/doctree.html#field-list fieldList = nodes.field_list() for item in node: # TODO: does item.parameternamelist really have more than 1 parametername? assert len(item.parameternamelist) <= 1, item.parameternamelist nameNodes: list[nodes.Node] = [] parameterDirectionNodes = [] if len(item.parameternamelist) != 0: paramNameNodes = item.parameternamelist[0].parametername if len(paramNameNodes) != 0: nameNodes = [] for paramName in paramNameNodes: assert len(paramName) == 1 thisName = self.render_tagged(paramName[0]) if len(nameNodes) != 0: if node.kind == parser.DoxParamListKind.exception: msg = "Doxygen \\exception commands with multiple names can not be" msg += " converted to a single :throws: field in Sphinx." msg += " Exception '{}' suppressed from output.".format( "".join(n.astext() for n in thisName) ) self.state.document.reporter.warning(msg) continue nameNodes.append(nodes.Text(", ")) nameNodes.extend(thisName) if paramName.direction is not None: # note, each paramName node seems to have the same direction, # so just use the last one dir = { parser.DoxParamDir.in_: "[in]", parser.DoxParamDir.out: "[out]", parser.DoxParamDir.inout: "[inout]", }[paramName.direction] parameterDirectionNodes = [nodes.strong(dir, dir), nodes.Text(" ")] # it seems that Sphinx expects the name to be a single node, # so let's make it that txt = fieldListName[node.kind] + " " for n in nameNodes: txt += n.astext() name = nodes.field_name("", nodes.Text(txt)) bodyNodes = self.render_optional(item.parameterdescription) # TODO: is it correct that bodyNodes is either empty or a single paragraph? assert len(bodyNodes) <= 1, bodyNodes if len(bodyNodes) == 1: assert isinstance(bodyNodes[0], nodes.paragraph) bodyNodes = [ nodes.paragraph("", "", *(parameterDirectionNodes + bodyNodes[0].children)) ] body = nodes.field_body("", *bodyNodes) field = nodes.field("", name, body) fieldList += field return [fieldList] @node_handler(parser.Node_docDotMscType) def visit_docdot(self, node: parser.Node_docDotMscType) -> list[nodes.Node]: """Translate node from doxygen's dot command to sphinx's graphviz directive.""" graph_node = graphviz() str_value = "" if len(node): val = node[0] assert isinstance(val, str) str_value = val if str_value.rstrip("\n"): graph_node["code"] = str_value else: graph_node["code"] = "" # triggers another warning from sphinx.ext.graphviz self.state.document.reporter.warning( # would be better if this output includes the parent node's # name/reference, but that would always be a element. "no content provided for generating DOT graph." ) graph_node["options"] = {} if node.caption: caption_node = nodes.caption(node.caption, "") caption_node += nodes.Text(node.caption) return [nodes.figure("", graph_node, caption_node)] return [graph_node] @node_handler(parser.Node_docImageFileType) def visit_docdotfile(self, node: parser.Node_docImageFileType) -> list[nodes.Node]: """Translate node from doxygen's dotfile command to sphinx's graphviz directive.""" dotcode = "" dot_file_path = Path(node.name or "") # Doxygen v1.9.3+ uses a relative path to specify the dot file. # Previously, Doxygen used an absolute path. # This relative path is with respect to the XML_OUTPUT path. # Furthermore, Doxygen v1.9.3+ will copy the dot file into the XML_OUTPUT if not dot_file_path.is_absolute(): # Use self.project_info.project_path as the XML_OUTPUT path, and # make it absolute with consideration to the conf.py path project_path = self.project_info.project_path() dot_file_path = Path(self.app.confdir, project_path, dot_file_path).resolve() try: dotcode = dot_file_path.read_text(encoding="utf-8") if not dotcode.rstrip("\n"): raise RuntimeError("%s found but without any content" % dot_file_path) except OSError as exc: # doxygen seems to prevent this from triggering as a non-existent file # generates no XML output for the corresponding `\dotfile` cmd self.state.document.reporter.warning(exc) # better safe than sorry except RuntimeError as exc: self.state.document.reporter.warning(exc) graph_node = graphviz() graph_node["code"] = dotcode graph_node["options"] = {"docname": dot_file_path} caption = "" if len(node) == 0 else parser.tag_name_value(node[0])[1] if caption: assert isinstance(caption, str) caption_node = nodes.caption(caption, "") caption_node += nodes.Text(caption) return [nodes.figure("", graph_node, caption_node)] return [graph_node] @tagged_node_handler(parser.Node_graphType) def visit_docgraph(self, tag: str, node: parser.Node_graphType) -> list[nodes.Node]: """Create a graph (generated by doxygen - not user-defined) from XML using dot syntax.""" assert self.context parent = self.context.node_stack[1].value assert isinstance(parent, parser.Node_compounddefType) direction = "forward" if tag == "incdepgraph": caption = f"Include dependency graph for {parent.compoundname}:" elif tag == "invincdepgraph": direction = "back" caption = ( "This graph shows which files directly or indirectly " + f"include {parent.compoundname}:" ) elif tag == "inheritancegraph": caption = f"Inheritance diagram for {parent.compoundname}:" else: assert tag == "collaborationgraph" caption = f"Collaboration diagram for {parent.compoundname}:" # use graphs' legend from doxygen (v1.9.1) # most colors can be changed via `graphviz_dot_args` in conf.py edge_colors = { # blue (#1414CE) doesn't contrast well in dark mode. # "public-inheritance": "1414CE", # allow user to customize this one "private-inheritance": "8B1A1A", # hardcoded "protected-inheritance": "006400", # hardcoded # the following are demonstrated in the doxygen graphs' legend, but # these don't show in XML properly (bug?); these keys are fiction. "used-internal": "9C35CE", # should also be dashed "template-instantiated-inheritance": "FFA500", # should also be dashed } # assemble the dot syntax we'll pass to the graphviz directive dot = "digraph {\n" dot += ' graph [bgcolor="#00000000"]\n' # transparent color for graph's bg dot += ' node [shape=rectangle style=filled fillcolor="#FFFFFF"' dot += " font=Helvetica padding=2]\n" dot += ' edge [color="#1414CE"]\n' relations = [] for g_node in node.node: dot += ' "%s" [label="%s"' % (g_node.id, g_node.label) dot += ' tooltip="%s"' % g_node.label if g_node.id == "1": # the disabled grey color is used in doxygen to indicate that the URL is # not set (for the compound in focus). Setting this here doesn't allow # further customization. Maybe remove this since URL is not used? # dot += ' fillcolor="#BFBFBF"' # hardcoded # URLs from a doxygen refid won't work in sphinx graphviz; we can't convert # the refid until all docs are built, and pending references are un-noticed # within graphviz directives. Maybe someone wiser will find a way to do it. # # dot += ' URL="%s"' % g_node.get_link().get_refid() dot += "]\n" for child_node in g_node.childnode: edge = f' "{g_node.id}"' edge += f' -> "{child_node.refid}" [' edge += f"dir={direction} " # edge labels don't appear in XML (bug?); use tooltip in meantime edge += 'tooltip="%s"' % child_node.relation.value if child_node.relation.value in edge_colors.keys(): edge += ' color="#%s"' % edge_colors.get(child_node.relation.value) edge += "]\n" relations.append(edge) for relation in relations: dot += relation dot += "}" # use generated dot syntax to create a graphviz node graph_node = graphviz() graph_node["code"] = dot graph_node["align"] = "center" graph_node["options"] = {} # if caption is first node in a figure, then everything that follows is # considered a caption. Use a paragraph followed by a figure to center the # graph. This may have illegible side effects for very large graphs. caption_node = nodes.paragraph("", nodes.Text(caption)) return [caption_node, nodes.figure("", graph_node)] def visit_unknown(self, node) -> list[nodes.Node]: """Visit a node of unknown type.""" return [] @node_handler(parser.Node_CompoundType) def dispatch_compound(self, node: parser.Node_CompoundType) -> list[nodes.Node]: """Dispatch handling of a compound node to a suitable visit method.""" if node.kind in [ parser.CompoundKind.file, parser.CompoundKind.dir, parser.CompoundKind.page, parser.CompoundKind.example, parser.CompoundKind.group, ]: return self.visit_file(node) return self.visit_compound(node) @node_handler(parser.Node_memberdefType) def dispatch_memberdef(self, node: parser.Node_memberdefType) -> list[nodes.Node]: """Dispatch handling of a memberdef node to a suitable visit method.""" if node.kind in ( parser.DoxMemberKind.function, parser.DoxMemberKind.signal, parser.DoxMemberKind.slot, ) or (node.kind == parser.DoxMemberKind.friend and node.argsstring): return self.visit_function(node) if node.kind == parser.DoxMemberKind.enum: return self.visit_enum(node) if node.kind == parser.DoxMemberKind.typedef: return self.visit_typedef(node) if node.kind == parser.DoxMemberKind.variable: return self.visit_variable(node) if node.kind == parser.DoxMemberKind.property: # Note: visit like variable for now return self.visit_variable(node) if node.kind == parser.DoxMemberKind.event: # Note: visit like variable for now return self.visit_variable(node) if node.kind == parser.DoxMemberKind.define: return self.visit_define(node) if node.kind == parser.DoxMemberKind.friend: # note, friend functions should be dispatched further up return self.visit_friendclass(node) return self.render_declaration(node, update_signature=self.update_signature) @tagged_node_handler(str) def visit_string(self, tag: str, node: str) -> list[nodes.Node]: if tag == "verbatim": return self.visit_verbatim(node) return self.render_string(node) @node_handler(str) def render_string(self, node: str) -> list[nodes.Node]: # Skip any nodes that are pure whitespace # Probably need a better way to do this as currently we're only doing # it skip whitespace between higher-level nodes, but this will also # skip any pure whitespace entries in actual content nodes # # We counter that second issue slightly by allowing through single white spaces # stripped = node.strip() if stripped: delimiter = None if "" in stripped: delimiter = "" elif "\n" in stripped: delimiter = "\n" if delimiter: # Render lines as paragraphs because RST doesn't have line breaks. return [ nodes.paragraph("", "", nodes.Text(line.strip())) for line in node.split(delimiter) if line.strip() ] # importantly, don't strip whitespace as visit_docpara uses it to collapse # consecutive nodes.Text and rerender them with this function. return [nodes.Text(node)] if node == " ": return [nodes.Text(node)] return [] def render_tagged( self, item: parser.TaggedValue[str, parser.NodeOrValue] | str ) -> list[nodes.Node]: if isinstance(item, str): return self.render_string(item) return self.render(item.value, None, item.name) def render( self, node: parser.NodeOrValue, context: RenderContext | None = None, tag: str | None = None ) -> list[nodes.Node]: if context is None: assert self.context is not None context = self.context.create_child_context(node, tag) with WithContext(self, context): assert self.context is not None result: list[nodes.Node] = [] if self.filter_(NodeStack(self.context.node_stack)): tmethod = self.tagged_node_handlers.get(type(node)) if tmethod is None: method = self.node_handlers.get(type(node)) if method is None: method = SphinxRenderer.visit_unknown result = method(self, node) elif tag is None: assert isinstance(node, str) result = self.render_string(node) else: result = tmethod(self, tag, node) return result def render_optional(self, node, tag: str | None = None) -> list[nodes.Node]: """Render a node that can be None.""" return self.render(node, None, tag) if node is not None else [] def setup(app: Sphinx) -> None: app.add_config_value("breathe_debug_trace_directives", False, "") app.add_config_value("breathe_debug_trace_doxygen_ids", False, "") app.add_config_value("breathe_debug_trace_qualification", False, "") ================================================ FILE: breathe/renderer/target.py ================================================ from __future__ import annotations from typing import TYPE_CHECKING from docutils import nodes from sphinx.util.nodes import make_id if TYPE_CHECKING: from collections.abc import Mapping, Sequence from typing import Any, Callable from docutils.nodes import Element from sphinx.environment import BuildEnvironment TargetHandler = Callable[[nodes.document, str], Sequence[Element]] class _RealTargetHandler: def __init__(self, env: BuildEnvironment): self.env = env def __call__(self, document: nodes.document, refid: str) -> list[Element]: """Creates a target node, registers it with the document and returns it in a list""" if refid in document.ids: # Sphinx will already warn about a duplicate declaration so we don't # need any warnings here refid = make_id(self.env, document) target = nodes.target(ids=[refid], names=[refid]) document.note_explicit_target(target) return [target] def create_target_handler(options: Mapping[str, Any], env: BuildEnvironment) -> TargetHandler: if "no-link" in options: return lambda document, refid: [] return _RealTargetHandler(env) ================================================ FILE: breathe-apidoc.py ================================================ #!/usr/bin/env python from __future__ import annotations import sys if __name__ == "__main__": from breathe.apidoc import main sys.exit(main()) ================================================ FILE: documentation/.gitignore ================================================ /build /view /comparison ================================================ FILE: documentation/Makefile ================================================ # Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS ?= -v -W -E # -n triggers an additional 100+ warnings (all about undefined references) that -W treats as errors. # these extraneous warnings are ignored because some doc pages have to avoid duplicate IDs SPHINXBUILD = sphinx-build PAPER = BUILDDIR ?= build DEBUG ?= -P # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from https://www.sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = $(DEBUG) -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/test.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/test.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/test" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/test" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." spelling: $(SPHINXBUILD) -b spelling -N $(ALLSPHINXOPTS) $(BUILDDIR)/spelling @echo @echo "Build finished. The spelling files are in $(BUILDDIR)/spelling." ================================================ FILE: documentation/compare ================================================ #!/bin/sh -e first=$1 second=$2 if [ -z "$first" ]; then echo "Usage: compare []" echo "" echo "The second argument defaults to the current branch if not specified" exit 1 fi # Remember the branch we're on currentbranch=`git symbolic-ref --short HEAD` if [ -z "$second" ]; then second=$currentbranch fi firstdir=comparison/first seconddir=comparison/second # Make sure the output directory exists mkdir -p comparison # Remove any previous builds rm -fr $firstdir rm -fr $seconddir export BREATHE_COMPARE=True # Checkout the first target echo git checkout $first git checkout $first # Run doxygen for this state (cd ../examples/specific; make) (cd ../examples/tinyxml; make) (cd ../examples/doxygen; make) # Clean current sphinx build directory make clean # Make sure the BUILDDIR variable can be overridden in the Makefile. Required for older commits sed -i 's/BUILDDIR = build/BUILDDIR ?= build/g' Makefile # Build into our first comparison directory make html BUILDDIR=$firstdir || true # Reset the Makefile to its state for this commit git checkout Makefile # Checkout the second target and repeat echo git checkout $second git checkout $second (cd ../examples/specific; make) (cd ../examples/tinyxml; make) (cd ../examples/doxygen; make) make clean sed -i 's/BUILDDIR = build/BUILDDIR ?= build/g' Makefile make html BUILDDIR=$seconddir || true git checkout Makefile # Return to our current branch echo git checkout $currentbranch git checkout $currentbranch # Launch meld to compare the two html directories echo meld $firstdir/html $seconddir/html meld $firstdir/html $seconddir/html ================================================ FILE: documentation/environment.yaml ================================================ name: RTD channels: - conda-forge - defaults dependencies: # doxygen versions available from conda forge are listed at # https://anaconda.org/conda-forge/doxygen/files # # - doxygen=1.9.4 # to specify a specific version of doxygen # we'll be using the latest available - doxygen - pip - pip: - ".[docs,lint,test]" ================================================ FILE: documentation/make.bat ================================================ @ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=build set SRCDIR=source set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% %SRCDIR% set I18NSPHINXOPTS=%SPHINXOPTS% %SRCDIR% if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. xml to make Docutils-native XML files echo. pseudoxml to make pseudoxml-XML files for display purposes echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) %SPHINXBUILD% 2> nul if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.https://www.sphinx-doc.org/ exit /b 1 ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\libuv.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\libuv.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdf" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdfja" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf-ja cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) if "%1" == "xml" ( %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml if errorlevel 1 exit /b 1 echo. echo.Build finished. The XML files are in %BUILDDIR%/xml. goto end ) if "%1" == "pseudoxml" ( %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml if errorlevel 1 exit /b 1 echo. echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. goto end ) :end ================================================ FILE: documentation/source/_static/breathe.css ================================================ /* -- breathe specific styles ----------------------------------------------- */ /* So enum value descriptions are displayed inline to the item */ .breatheenumvalues li tt + p { display: inline; } /* So parameter descriptions are displayed inline to the item */ .breatheparameterlist li tt + p { display: inline; } :root { --color-brand-primary-light: hsl(205deg, 52%, 39%); --color-brand-primary-dark: hsl(205deg, 65%, 65%); --breathe-str-char-color-dark: hsl(41, 85%, 46%); --breathe-str-char-color-light: hsl(41, 89%, 37%); --breathe-keyword-type-color-dark: hsl(256, 100%, 65%); --breathe-keyword-type-color-light: hsl(276, 85%, 29%); --breathe-number-color-dark: hsl(157, 81%, 50%); --breathe-number-color-light: hsl(157, 93%, 32%); --breathe-name-color-dark: hsl(88, 72%, 56%); --breathe-name-color-light: hsl(88, 100%, 28%); } /* ************************************************** */ /* rules to uniform color scheme with breathe-doc.org */ body { --color-brand-primary: var(--color-brand-primary-light); --color-brand-content: var(--color-brand-primary-light); --breathe-str-char-color: var(--breathe-str-char-color-light); --breathe-keyword-type-color: var(--breathe-keyword-type-color-light); --breathe-number-color: var(--breathe-number-color-light); --breathe-name-color: var(--breathe-name-color-light); } /* Enable dark-mode, if requested. */ body[data-theme="dark"] { --color-brand-primary: var(--color-brand-primary-dark); --color-brand-content: var(--color-brand-primary-dark); --breathe-str-char-color: var(--breathe-str-char-color-dark); --breathe-keyword-type-color: var(--breathe-keyword-type-color-dark); --breathe-number-color: var(--breathe-number-color-dark); --breathe-name-color: var(--breathe-name-color-dark); } /* Enable dark mode, unless explicitly told to avoid. */ @media (prefers-color-scheme: dark) { body:not([data-theme="light"]) { --color-brand-primary: var(--color-brand-primary-dark); --color-brand-content: var(--color-brand-primary-dark); --breathe-str-char-color: var(--breathe-str-char-color-dark); --breathe-keyword-type-color: var(--breathe-keyword-type-color-dark); --breathe-number-color: var(--breathe-number-color-dark); --breathe-name-color: var(--breathe-name-color-dark); } } .mobile-header { /* background-color: var(--color-header-background); */ background-color: #003c66; } .mobile-header .toc-overlay-icon .icon { /* color: var(--color-foreground-secondary); */ color: white; } .mobile-header .header-center a { /* color: var(--color-header-text); */ color: white; } .mobile-header .theme-toggle svg { /* color: var(--color-foreground-primary); */ color: white; } /* C++ specific styling */ .highlight { /* desaturate that ugly yellow color used by most other theme's code snippets */ background-color: var(--color-api-background); /* for light theme only */ } .sig.c .k, .sig.c .kt, .sig.cpp .k, .sig.cpp .kt { color: var(--breathe-keyword-type-color); } .sig.c .m, .sig.cpp .m { color: var(--breathe-number-color); } .sig.c .s, .sig.c .sc, .sig.cpp .s, .sig.cpp .sc { color: var(--breathe-str-char-color); } .sig > .n { color: var(--breathe-name-color); } /* bugfix for multi-lined signatures (see https://github.com/pradyunsg/furo/discussions/427 ) */ .sig { padding-left: 0.5em; text-indent: revert; } ================================================ FILE: documentation/source/autofile.rst ================================================ .. _autodoxygenfile-example: autodoxygenfile Directive Example ================================= For more details and directive documentation please see :ref:`file-example`, which is very similar to this directive. Working Example --------------- This should work: .. code-block:: rst .. autodoxygenfile:: auto_class.h :project: auto With the following config value: .. code-block:: python breathe_projects_source = { "auto" : ( "../examples/specific", ["auto_class.h"] ) } It produces this output: .. autodoxygenfile:: auto_class.h :project: auto ================================================ FILE: documentation/source/autoindex.rst ================================================ .. _autodoxygenindex-example: autodoxygenindex Directive Example ================================== Working Example --------------- This should work: .. code-block:: rst .. autodoxygenindex:: :project: auto With the following config value: .. code-block:: python breathe_projects_source = { "auto" : ( "../examples/specific", ["auto_function.h", "auto_class.h"] ) } It produces this output: .. cpp:namespace:: @ex_autoindex .. autodoxygenindex:: :project: auto ================================================ FILE: documentation/source/class.rst ================================================ .. _class-example: doxygenclass Directive ====================== This directive generates the appropriate output for a single class. It takes the standard ``project``, ``path``, ``outline`` and ``no-link`` options and additionally the ``members``, ``protected-members``, ``private-members``, ``undoc-members``, ``membergroups`` and ``members-only`` options. ``members`` Designed to behavior in a similar manner to the ``members`` option for the ``autoclass`` directive that comes with the Sphinx ``autodoc`` extension. If you do not specify this option you will not get any information about the class members, just the general class documentation. If you provide it without arguments, then Breathe adds all the public members and their documentation. If you specify it with **comma separated** arguments, then Breathe will treat the arguments as names of members and provide documentation for only those members that have been named. ``protected-members`` If specified, the protected members of the class will be displayed. ``private-members`` If specified, the private members of the class will be displayed. ``undoc-members`` If specified, the undocumented members of the class will be displayed. ``membergroups`` If specified, only the groups in a space-delimited list following this directive will be displayed. ``members-only`` This will allow to show only the members, not the class information. Child classes and structs are also not shown. If you would like to always specify some combination of ``members``, ``protected-members``, ``private-members`` and ``undoc-members`` then you can use the :ref:`breathe_default_members ` configuration variable to set it in the ``conf.py``. The output includes references to any base classes and derived classes of the specified class. Basic Example ------------- .. cpp:namespace:: @ex_class_basic This displays the class documentation without any members: .. code-block:: rst .. doxygenclass:: Nutshell :project: nutshell It produces this output: .. doxygenclass:: Nutshell :project: nutshell Template Specialisation Example ------------------------------- .. cpp:namespace:: @ex_class_template_spec You can reference class template specialisations by include the specialisation in the name: .. code-block:: rst .. doxygenclass:: TemplateClass< T * > :project: template_specialisation Produces this output: .. doxygenclass:: TemplateClass< T * > :project: template_specialisation Where as without the specialisation, the directive references the generic declaration: .. code-block:: rst .. doxygenclass:: SecondTemplateClass :project: template_specialisation Produces this output: .. doxygenclass:: SecondTemplateClass :project: template_specialisation Note the spacing inside the ``<>``, it's important: there must be a space after the ``<`` and before the ``>``. Members Example --------------- .. cpp:namespace:: @ex_class_members This directive call will display the class documentation with all the public members: .. code-block:: rst .. doxygenclass:: Nutshell :project: nutshell :members: It produces this output: .. doxygenclass:: Nutshell :project: nutshell :members: :no-link: Specific Members Example ------------------------ .. cpp:namespace:: @ex_class_members_specific This displays the class documentation with only the members listed in the ``:members:`` option: .. code-block:: rst .. doxygenclass:: Nutshell :project: nutshell :members: Tool, crack, isCracked It produces this output: .. doxygenclass:: Nutshell :project: nutshell :members: Tool, crack, isCracked :no-link: Protected Members ----------------- .. cpp:namespace:: @ex_class_members_protected This displays only the protected members of the class. Normally this is combined with the ``:members:`` option to show the public members as well. .. code-block:: rst .. doxygenclass:: GroupedClassTest :project: group :protected-members: It produces this output: .. doxygenclass:: GroupedClassTest :project: group :protected-members: Private Members --------------- .. cpp:namespace:: @ex_class_members_private This displays only the private members of the class. Normally this is combined with the ``:members:`` option to show the public members as well. .. code-block:: rst .. doxygenclass:: Nutshell :project: nutshell :private-members: It produces this output: .. doxygenclass:: Nutshell :project: nutshell :private-members: :no-link: Undocumented Members -------------------- .. cpp:namespace:: @ex_class_members_undocumented This displays the undocumented members of the class which are suppressed by default. Undocumented public members are only shown if the ``:members:`` option is also used. The same goes for the undocumented private members and the ``private-members`` option. .. code-block:: rst .. doxygenclass:: ClassTest :project: class :members: :private-members: :undoc-members: It produces this output: .. doxygenclass:: ClassTest :project: classtest :members: :private-members: :undoc-members: :no-link: .. note:: Undocumented classes are still not shown in the output due to an implementation issue. Please post an issue on github if you would like this resolved. .. _class-example-membergroups: Membergroups ------------ .. cpp:namespace:: @ex_class_membergroups This will show only members in the specified member group(s). .. code-block:: rst .. doxygenclass:: GroupedMembers :project: membergroups :members: :membergroups: myGroup It produces this output: .. doxygenclass:: GroupedMembers :project: membergroups :members: :membergroups: myGroup :no-link: Without ``:membergroups: myGroup`` it would produce: .. cpp:namespace:: @ex_class_membergroups_all .. doxygenclass:: GroupedMembers :project: membergroups :members: .. _class-example-membersonly: Members-only ------------ .. cpp:namespace:: @ex_class_members_only This will only show the members of a class, and not the class name, child classes or structs, or any information about the class. .. code-block:: rst .. doxygenclass:: ClassTest :project: class :members: :members-only: It produces this output: .. doxygenclass:: ClassTest :project: classtest :members: :members-only: :no-link: Without ``:members-only:`` it would produce: .. cpp:namespace:: @ex_class_members_all .. doxygenclass:: ClassTest :project: classtest :members: :no-link: .. note:: The members will be shown at the indentation normally reserver for class definitions. To prevent this, you may want to indent the block by indenting the ``.. doxygenclass`` directive. .. note:: In the ``readthedocs`` theme, the members will show up in the color scheme of the class definitions. If you would like them rendered as the other members, indent like above, create a ``_static/css/custom.css`` file containing .. code-block:: css /* render as functions not classes when indented (for :members-only:) */ html.writer-html4 .rst-content blockquote dl:not(.field-list)>dt, html.writer-html5 .rst-content blockquote dl[class]:not(.option-list):not(.field-list):not(.footnote):not(.glossary):not(.simple)>dt { margin-bottom: 6px; border: none; border-left: 3px solid #ccc; background: #f0f0f0; color: #555; } and add the following to your ``conf.py`` .. code-block:: python html_static_path = ['_static'] html_css_files = ['css/custom.css'] Outline Example --------------- .. cpp:namespace:: @ex_class_outline This displays only the names of the class members and not their documentation. The ``:members:`` and ``:private-members:`` options determine which members are displayed. .. code-block:: rst .. doxygenclass:: Nutshell :project: nutshell :members: :outline: It produces this output: .. doxygenclass:: Nutshell :project: nutshell :members: :outline: :no-link: Qt Signals & Slots Example -------------------------- .. cpp:namespace:: @ex_class_qt Doxygen is aware of Qt Signals and Slots and so Breathe can pick them up and display them in the output. They are displayed in appropriate ``Signals``, ``Public Slots``, ``Protected Slots`` and ``Private Slots`` sections. .. code-block:: rst .. doxygenclass:: QtSignalSlotExample :project: qtsignalsandslots :members: Produces the following output: .. doxygenclass:: QtSignalSlotExample :project: qtsignalsandslots :members: Failing Example --------------- .. cpp:namespace:: @ex_class_failing This intentionally fails: .. code-block:: rst .. doxygenclass:: made_up_class :project: class :members: It produces the following warning message: .. warning:: doxygenclass: Cannot find class “made_up_class” in doxygen xml output for project “class” from directory: ../../examples/doxygen/class/xml/ ================================================ FILE: documentation/source/code/groups.h ================================================ // Example from Doxygen documentation /** A class. More details about the Test class */ class UserDefinedGroupTest { public: //@{ /** Same documentation for both members. Details */ void func1InGroup1(); void func2InGroup1(); //@} /** Function without group. Details. */ void ungroupedFunction(); void func1InCustomGroup(); protected: void func2InCustomGroup(); }; void UserDefinedGroupTest::func1InGroup1() {} void UserDefinedGroupTest::func2InGroup1() {} /** @name Custom Group * Description of custom group */ //@{ /** Function 2 in custom group. Details. */ void UserDefinedGroupTest::func2InCustomGroup() {} /** Function 1 in custom group. Details. */ void UserDefinedGroupTest::func1InCustomGroup() {} //@} ================================================ FILE: documentation/source/code/namespaces.h ================================================ /*! A namespace for nothing. */ namespace test_namespace { /*! A very important class. */ class Sample1 { public: Sample1() {} }; /*! Even more important class */ class Sample2 { Sample2() {} }; /*! A function in the namespace */ void foo() {} } ================================================ FILE: documentation/source/code/nested_list_1.h ================================================ /** * \file nested_list_1.h * Example of nested lists in documentation. */ /** * A list of events: * - mouse events * -# mouse move event * -# mouse click event\n * More info about the click event. * -# mouse double click event * - keyboard events * 1. key down event * 2. key up event * * More text here. */ class NestedLists_1 { }; ================================================ FILE: documentation/source/code/nested_list_2.h ================================================ /** * \file nested_list_2.h * Example of nested list in documentation. */ /** * Text before the list * - list item 1 * - sub item 1 * - sub sub item 1 * - sub sub item 2 * . * The dot above ends the sub sub item list. * * More text for the first sub item * . * The dot above ends the first sub item. * * More text for the first list item * - sub item 2 * - sub item 3 * - list item 2 * . * More text in the same paragraph. * * More text in a new paragraph. */ class NestedLists_2 { }; ================================================ FILE: documentation/source/code/nested_list_3.h ================================================ /** * \file nested_list_3.h * Example of nested lists in documentation. */ /*! * A list of events: *
    *
  • mouse events *
      *
    1. mouse move event *
    2. mouse click event
      * More info about the click event. *
    3. mouse double click event *
    *
  • keyboard events *
      *
    1. key down event *
    2. key up event *
    *
* More text here. */ class NestedLists_3 { }; ================================================ FILE: documentation/source/code/nested_list_4.h ================================================ /** * \file nested_list_4.h * Example of nested lists in documentation. */ /** * A list of events: * 1. mouse events * -# mouse move event * 1. swipe event * 2. circle event * 3. wave event * -# mouse click event\n * More info about the click event. * -# mouse double click event * 2. keyboard events * -# key down event * -# key up event * 3. touch events * -# pinch event * -# swipe event * More text here. */ class NestedLists_4 { }; ================================================ FILE: documentation/source/code/nested_list_5.h ================================================ /** * \file nested_list_4.h * Example of nested lists in documentation. */ /** * A deeply nested list of events: * 1. mouse events * -# mouse move event * 1. swipe event * -# swipe left * -# swipe right * 2. circle event * 3. wave event * -# mouse click event\n * More info about the click event. * -# mouse double click event * 2. keyboard events * -# key down event * -# key up event * 3. touch events * -# pinch event * -# swipe event * More text here. */ class NestedLists_5 { }; ================================================ FILE: documentation/source/code/nutshell.h ================================================ /** \file nutshell.h An overly extended example of how to use breathe */ /*! With a little bit of a elaboration, should you feel it necessary. */ class Nutshell { public: //! Our tool set /*! The various tools we can opt to use to crack this particular nut */ enum Tool { kHammer = 0, //!< What? It does the job kNutCrackers, //!< Boring kNinjaThrowingStars //!< Stealthy }; //! Nutshell constructor Nutshell(); //! Nutshell destructor ~Nutshell(); /*! Crack that shell with specified tool \param tool - the tool with which to crack the nut */ void crack( Tool tool ); /*! \return Whether or not the nut is cracked */ bool isCracked(); private: //! Our cracked state bool m_isCracked; }; ================================================ FILE: documentation/source/codeblocks.rst ================================================ Code Blocks =========== Breathe supports rendering code blocks with syntax highlighting provided by the `Pygments `_ library. By default, Breathe will assume that code blocks match the language of the source file, but you can also specify the language of the code blocks using `Doxygen's code command `_ or `MarkDown's fenced code blocks `_. .. note:: Any hyperlinked text found within the code blocks rendered with Doxygen's HTML output will not be hyperlinked in any Sphinx output due to the use of the Pygments library. As a benefit, a code-block's syntax highlighting can be any syntax supported by Pygments (which is much more than only the languages supported by Doxygen's parsers). The Doxygen syntax for code blocks supports specifying the language as follows: .. code-block:: \code{.py} class Python: pass \endcode @code{.cpp} class Cpp {}; @endcode This technique can also be utilized from MarkDown syntax/files .. code-block:: markdown ```py class Python: pass ``` ```cpp class Cpp {}; ``` Breathe will pass the language specified to Pygments to get accurate highlighting. If no language is explicitly provided (either from ``\code`` command or via Doxygen's XML output about the language documented), then Pygments will try to guess what syntax the code block is using (based on the code block's contents). Examples -------- The following should render with standard C/C++ highlighting. Notice, the syntax is automatically highlighted as C++ because the documented function exists in a C++ source file. ---- .. code-block:: cpp /** A function with an unannotated code block with C/C++ code. * * @code * char *buffer = new char[42]; * int charsAdded = sprintf(buffer, "Tabs are normally %d spaces\n", 8); * @endcode */ void with_standard_code_block(); ---- .. doxygenfunction:: with_standard_code_block :path: ../../examples/specific/code_blocks/xml :no-link: ---- The following should render with no detected highlighting. Notice, there is no syntax highlighting because Pygments does not recognize the code block's contained syntax as a C++ snippet. ---- .. code-block:: cpp /** A function with an unannotated code block with non-C/C++ code. * * @code * set(user_list A B C) * foreach(element ${user_list}) * message(STATUS "Element is ${element}") * endforeach() * @endcode * * Another code-block that explicitly remains not highlighted. * @code{.unparsed} * Show this as is. * @endcode */ void with_unannotated_cmake_code_block(); ---- .. doxygenfunction:: with_unannotated_cmake_code_block :path: ../../examples/specific/code_blocks/xml :no-link: ---- The following should render with specified CMake highlighting. Here, the syntax highlighting is explicitly recognized as a CMake script snippet which overrides the inherent C++ context. ---- .. code-block:: cpp /** A function with an annotated cmake code block. * * @code{.cmake} * set(user_list A B C) * foreach(element ${user_list}) * message(STATUS "Element is ${element}") * endforeach() * @endcode */ void with_annotated_cmake_code_block(); ---- .. doxygenfunction:: with_annotated_cmake_code_block :path: ../../examples/specific/code_blocks/xml :no-link: .. warning:: Pygments will raise a warning in the Sphinx build logs if the specified syntax does conform the specified syntax's convention(s). ================================================ FILE: documentation/source/codeguide.rst ================================================ .. _codeguide: How It Works ============ There are three main sections to Breathe: parser, finders and renderers. Briefly: **parser** Responsible for reading the doxygen xml output and creating objects representing the data. Found in ``breathe.parser``. **finders** Responsible for finding reference objects within the output from the parser. Found in ``breathe.finder``. **renderers** Responsible for producing reStructuredText nodes to represent the objects that the finders have found. The renderers generally descend through the object hierarchies rendering the objects, their children, their children's children and so on. Found in ``breathe.renderer``. The following flow chart shows how the different components of Breathe transform data. The shaded region indicates which components are part of Breathe. .. image:: ../assets/BreatheFlowChart.svg :width: 500 :alt: A flow chart showing that the initial input format is code. Doxgyen converts code to XML. The Breathe parser converts XML to a hierarchy of python objects. The Breathe Filter identifies which of these objects need to be rendered. The Breathe Renderer converts these objects into reStructuredText (RST) nodes. Finally, the RST node objects are passed to Sphinx to be turned into actual HTML or LaTeX documents. :class: only-light :align: center .. image:: ../assets/BreatheFlowChart_DarkMode.svg :width: 500 :alt: A flow chart showing that the initial input format is code. Doxgyen converts code to XML. The Breathe parser converts XML to a hierarchy of python objects. The Breathe Filter identifies which of these objects need to be rendered. The Breathe Renderer converts these objects into reStructuredText (RST) nodes. Finally, the RST node objects are passed to Sphinx to be turned into actual HTML or LaTeX documents. :class: only-dark :align: center Parser ------ The parser's job is to parse the doxygen xml output and create a hierarchy of Python objects to represent the xml data. Doxygen XML Output ~~~~~~~~~~~~~~~~~~ The xml output from doxygen comes in multiple files. There is always an ``index.xml`` file which is a central reference point and contains a list of all the other files that have been generated by doxygen and an indication of what they contain. For example, in ``examples/doxygen/func/xml`` directory, the ``index.xml`` file contains: .. code-block:: xml Test member func.h This suggests there is additional information about a class called **Test** which has a function called **member**. Additionally there is some more information about a file called **func.h**. Now, the ``refid`` attribute on the ``compound`` xml nodes gives an indication of where the additional information can be found. So for the **Test** class, we should look in ``class_test.xml``, which we get by simply appending ``.xml`` to the ``refid`` value, and for the **func.h** file we should look in ``func_8h.xml``. So the ``index.xml`` file is unique in its role and has its own structure which is defined in the ``index.xsd`` file which you will also find in the same directory. All the other files, the ones referenced by the ``index.xml`` file, follow another structure. This is described in ``compound.xsd`` file so we call these other files **compound** files. These are generally longer than the ``index.xml`` file and contain all the specific information you might expect from doxygen, including any documentation you added to your code as doxygen markup. Have a look at ``examples/doxygen/func/xml/class_test.xml`` for a fairly short example. Doing the Parsing ~~~~~~~~~~~~~~~~~ To get things up and running quickly, I have used the `generateDS `_ project to help create classes to parse the doxygen xml output. The script automatically creates the ``compound.py``, ``compoundsuper.py``, ``index.py`` and ``indexsuper.py`` files that you can see inside ``breathe/parser/doxygen``. So what is the difference between ``index.py`` and ``indexsuper.py``, and ``compound.py`` and ``compoundsuper.py``? These files allow us to separate the bulk of the automatically generated code from the code changes we might want to make. There are a large number of classes in the ``...super.py`` files and each one has a basic derived class in the corresponding non-super files. It is designed so that all the hard work done by the generated code is done in the ``...super.py`` files and if we need to make changes we can do them in the derived classes in the non-super files and if we ever need to regenerate the code, we only regenerate the ``...super.py`` files and so we don't lose our changes in the process. The end result is that for the parsing, we have written relatively little code, but have a large amount automatically generated for us. This has only been done once and it seems relatively unlikely that we'll do it again. The entry points to the parsing code is the ``parse`` functions at the bottom of the ``breathe.parser.doxygen.compound`` and ``breathe.parser.doxygen.index``. I have never really examined the details of the parsing but you can see that there is a class for each node type you are likely to find in the xml files. I say "node type" instead of just "node" because different nodes can share the same type and there is one class per type. For example, there are **detaileddescription** nodes and **briefdescription** nodes which are both of type **descriptionType**. If we look in ``breathe.parser.doxygen.compoundsuper`` we see a **descriptionType** class and in ``breathe.parser.doxygen.compound`` we see a **descriptionTypeSub** class which is derived from **descriptionType**. Our Changes ~~~~~~~~~~~ You'll notice there are some classes in the non-super files that have some additional code in them. This tends to be adjusting the ``buildChildren`` member function in the derived class to extend or override the one in the automatically generated base class. We have to do this sometimes as it seems the original code we generated with ``generateDS`` fails to construct the children of some classes. The ``generateDS`` scripts uses the descriptions in the ``.xsd`` files to determine what classes to generate and what nodes can be the children of other nodes. It is possible that the doxygen ``.xsd`` files contain levels of abstraction that the ``generateDS`` project did not cope with at the time I used it. It is possible that newer versions would handle it better but for the moment I'm content updating the derived classes to handle the cases I see missing. Finders ------- The finder classes have a relatively small but important job of finding objects in the hierarchy generated by the parsers. For example, when a user specifies a particular class for the :ref:`doxygenclass directive `, we use the finder classes to go and find the object corresponding to that class. In fact, if you look closely, it is the finders that use the parser entry points to parse the xml and then find the objects. The finders also use ``Filter`` objects to actually figure out if they have found what they are looking for. The finder is given a hierarchy of filter objects which are designed to match at different levels of the XML hierarchy. Filters can also represent logical conditions such as 'and' and 'or'. More Details, Please ~~~~~~~~~~~~~~~~~~~~ So initially, we create a finder to look at the root of the hierarchy: the **doxygenTypeSub** node. That finder, handily called **DoxygenTypeSubItemFinder** (you'll notice a lot of that) looks through all the child compound nodes of the **doxygenTypeSub** node and tries a compound-level match against each of them and if something matches it creates a **CompoundTypeSubItemFinder** to look further. In turn, that checks each of its member child nodes with a member-level match and if it finds one it creates a **MemberTypeSubItemFinder** (see the pattern?) and that does another check. The interesting part is, if that is successful, the **CompoundTypeSubItemFinder** finds the corresponding xml file that has more information in it (remember ``refid + .xml``?) and parses that and creates another finder to start looking in there. This time it is a **DoxygenTypeSubItemFinder** from the ``breathe.finder.doxygen.compound`` module. And the search goes on until we find an object to return for rendering. If the **CompoundTypeSubItemFinder** fails to find any deeper levels to match against then it returns itself as it must be the target we're interested in. As stated, the job of the finder is to find a single node for the renderers to starting rendering to reStructuredText. That is all the finder does. Renderers --------- Finally, the bit that really does something we care about. Rendering is the art of turning whatever object we've found in the hierarchy into reStructuredText nodes. This almost invariably means most of its children as well. Much like with the finder classes, we start off creating a renderer for a particular parser object and then it looks at its children and uses the renderer factory to create appropriate renderers for those objects and tells them to render and they look at their object's children and create appropriate renderers for those and so on and so forth. The node we start at is determined by the finder and ultimately by the user. The whole process is kicked off by the ``Builder`` class, though it doesn't really do much. The aim of the renderers is to return a list of reStructuredText nodes which is passed back to Sphinx to render into whatever you're final output format is. There are two complicated bits here. All the different renderers and all the different reStructuredText nodes. Different Renderers ~~~~~~~~~~~~~~~~~~~ Just like with the parsers, there is one renderer per node type. In fact there is one renderer class per parser class and they are named almost the same and are designed to match up. The renderers look at the data on the instance of the corresponding parser class that they have been given and grab the interesting bits and return reStructuredText nodes. For reference on what there is to render, you can look at the parser class definitions or at the raw xml to see what attributes there are to render. Sometimes if something isn't appearing in the final output, it is because the renderer isn't returning an reStructuredText representation of it so the rendering code needs to be updated, and sometimes it is because the parser classes are not picking it up properly so both the parser and the renderer code needs to be updated. Given a little bit of time, you get used to chasing through the xml nodes, the parser classes and the corresponding renderers to figure out where all the information is ending up. reStructuredText Nodes ~~~~~~~~~~~~~~~~~~~~~~ We use the reStructuredText API as provided by the fabulous docutils project and extended by Sphinx itself. For the most part, they are fairly straight forward and they are certainly well named. Unfortunately there are a lot of nodes and only certain ways of combining them. It is also not always clear what arguments their constructs take. Whilst I'm sure it would be possible to figure it out with time and the appropriate source code, the use of them is not something I've found very well documented and my code largely operates on a basis of trial and error. One day I'm sure I'll be enlightened, until then expect fairly naive code. Testing ------- Tests for Breathe can be found in the ``tests`` directory. They can be run by running ``make test`` in your terminal (assuming that you have pytest installed). The bulk of Breathe's test suite is in ``tests/test_renderer.py``, and this is where any renderer-related tests should be added. This documentation will focus on how to write more renderer tests, as this is the most common region of the code to add new features to and perhaps the hardest to test. Creating Python Doxygen Nodes ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ As indicated in the diagram at the top of this page, the renderer is expecting to be run after the parser has created a hierarchy of python objects. Thus, there is a lot of set-up that would usually happen before the renderer is invoked. For ease of testing, it is often expedient to skip straight to the step where you have a hierarchy of Python objects representing some hypothetical XML that doxygen could have produced. ``test_renderer.py`` contains a number of classes designed to assist with this process. For just about any node that could show up in the XML produced by doxygen, there is a class that quickly instantiates it in Python. For example, if you want to test the rendering of a member definition, you can use the ``WrappedMemebrDef`` class. Figuring out how nodes fit together can be challenging; until you're comfortable with the type of XML produced by doxygen, the easiest process is likely: #. Write C++ code containing the behavior you would like to test. #. Run Doxygen on it, which will produce an XML file (likely inside a directory called xml within your doxygen output directory) #. Re-build the relevant part of the xml file in Python using the ``Wrapped*`` classes. For example, lets say you have a struct representing a cat. Your C++ might look something like this (inspired by Sy Brand's `blog post `_): .. code-block:: cpp /** A fluffy feline */ struct cat { /** Make this cat look super cute */ void make_cute(); }; Running Doxygen on this might give you XML something like this: .. code-block:: xml cat test_cpp.hpp void void cat::make_cute () make_cute cat::make_cute Make this cat look super cute A fluffy feline catmake_cute There's a lot here. For now, let's just say we're testing something related to member function definitions, and we only need to test that part of the hierarchy. We can load the ``memberdef`` part of this XML into a ``WrappedMemberDef`` object as follows: .. code-block:: python member_def = WrappedMemberDef( kind="function", # From "kind" in open memberdef tag definition="void cat::make_cute", # From tag type="void", # From tag name="make_cute", # From tag argstring="()", # From tag virt="non-virtual", # From "virt" in open memberdef tag ) As you can see, all of the arguments to the constructor are pulled directly out of the XML, either from options on the original memberdef or tags nested under it. There are a lot more optional arguments that can be provided to specify additional details of the memberdef. More advanced hierarchies can be represented by nesting nodes inside each other. For example, if our function took arguments, it would have ```` tags nested within it. We could represent these as a list of ``WrappedParam`` objects passed into the ``param`` keyword argument. To test that the node renders correctly, you can use the ``render`` function provided in ``test_renderer.py``: .. code-block:: python # Render the node and grab its description signature = find_node(render(app, member_def), "desc_signature") # You can now examine the contents of signature.astext() and assert that # they are as expected Mocks ~~~~~ If you want to do more elaborate tests, it is useful to be aware of the various Mock objects provided in ``test_renderer.py``. Because the renderer is expecting to be executing in the context of a full Sphinx run, there are a lot of objects that it is expecting to have access to. For example, rendering some nodes requires making reference to a context object. The ``MockContext`` class can serve as a stand-in. ================================================ FILE: documentation/source/concept.rst ================================================ .. _concept-example: doxygenconcept Directive Example ================================ .. warning:: C++20 Concepts support was added in Doxygen v1.9.2. Please be sure to use Doxygen v1.9.2 or newer with :ref:`doxygenconcept`. Working Example --------------- This should work: .. code-block:: rst .. doxygenconcept:: Hashable :project: cpp_concept It produces this output: .. doxygenconcept:: Hashable :project: cpp_concept Failing Example --------------- This intentionally fails: .. code-block:: rst .. doxygenconcept:: MadeUpConcept :project: cpp_concept It produces the following warning message: .. warning:: doxygenconcept: Cannot find concept "MadeUpConcept" in doxygen xml output ================================================ FILE: documentation/source/conf.py ================================================ from __future__ import annotations import os import re import subprocess import sys from pathlib import Path from typing import TYPE_CHECKING import sphinx if TYPE_CHECKING: from sphinx.application import Sphinx from sphinx.util.typing import ExtensionMetadata PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent sys.path.append(str(PROJECT_ROOT)) # General configuration # --------------------- extensions = [ "breathe", "sphinx.ext.graphviz", "sphinx.ext.imgconverter", "sphinx_copybutton", "sphinxcontrib.spelling", ] master_doc = "index" project = "Breathe" copyright = "2009-2025, Michael Jones" if os.getenv("BREATHE_COMPARE") == "True": # If we're doing a comparison then set the version & release to 'compare' # so that they are always the same otherwise they can come up as changes # when we really don't care if they are different. version = release = "compare" else: # Get a description of the current position. git_tag = subprocess.run(["git", "describe", "--tags"], capture_output=True, encoding="utf-8") if re.match(r"^v\d+\.\d+\.\d+$", git_tag.stdout): # Check if it matches a pure tag number vX.Y.Z, # rather than vX.Y.Z-91-g8676988, which is how non-tagged commits # are described (relative to the last tag). version = release = git_tag.stdout else: version = release = "latest" # Options for breathe extension # ----------------------------- breathe_projects = { "class": "../../examples/doxygen/class/xml/", "classtest": "../../examples/specific/class/xml/", "struct": "../../examples/specific/struct/xml/", "interface": "../../examples/specific/interface/xml/", "decl_impl": "../../examples/specific/decl_impl/xml/", "structcmd": "../../examples/doxygen/structcmd/xml/", "tinyxml": "../../examples/tinyxml/tinyxml/xml/", "restypedef": "../../examples/doxygen/restypedef/xml/", "nutshell": "../../examples/specific/nutshell/xml/", "rst": "../../examples/specific/rst/xml/", "c_file": "../../examples/specific/c_file/xml/", "namespace": "../../examples/specific/namespacefile/xml/", "userdefined": "../../examples/specific/userdefined/xml/", "template_function": "../../examples/specific/template_function/xml/", "template_class": "../../examples/specific/template_class/xml/", "template_class_non_type": "../../examples/specific/template_class_non_type/xml/", "template_specialisation": "../../examples/specific/template_specialisation/xml/", "latexmath": "../../examples/specific/latexmath/xml/", "functionOverload": "../../examples/specific/functionOverload/xml/", "programlisting": "../../examples/specific/programlisting/xml/", "image": "../../examples/specific/image/xml/", "lists": "../../examples/specific/lists/xml/", "tables": "../../examples/specific/tables/xml/", "group": "../../examples/specific/group/xml/", "union": "../../examples/specific/union/xml/", "qtsignalsandslots": "../../examples/specific/qtsignalsandslots/xml/", "array": "../../examples/specific/array/xml/", "c_struct": "../../examples/specific/c_struct/xml/", "c_enum": "../../examples/specific/c_enum/xml/", "c_typedef": "../../examples/specific/c_typedef/xml/", "c_macro": "../../examples/specific/c_macro/xml/", "c_union": "../../examples/specific/c_union/xml/", "define": "../../examples/specific/define/xml/", "multifile": "../../examples/specific/multifilexml/xml/", "cpp_anon": "../../examples/specific/cpp_anon/xml/", "cpp_concept": "../../examples/specific/cpp_concept/xml/", "cpp_enum": "../../examples/specific/cpp_enum/xml/", "cpp_union": "../../examples/specific/cpp_union/xml/", "cpp_function": "../../examples/specific/cpp_function/xml/", "cpp_function_lookup": "../../examples/specific/cpp_function_lookup/xml/", "cpp_friendclass": "../../examples/specific/cpp_friendclass/xml/", "cpp_inherited_members": "../../examples/specific/cpp_inherited_members/xml/", "cpp_ns_template_specialization": "../../examples/specific/cpp_ns_template_specialization/xml/", "cpp_trailing_return_type": "../../examples/specific/cpp_trailing_return_type/xml/", "cpp_constexpr_hax": "../../examples/specific/cpp_constexpr_hax/xml/", "xrefsect": "../../examples/specific/xrefsect/xml/", "membergroups": "../../examples/specific/membergroups/xml/", "simplesect": "../../examples/specific/simplesect/xml/", "dot_graphs": "../../examples/specific/dot_graphs/xml/", } breathe_projects_source = { "auto": ("../../examples/specific", ["auto_function.h", "auto_class.h"]), } breathe_default_project = "tinyxml" breathe_domain_by_extension = { "h": "cpp", "py": "py", } breathe_domain_by_file_pattern = { "class.h": "cpp", "alias.h": "c", "array.h": "c", "c_*.h": "c", } breathe_use_project_refids = True # Options for HTML output # ----------------------- html_theme = "furo" html_logo = "_static/logo.svg" html_favicon = "_static/favicon.ico" html_static_path = ["_static"] html_css_files = ["breathe.css"] # Options for the spelling extension # ---------------------------------- spelling_word_list_filename = "spelling_wordlist.txt" spelling_lang = "en_US" # Extension interface # ------------------- try: doxygen_test = subprocess.check_output(["doxygen", "--version"], encoding="utf-8") except subprocess.CalledProcessError as err: msg = f"doxygen --version reported an error: {err.stderr}" raise RuntimeError(msg) from err else: print(f"Using Doxygen v{doxygen_test}") del doxygen_test if os.getenv("READTHEDOCS") == "True": if version == "latest": tags.add("documentation_build_readthedocs_latest") # NoQA: F821 else: tags.add("documentation_build_readthedocs") # NoQA: F821 else: tags.add("documentation_build_development") # NoQA: F821 def run_doxygen(folder: Path) -> None: """Run the doxygen make command in the designated folder""" try: subprocess.run(["make", "DOXYGEN=doxygen"], check=True, cwd=folder) except subprocess.CalledProcessError as e: print(f"doxygen terminated by signal {-e.returncode}", file=sys.stderr) except OSError as e: print(f"doxygen execution failed: {e}", file=sys.stderr) def generate_doxygen_xml(app: Sphinx) -> None: """Run the doxygen make commands if we're on the ReadTheDocs server""" if os.getenv("READTHEDOCS") == "True": # Attempt to build the doxygen files on the RTD server. # Explicitly override the path/name used for executing doxygen # to simply be 'doxygen' to stop the makefiles looking for the executable. # This is because the `which doxygen` effort seemed to fail # when tested on the RTD server. run_doxygen(PROJECT_ROOT / "examples" / "doxygen") run_doxygen(PROJECT_ROOT / "examples" / "specific") run_doxygen(PROJECT_ROOT / "examples" / "tinyxml") def setup(app) -> ExtensionMetadata: if sphinx.version_info[:2] < (7, 4): # Approach borrowed from the Sphinx docs app.add_object_type( "confval", "confval", objname="configuration value", indextemplate="pair: %s; configuration value", ) # Add hook for building doxygen xml when needed app.connect("builder-inited", generate_doxygen_xml) return { "version": version, "parallel_read_safe": True, "parallel_write_safe": True, } ================================================ FILE: documentation/source/contributing.rst ================================================ Contributing to Breathe ======================= There are four main ways you might consider contributing to Breathe. Give It A Go ------------ **...and let me know!** Firstly, the more people using it the better, but more than that, hearing about the project being put to use is a great motivator for the developer, namely me. Report Bugs & Suggest Features ------------------------------ Embarrassingly I don't get to use Breathe that much in my general work, so it doesn't really get pushed beyond the test code we have here in the repository. If you use it and find issues with it, minor or major, please let me know and if possible provide some detail so I can reproduce it. With the help of those who have posted issues on the github issue tracker we've managed to track down and improve some of the less obvious (and some more obvious!) parts of Breathe that weren't working properly. Improve the Documentation ------------------------- I've made an effort to document Breathe so it is usable, but I have a twisted perspective on the whole thing as I made it. I've already had some help with the documentation, which was greatly appreciated, but if you managed to get it working and find that the documentation could have been clearer in parts, let me know or write up a paragraph or two that would have helped you when you were trying it. Fork It! And Improve the Code ----------------------------- If you find a bug, quite like Python and have some time, then grab a copy of the code and have a go at fixing it. Nothing motivates me quite like other people caring enough to put a bit of time into working on it. The contributions we've had this way have been great and much appreciated. If you want to help out, take a look at the :ref:`code guide` to see how it is all structured and works. ================================================ FILE: documentation/source/credits.rst ================================================ Credits ======= Thank you to: - `nijel `_ - `sebastianschaetz `_ - `mbolivar `_ - `queezythegreat `_ - `abingham `_ - `davidm `_ - `hobu `_ - `magro11 `_ - `scopatz `_ - `vitaut `_ - `vonj `_ - `jmnas `_ - `donkopotamus `_ - `jo3w4rd `_ - `Anthony Truchet `_ - `Daniel Matz `_ - `Andrew Hundt `_ - `sebastinas `_ - `robo9k `_ - `sieben `_ - `rweickelt `_ - `sam-roth `_ - `bnewbold `_ - `serge-sans-paille `_ For their contributions; reporting bugs, suggesting features, improving the code and working on the documentation. And thanks to: - Dimitri van Heesch for `Doxygen `_. - Georg Brandl for `Sphinx `_. - David Goodger for `Docutils `_ and reStructuredText. And thank you to whoever made the ``haiku`` theme for Sphinx. ================================================ FILE: documentation/source/customcss.rst ================================================ .. highlight:: css Custom CSS ========== In order to help with the output styling in HTML, Breathe attaches some custom classes to parts of the document. There are three such classes: **breatheparameterlist** Used to keep the description of a parameter displayed inline with the parameter name. The Breathe docs use: .. code-block:: css .breatheparameterlist li tt + p { display: inline; } **breatheenumvalues** Used to keep the description of an enum displayed inline with the enum name. The Breathe docs use: .. code-block:: css .breatheenumvalues li tt + p { display: inline; } ================================================ FILE: documentation/source/define.rst ================================================ .. _define-example: doxygendefine Directive Example =============================== Working Example --------------- This should work: .. code-block:: rst .. doxygendefine:: WRITE_TREE_MISSING_OK :project: c_file It produces this output: .. doxygendefine:: WRITE_TREE_MISSING_OK :project: c_file Failing Example --------------- This intentionally fails: .. code-block:: rst .. doxygendefine:: MADEUPDEFINE :project: define It produces the following warning message: .. warning:: doxygendefine: Cannot find define "MADEUPDEFINE" in doxygen xml output for project "define" in directory: ../../examples/specific/define/xml ================================================ FILE: documentation/source/differences.rst ================================================ Deviations from Doxygen & Autodoc ================================= As Breathe attempts to bridge the gap between Sphinx and Doxygen it is confined by both what Doxygen outputs in its XML and what Sphinx will accept through the Docutils document model. This leads to a few differences between Breathe output and the Doxygen HTML output and the Sphinx Autodoc output. These are incomplete lists but we're keen to expand them as issues are brought to our attention. Doxygen ------- - Doxygen allows both HTML and Markdown syntax for headings in comments. These are rendered as standard HTML headings in the output (h1, h2, h3, etc.) RestructuredText only allows headings at the start of document sections and you cannot put arbitrary sections into the output to gain the appearance of headings so any headings found in the doxygen comments are rendered as emphasized text in the Breathe HTML output. Sphinx Autodoc -------------- - No differences highlighted yet, though they certainly exist. ================================================ FILE: documentation/source/directives.rst ================================================ Directives & Config Variables ============================= .. toctree:: :hidden: autoindex function struct class namespace concept enum enumvalue typedef union define variable file group autofile page Directives ---------- The available directives are shown below. In each case the ``project``, ``path``, ``no-link`` and ``outline`` options have the following meaning: ``project`` Specifies which project, as defined in the ``breathe_projects`` config value, should be used for this directive. This overrides the default project if one has been specified. This is not used by the `autodoxygenindex`_ directive. Use ``source`` instead to specify the entry in the :confval:`breathe_projects_source` config value to use. ``path`` Directly specifies the path to the folder with the doxygen output. This overrides the project and default project if they have been specified. This is not used by the `autodoxygenindex`_ directive. Use ``source-path`` instead to specify the root path to the sources files which are to be processed. ``no-link`` Instructs Breathe to not attempt to generate any document targets for the content generated by this particular directive. This allows you to have your main reference listings somewhere with targets, but then to be able to sneak in repeat directives into other parts of the documentation to illustrate particular points without Sphinx getting confused what should be linked to by other references. ``outline`` Results in Breathe only outputting the raw code definitions without any additional description information. If neither project nor path are provided on the directive then breathe will expect the :ref:`breathe_default_project ` config value to be set. .. _doxygenclass: doxygenclass ~~~~~~~~~~~~ This directive generates the appropriate output for a single class. It takes the standard ``project``, ``path``, ``outline`` and ``no-link`` options and additionally the ``members``, ``protected-members``, ``private-members``, ``undoc-members``, ``membergroups`` and ``members-only`` options .. code-block:: rst .. doxygenclass:: :project: ... :path: ... :members: [...] :protected-members: :private-members: :undoc-members: :membergroups: ... :members-only: :outline: :no-link: :allow-dot-graphs: Checkout the :ref:`doxygenclass documentation ` for more details and to see it in action. .. _doxygendefine: doxygendefine ~~~~~~~~~~~~~ This directive generates the appropriate output for a single preprocessor define. It behaves the same as the `doxygenstruct`_ directive. .. code-block:: rst .. doxygendefine:: :project: ... :path: ... :outline: :no-link: Checkout the :ref:`example ` to see it in action. .. _doxygenconcept: doxygenconcept ~~~~~~~~~~~~~~ This directive generates the appropriate output for a single concept. It behaves the same as the `doxygenstruct`_ directive. .. code-block:: rst .. doxygenconcept:: :project: ... :path: ... :outline: :no-link: Checkout the :ref:`example ` to see it in action. .. _doxygenenum: doxygenenum ~~~~~~~~~~~ This directive generates the appropriate output for a single enum. It behaves the same as the `doxygenstruct`_ directive. .. code-block:: rst .. doxygenenum:: :project: ... :path: ... :outline: :no-link: Checkout the :ref:`example ` to see it in action. .. _doxygenenumvalue: doxygenenumvalue ~~~~~~~~~~~~~~~~ This directive generates the appropriate output for a single enum value. .. code-block:: rst .. doxygenenumvalue:: :project: ... :path: ... :outline: :no-link: Checkout the :ref:`example ` to see it in action. .. _doxygenfile: doxygenfile ~~~~~~~~~~~ This directive generates the appropriate output for the contents of a source file. .. code-block:: rst .. doxygenfile:: :project: ... :path: ... :outline: :no-link: :sections: ... :allow-dot-graphs: Checkout the :ref:`example ` to see it in action. .. _autodoxygenfile: autodoxygenfile ~~~~~~~~~~~~~~~ This directive is this ``auto`` version of the `doxygenfile`_ directive above. It handles the doxygen xml generation for you like the other auto directives. .. code-block:: rst .. autodoxygenfile:: :project: ... :outline: :no-link: :sections: ... :allow-dot-graphs: Checkout the :ref:`example ` to see it in action. .. _doxygenfunction: doxygenfunction ~~~~~~~~~~~~~~~ This directive generates the appropriate output for a single function. The function name is required to be unique in the project. .. code-block:: rst .. doxygenfunction:: :project: ... :path: ... :outline: :no-link: Checkout the :ref:`example ` to see it in action. .. _doxygengroup: doxygengroup ~~~~~~~~~~~~ This directive generates the appropriate output for the contents of a doxygen group. A doxygen group can be declared with specific doxygen markup in the source comments as covered in the `doxygen grouping documentation`_. It takes the standard ``project``, ``path``, ``outline`` and ``no-link`` options and additionally the ``content-only``, ``desc-only``, ``members``, ``protected-members``, ``private-members`` and ``undoc-members`` options. .. code-block:: rst .. doxygengroup:: :project: ... :path: ... :content-only: :desc-only: :outline: :members: :protected-members: :private-members: :undoc-members: :no-link: :inner: Checkout the :ref:`doxygengroup documentation ` for more details and to see it in action. .. _doxygen grouping documentation: https://www.doxygen.nl/manual/grouping.html .. _doxygenindex: doxygenindex ~~~~~~~~~~~~ This directive processes and produces output for everything described by the Doxygen xml output. It reads the ``index.xml`` file and process everything referenced by it. .. code-block:: rst .. doxygenindex:: :project: ... :path: ... :outline: :no-link: :allow-dot-graphs: .. _autodoxygenindex: autodoxygenindex ~~~~~~~~~~~~~~~~ This directive performs a similar role to the `doxygenindex`_ directive except that it handles the doxygen xml generation for you. It uses the :confval:`breathe_projects_source` configuration dictionary to judge which code source files should have doxygen xml generated for them. The ``project`` directive option associates the directive with a particular project in the :confval:`breathe_projects_source` dictionary. All the files references by the entry in the :confval:`breathe_projects_source` will be included in the output. In addition, any options specified in :confval:`breathe_doxygen_config_options` will be added to the generated Doxygen config file and any custom aliases specified in :confval:`breathe_doxygen_aliases` will be added to the `doxygen aliases `_. Thank you to `Scopatz `_ for the idea and initial implementation. .. code-block:: rst .. autodoxygenindex:: :project: ... :outline: :no-link: :allow-dot-graphs: Checkout the :ref:`example ` to see it in action. .. _doxygennamespace: doxygennamespace ~~~~~~~~~~~~~~~~ This directive generates the appropriate output for the contents of a namespace. It takes the standard ``project``, ``path``, ``outline`` and ``no-link`` options and additionally the ``content-only``, ``desc-only``, ``members``, ``protected-members``, ``private-members`` and ``undoc-members`` options. To reference a nested namespace, the full namespaced path must be provided, e.g. ``foo::bar`` for the ``bar`` namespace inside the ``foo`` namespace. .. code-block:: rst .. doxygennamespace:: :project: ... :path: ... :content-only: :desc-only: :outline: :members: :protected-members: :private-members: :undoc-members: :no-link: Checkout the :ref:`doxygennamespace documentation ` for more details and to see it in action. .. _doxygenstruct: doxygenstruct ~~~~~~~~~~~~~ This directive generates the appropriate output for a single struct. The struct name is required to be unique in the project. It takes the standard ``project``, ``path``, ``outline`` and ``no-link`` options and additionally the ``members``, ``protected-members``, ``private-members``, ``membergroups``, ``members-only`` and ``undoc-members`` options. .. code-block:: rst .. doxygenstruct:: :project: ... :path: ... :members: :protected-members: :private-members: :undoc-members: :membergroups: ... :members-only: :outline: :no-link: :allow-dot-graphs: Checkout the :ref:`example ` to see it in action. .. _doxygeninterface: doxygeninterface ~~~~~~~~~~~~~~~~ This directive generates the appropriate output for a single interface (specially-used class). It behaves the same as the `doxygenclass`_ directive. .. code-block:: rst .. doxygeninterface:: :project: ... :path: ... :members: :protected-members: :private-members: :undoc-members: :membergroups: ... :members-only: :outline: :no-link: .. _doxygentypedef: doxygentypedef ~~~~~~~~~~~~~~ This directive generates the appropriate output for a single typedef. It behaves the same as the doxygenstruct directive. .. code-block:: rst .. doxygentypedef:: :project: ... :path: ... :outline: :no-link: Checkout the :ref:`example ` to see it in action. .. _doxygenunion: doxygenunion ~~~~~~~~~~~~ This directive generates the appropriate output for a single union. It behaves the same as the doxygenstruct directive. .. code-block:: rst .. doxygenunion:: :project: ... :path: ... :outline: :no-link: Checkout the :ref:`example ` to see it in action. .. _doxygenvariable: doxygenvariable ~~~~~~~~~~~~~~~ This directive generates the appropriate output for a single variable. It behaves the same as the doxygenstruct directive. .. code-block:: rst .. doxygenvariable:: :project: ... :path: ... :outline: :no-link: Checkout the :ref:`example ` to see it in action. .. _doxygenpage: doxygenpage ~~~~~~~~~~~ This directive generates the appropriate output for the contents of a doxygen page. A doxygen page is created for each "key" of every \\xrefitem command used for markup in the source comments. For more information check the `doxygen xrefitem documentation`_. It takes the standard ``project`` and ``path`` options and additionally the ``content-only`` option. .. code-block:: rst .. doxygenpage:: :project: ... :path: ... :content-only: Checkout the :ref:`doxygenpage documentation ` for more details and to see it in action. .. _doxygen xrefitem documentation: https://www.doxygen.nl/manual/commands.html#cmdxrefitem Config Values ------------- .. confval:: breathe_projects This should be a dictionary in which the keys are project names and the values are paths to the folder containing the doxygen output for that project. .. _default_project: .. confval:: breathe_default_project This should match one of the keys in the :confval:`breathe_projects` dictionary and indicates which project should be used when the project is not specified on the directive. .. confval:: breathe_domain_by_extension Allows you to specify domains for particular files according to their extension. For example: .. code-block:: python breathe_domain_by_extension = { "h" : "cpp", } You can also use this to enable support for Doxygen XML generated from PHP code: .. code-block:: python breathe_domain_by_extension = { "php" : "php", } .. confval:: breathe_domain_by_file_pattern Allows you to specify domains for particular files by wildcard syntax. This is checked after :confval:`breathe_domain_by_extension` and so will override it when necessary. For example: .. code-block:: python breathe_domain_by_file_pattern = { "\*/alias.h" : "c", } If you wanted all ``.h`` header files to be treated as being in the **cpp** domain you might use the :confval:`breathe_domain_by_extension` example above. But if you had one ``.h`` file that should be treated as being in the **c** domain then you can override as above. .. confval:: breathe_projects_source A dictionary in which the keys are project names and the values are a tuple of the directory and a list of file names of the source code for those projects that you would like to be automatically processed with doxygen. If you have some files in: .. code-block:: text /some/long/path/to/myproject/file.c /some/long/path/to/myproject/subfolder/otherfile.c Then you can set: .. code-block:: python breathe_projects_source = { "myprojectsource" : ( "/some/long/path/to/myproject", ["file.c", "subfolder/otherfile.c"] ) } Then your `autodoxygenfile`_ usage can look like this: .. code-block:: rst .. autodoxygenfile:: file.c :project: myprojectsource The directory entry in the tuple can be an empty string if the entries in the list are full paths. .. confval:: breathe_build_directory In order to process the `autodoxygenindex`_ Breathe has to run ``doxygen`` to create the xml files for processing. This config value specifies the root directory that these files should be created in. By default, this is set to the parent directory of the ``doctrees`` output folder which is the normal build directory. You can change it with this setting if you have a custom set up. Breathe will take the final value and append ``breathe/doxygen/`` to the path to minimize conflicts. .. _breathe-default-members: .. confval:: breathe_default_members Provides the directive flags that should be applied to all directives which take ``:members:``, ``:private-members:`` and ``:undoc-members:`` options. By default, this is set to an empty list, which means no members are displayed. If you'd like to always display the public and public, undocumented members then you could set it like this: .. code-block:: python breathe_default_members = ('members', 'undoc-members') .. _breathe-implementation-filename-extensions: .. confval:: breathe_implementation_filename_extensions Provides a list of the filename extensions which are considered to be implementation files. These files are ignored when the `doxygenfunction`_ directive looks for un-namespaced function names. This is to avoid the issue where the same function name appears in the doxygen XML output for a header file and implementation file because the declaration is in one and the definition is in the other. Doxygen appends the documentation from the definition to the XML for the declaration so we don't miss any documentation information from the implementation files. The default value for this variable is: .. code-block:: python breathe_implementation_filename_extensions = ['.c', '.cc', '.cpp'] .. _breathe-doxygen-config-options: .. confval:: breathe_doxygen_config_options A dictionary in which the keys and values are the names and values of config options to be placed in the Doxygen config file generated by `autodoxygenindex`_. For instance, this: .. code-block:: python breathe_doxygen_config_options = {'EXCLUDE_SYMBOLS': 'abc123'} would place ``EXCLUDE_SYMBOLS=abc123`` in the config file. The default value is the empty dictionary. .. confval:: breathe_doxygen_aliases A dictionary in which the keys and values are the names and values of aliases to be placed in the Doxygen config file generated by `autodoxygenindex`_. For instance, this: .. code-block:: python breathe_doxygen_aliases = { 'rstref{1}': r'\verbatim embed:rst:inline :ref:`\1` \endverbatim' } would place the line:: ALIASES += rstref{1}="\verbatim embed:rst:inline :ref:`\1` \endverbatim" in the config file. The default value is an empty dictionary. Note the use of a raw string to ensure that backslashes are interpreted as literal characters. (This example alias enables linking to rst targets inline in doxygen comments using ``\rstref{}``) .. confval:: breathe_show_define_initializer A boolean flag which can be set to ``True`` to display the initializer of a define in the output. For example a define ``MAX_LENGTH 100`` would be shown with the value 100 if this is set to ``True``, and without if it is set to ``False``. .. confval:: breathe_show_enumvalue_initializer A boolean flag which can be set to ``True`` to display the initializer of an enum value in the output. For example an enum value ``TWO = 2`` would be shown with the value 2 if this is set to ``True``, and without if it is set to ``False``. .. confval:: breathe_show_include A boolean flag which can be set to ``False`` to hide the header in which each entity (struct, function, macro, etc.) is defined. For example, when set to ``True`` (the default) a ``struct Foo`` is rendered similarly to:: struct Foo #include Description of Foo. but when set to ``False`` it is instead rendered as:: struct Foo Description of Foo. .. confval:: breathe_use_project_refids True or False setting to control if the refids generated by breathe for doxygen elements contain the project name or not. Legacy (breathe 4.6.0 and earlier) behavior was that all refids are prefixed with the project name. This causes trouble when trying to link documentation between multiple projects since the projects can't see each other's documentation elements. The new default behavior is to not prefix the refids with the project name. This:: breathe_use_project_refids = True will restore the legacy behavior if it is needed. .. confval:: breathe_order_parameters_first True or False setting to control if the parameters section from doxygen generated documentation should be placed immediately after the brief and detailed description or at the end, after the returns, remarks and warnings section. Default value and also the legacy behavior is False. .. confval:: breathe_separate_member_pages True or False setting to control if the input XML generated by Doxygen had the Doxygen SEPARATE_MEMBER_PAGES option set to YES or NO. The Doxygen option defaults to NO which generates XML that allows Breathe to resolve all references. When set to YES the refid/id of elements get an extra element which Breathe tries to get rid of when this setting is True. ================================================ FILE: documentation/source/domains.rst ================================================ Domains ======= Breathe has some limited support for Sphinx domains. It tries to output targets that the Sphinx domain references expect. This should allow you to use Sphinx domain roles like ``:c:func:`foo``` to link to output from Breathe. Class Example ------------- .. cpp:namespace:: @ex_domains_class Given the following Breathe directives: .. code-block:: rst .. doxygenclass:: TestNamespaceClasses::NamespacedClassTest :path: ../../examples/specific/class/xml Which create formatted output like: .. doxygenclass:: TestNamespaceClasses::NamespacedClassTest :path: ../../examples/specific/class/xml We can refer to **NamespacedClassTest** using: .. code-block:: rst :cpp:class:`TestNamespaceClasses::NamespacedClassTest` which renders as :cpp:class:`TestNamespaceClasses::NamespacedClassTest`, or using: .. code-block:: rst :cpp:class:`another reference ` which renders as: :cpp:class:`another reference `. Inner Class Example ------------------- .. cpp:namespace:: @ex_domains_inner_class Given the following Breathe directive:: .. doxygenclass:: OuterClass :path: ../../examples/specific/class/xml :members: Which create formatted output like: .. doxygenclass:: OuterClass :path: ../../examples/specific/class/xml :members: We can refer to **OuterClass::InnerClass** using:: :cpp:class:`OuterClass::InnerClass` which renders as :cpp:class:`OuterClass::InnerClass`. Function Examples ----------------- .. cpp:namespace:: @ex_domains_function Given the following Breathe directives: .. code-block:: rst .. doxygenfunction:: TestNamespaceClasses::NamespacedClassTest::function :path: ../../examples/specific/class/xml .. doxygenfunction:: frob_foos :path: ../../examples/specific/alias/xml Which create formatted output like: .. doxygenfunction:: TestNamespaceClasses::NamespacedClassTest::function :path: ../../examples/specific/class/xml .. doxygenfunction:: frob_foos :path: ../../examples/specific/alias/xml We can refer to **namespaceFunc** using: .. code-block:: rst :cpp:func:`TestNamespaceFunction::namespaceFunc()` which renders as :cpp:func:`TestNamespaceFunction::namespaceFunc()`, or using: .. code-block:: rst :cpp:func:`another reference ` which renders as: :cpp:func:`another reference `. Note the use of the **cpp** domain. And we can refer to **frob_foos** using: .. code-block:: rst :c:func:`frob_foos()` which renders as: :c:func:`frob_foos()`, or using: .. code-block:: rst :c:func:`another reference ` which renders as: :c:func:`another reference `. Note the use of the **c** domain. Typedef Examples ---------------- .. cpp:namespace:: @ex_domains_typedef Given the following Breathe directives: .. code-block:: rst .. doxygentypedef:: TestTypedef :path: ../../examples/specific/typedef/xml .. doxygennamespace:: TypeDefNamespace :path: ../../examples/specific/typedef/xml .. doxygenclass:: TestClass :path: ../../examples/specific/typedef/xml :members: which create formatted output like: .. doxygentypedef:: TestTypedef :path: ../../examples/specific/typedef/xml .. doxygennamespace:: TypeDefNamespace :path: ../../examples/specific/typedef/xml .. doxygenclass:: TestClass :path: ../../examples/specific/typedef/xml :members: We can refer to **TestTypedef** using: .. code-block:: rst :cpp:type:`TestTypedef` which renders as :cpp:type:`TestTypedef`, to **TypeDefNamespace::AnotherTypedef** using: .. code-block:: rst :cpp:type:`TypeDefNamespace::AnotherTypedef` which renders as :cpp:type:`TypeDefNamespace::AnotherTypedef` and to **TestClass::MemberTypedef** using: .. code-block:: rst :cpp:type:`TestClass::MemberTypedef` which renders as :cpp:type:`TestClass::MemberTypedef`. Enum Value Examples ------------------- .. cpp:namespace:: @ex_domains_enum Given the following Breathe directives: .. code-block:: rst .. doxygenenumvalue:: VALUE :path: ../../examples/specific/enum/xml .. doxygenenumvalue:: TestEnumNamespace::FIRST :path: ../../examples/specific/enum/xml Which create formatted output like: .. doxygenenumvalue:: VALUE :path: ../../examples/specific/enum/xml .. doxygenenumvalue:: TestEnumNamespace::FIRST :path: ../../examples/specific/enum/xml We can refer to **VALUE** using: .. code-block:: rst :cpp:enumerator:`VALUE` which renders as :cpp:enumerator:`VALUE` and to **TestEnumNamespace::FIRST** using: .. code-block:: rst :cpp:enumerator:`TestEnumNamespace::FIRST` which renders as :cpp:enumerator:`TestEnumNamespace::FIRST`. ================================================ FILE: documentation/source/dot_graphs.rst ================================================ Using Dot Graphs ================ .. _graphviz: https://www.sphinx-doc.org/en/master/usage/extensions/graphviz.html#module-sphinx.ext.graphviz .. _dot: https://www.doxygen.nl/manual/commands.html#cmddot .. _dotfile: https://www.doxygen.nl/manual/commands.html#cmddotfile Sphinx graphviz prerequisites ----------------------------- To use Sphinx's graphviz_ directive at all, the project documentation's ``conf.py`` file must have ``sphinx.ext.graphviz`` added to the list of ``extensions``. .. code-block:: python extensions = ["breathe", "sphinx.ext.graphviz"] .. seealso:: To obtain the dot executable from the Graphviz library, see `the library's downloads section `_. .. note:: Typically, the dot executable's path should be added to your system's ``PATH`` environment variable. This is required for Sphinx, although the configuration option, `graphviz_dot `_, can compensate for abnormal dot executable installations. ``\dot`` and ``\dotfile`` commands ---------------------------------- By default, breathe will translate any dot_ and dotfile_ commands into Sphinx graphviz_ directives. However, there are some caveats: 1. The only graphviz_ option supported is the ``caption`` option. Graph captions are optionally specified using the dot_ or dotfile_ command syntax. All other graphviz_ directive options fallback to their default behavior. - any size hints from Doxygen's dot_ or dotfile_ commands are ignored. 2. Using Doxygen's ``@ref`` command within any dot syntax is functionally ignored and treated as literal text. Generated graphs from Doxygen ----------------------------- If Doxygen is configured to use the dot executable to generate certain graphs, then some of these graphs can be translated into Sphinx graphviz directives. Because this feature depends on having the dot executable installed to generate graphs in Sphinx, the option ``allow-dot-graphs`` must be specified for the following directives: - :ref:`doxygenclass` - :ref:`doxygenstruct` - :ref:`doxygenfile` - :ref:`doxygenindex` - :ref:`autodoxygenfile` - :ref:`autodoxygenindex` .. attention:: Only the following graphs generated by Doxygen can be found in its XML output: .. csv-table:: :header: graph relevance, configuration option files included within, `INCLUDE_GRAPH `_ files included by, `INCLUDED_BY_GRAPH `_ inheritance, `CLASS_GRAPH `_ collaboration, `COLLABORATION_GRAPH `_ Unfortunately, the ``call`` and ``caller`` graphs are not provided by Doxygen's XML output. Examples of graphs generated by Doxygen are shown in this documentation's `Diagrams section of the doxygen test suite `_ Example Graphs -------------- Graphs can be placed anywhere. For this example they are placed in a doxygen page. .. code-block:: rst .. doxygenpage:: dotgraphs :project: dot_graphs This will render as: .. doxygenpage:: dotgraphs :project: dot_graphs ================================================ FILE: documentation/source/doxygen.rst ================================================ Doxygen Test Suite ================== Class ----- .. cpp:namespace:: @ex_doxygen_class .. doxygenindex:: :project: class Interface --------- .. cpp:namespace:: @ex_doxygen_interface .. doxygenindex:: :path: ../../examples/doxygen/interface/xml Define ------ .. the macros are in the C domain, and anyway in global scope .. cpp:namespace:: 0 .. doxygenindex:: :path: ../../examples/doxygen/define/xml Enum ---- .. cpp:namespace:: @ex_doxygen_enum .. doxygenindex:: :path: ../../examples/doxygen/enum/xml File ----- .. cpp:namespace:: @ex_doxygen_file .. doxygenindex:: :path: ../../examples/doxygen/file/xml Func ---- .. cpp:namespace:: @ex_doxygen_func .. doxygenindex:: :path: ../../examples/doxygen/func/xml Page ---- .. this is not related to Sphinx, but let's reset the namespace anyway .. cpp:namespace:: 0 .. doxygenindex:: :path: ../../examples/doxygen/page/xml Relates ------- .. cpp:namespace:: @ex_doxygen_relates .. doxygenindex:: :path: ../../examples/doxygen/relates/xml Author ------ .. cpp:namespace:: @ex_doxygen_author .. doxygenindex:: :path: ../../examples/doxygen/author/xml Par --- .. cpp:namespace:: @ex_doxygen_par .. doxygenindex:: :path: ../../examples/doxygen/par/xml Parblock -------- .. cpp:namespace:: @ex_doxygen_parblock .. doxygenindex:: :path: ../../examples/doxygen/parblock/xml Overload -------- .. cpp:namespace:: @ex_doxygen_overload .. doxygenindex:: :path: ../../examples/doxygen/overload/xml Example ------- .. cpp:namespace:: @ex_doxygen_example .. doxygenindex:: :path: ../../examples/doxygen/example/xml Include ------- .. cpp:namespace:: @ex_doxygen_include .. doxygenindex:: :path: ../../examples/doxygen/include/xml QtStyle ------- .. cpp:namespace:: @ex_doxygen_qtstyle .. doxygenindex:: :path: ../../examples/doxygen/qtstyle/xml JdStyle ------- .. cpp:namespace:: @ex_doxygen_jdstyle .. doxygenindex:: :path: ../../examples/doxygen/jdstyle/xml StructCmd --------- .. cpp:namespace:: @ex_doxygen_structcmd .. doxygenindex:: :path: ../../examples/doxygen/structcmd/xml Autolink -------- .. cpp:namespace:: @ex_doxygen_autolink .. doxygenindex:: :path: ../../examples/doxygen/autolink/xml ResTypeDef ---------- .. cpp:namespace:: @ex_doxygen_restypedef .. doxygenindex:: :path: ../../examples/doxygen/restypedef/xml AfterDoc -------- .. cpp:namespace:: @ex_doxygen_afterdoc .. doxygenindex:: :path: ../../examples/doxygen/afterdoc/xml Template -------- .. cpp:namespace:: @ex_doxygen_template .. doxygenindex:: :path: ../../examples/doxygen/template/xml Tag --- .. cpp:namespace:: @ex_doxygen_tag .. doxygenindex:: :path: ../../examples/doxygen/tag/xml Group ----- .. cpp:namespace:: @ex_doxygen_group .. doxygenindex:: :path: ../../examples/doxygen/group/xml Diagrams -------- .. cpp:namespace:: @ex_doxygen_diagrams .. doxygenindex:: :path: ../../examples/doxygen/diagrams/xml :allow-dot-graphs: Memgrp ------ .. cpp:namespace:: @ex_doxygen_memgrp .. doxygenindex:: :path: ../../examples/doxygen/memgrp/xml Docstring --------- .. doxygenindex:: :path: ../../examples/doxygen/docstring/xml PyExample --------- .. doxygenindex:: :path: ../../examples/doxygen/pyexample/xml Manual ------ .. cpp:namespace:: @ex_doxygen_manual .. doxygenindex:: :path: ../../examples/doxygen/manual/xml ================================================ FILE: documentation/source/embeddedrst.rst ================================================ Embedded ReStructuredText ========================= .. cpp:namespace:: @ex_embedded_rst .. doxygenindex:: :path: ../../examples/specific/rst/xml ================================================ FILE: documentation/source/enum.rst ================================================ .. _enum-example: doxygenenum Directive Example =============================== Working Example --------------- .. cpp:namespace:: @ex_enum_example This should work: .. code-block:: rst .. doxygenenum:: NodeType :project: tinyxml It produces this output: .. doxygenenum:: NodeType :project: tinyxml Example with Namespace ---------------------- .. cpp:namespace:: @ex_enum_namespace This should work: .. code-block:: rst .. doxygenenum:: foo::ns::Letters :project: namespace It produces this output: .. doxygenenum:: foo::ns::Letters :project: namespace Failing Example --------------- .. cpp:namespace:: @ex_enum_failing This intentionally fails: .. code-block:: rst .. doxygenenum:: made_up_enum :project: restypedef It produces the following warning message: .. warning:: doxygenenum: Cannot find enum "made_up_enum" in doxygen xml output ================================================ FILE: documentation/source/enumvalue.rst ================================================ .. _enumvalue-example: doxygenenumvalue Directive Example ================================== Working Example --------------- .. cpp:namespace:: @ex_enumvalue_example This should work: .. code-block:: rst .. doxygenenumvalue:: TIXML_NO_ERROR :project: tinyxml It produces this output: .. doxygenenumvalue:: TIXML_NO_ERROR :project: tinyxml Example with Namespace ---------------------- .. cpp:namespace:: @ex_enumvalue_namespace This should work: .. code-block:: rst .. doxygenenumvalue:: foo::ns::A :project: namespace It produces this output: .. doxygenenumvalue:: foo::ns::A :project: namespace Failing Example --------------- .. cpp:namespace:: @ex_enumvalue_failing This intentionally fails: .. code-block:: rst .. doxygenenumvalue:: made_up_enumvalue :project: restypedef It produces the following warning message: .. warning:: doxygenenumvalue: Cannot find enumvalue "made_up_enumvalue" in doxygen xml output ================================================ FILE: documentation/source/file.rst ================================================ .. _file-example: doxygenfile Directive ===================== This directive generates the appropriate output for a single source file. It takes the standard ``project``, ``path``, ``outline`` and ``no-link`` options and additionally the ``sections`` options. For the standard option refer to the documentation on the main directives page. The directive-specific options are documented below. ``sections`` Limit the sections to render for the given source file to the given list. Many of the names come from Doxygen and Breathe internals and may not make sense from an external point of view. Nevertheless it is hoped this table is useful. .. csv-table:: Section types :header: "Type", "Description" "briefdescription", "Brief description" "dcop-func", "DCOP Function" "define", "Define" "derivedcompoundref", "Derived compound reference" "detaileddescription", "Detailed description" "enum", "Enumerator" "event", "Events" "friend", "Friend" "func", "Function" "innerclass", "**Must be given to show sections inside a class**" "innernamespace", "**Must be given to show sections inside a namespace**" "package-attrib", "Package attribute" "package-func", "Package function" "package-static-attrib", "Package static attribute" "package-static-func", "Package static function" "package-type", "Package type" "private-attrib", "Private attribute" "private-func", "Private function" "private-slot", "Private slot" "private-static-attrib", "Private static attribute" "private-static-func", "Private static function" "private-type", "Private type" "property", "Properties" "protected-attrib", "Protected attribute" "protected-func", "Protected function" "protected-slot", "Protected slot" "protected-static-attrib", "Protected static attribute" "protected-static-func", "Protected static function" "protected-type", "Protected type" "prototype", "Prototype" "public-attrib", "Public attribute" "public-func", "Public function" "public-slot", "Public slot" "public-static-attrib", "Public static attribute" "public-static-func", "Public static function" "public-type", "Public type" "related", "Related" "signal", "Signal" "typedef", "Type definition" "user-defined", "User defined" "var", "Variable" Example ------- .. cpp:namespace:: @ex_file_example This should work: .. code-block:: rst .. doxygenfile:: nutshell.h :project: nutshell It produces this output: ---- .. doxygenfile:: nutshell.h :project: nutshell Example with Selected and Ordered Sections ------------------------------------------ .. cpp:namespace:: @ex_file_section The following will only show the **briefdescription** and **public-type** sections, in that order: .. code-block:: rst .. doxygenfile:: nutshell.h :project: nutshell :sections: briefdescription innerclass public-type It produces this output: ---- .. doxygenfile:: nutshell.h :project: nutshell :sections: briefdescription innerclass public-type :no-link: Example with Nested Namespaces ------------------------------ .. cpp:namespace:: @ex_file_namespace This should work: .. code-block:: rst .. doxygenfile:: namespacefile.h :project: namespace It produces this output: ---- .. doxygenfile:: namespacefile.h :project: namespace Example for Multiple Files -------------------------- .. cpp:namespace:: @ex_file_multiple_files When there are multiple files with the same name in the project, you need to be more specific with the filename you provide. For example, in a project with the following two files: .. code-block:: text /some/long/project/path/parser/Util.h /some/long/project/path/finder/Util.h You should specify: .. code-block:: rst .. doxygenfile:: parser/Util.h .. doxygenfile:: finder/Util.h To uniquely identify them. Failing Example --------------- .. cpp:namespace:: @ex_file_failing This intentionally fails: .. code-block:: rst .. doxygenfile:: made_up_file.h :project: nutshell It produces the following warning message: .. warning:: Cannot find file "made_up_file.h" in doxygen xml output for project "nutshell" from directory: ../../examples/specific/nutshell/xml/ ================================================ FILE: documentation/source/function.rst ================================================ .. _function-example: doxygenfunction Directive Example ================================= This directive generates the appropriate output for a single function. The function name, including namespace, is required to be unique in the project. For functions which have a declaration and definition in separate files, doxygen generates two entries for the function and Breathe can get confused when faced with this. As a result Breathe ignores what it considers to be the implementation files when searching for the XML for the function documentation. In order to do this, it ignores any entries in files which have filename extensions listed in the :ref:`breathe_implementation_filename_extensions ` config variable. Working Example --------------- .. cpp:namespace:: @ex_function_example This should work: .. code-block:: rst .. doxygenfunction:: open :project: structcmd It produces this output: .. doxygenfunction:: open :project: structcmd Separated Declaration & Implementation Example ---------------------------------------------- .. cpp:namespace:: @ex_function_separated This should work: .. code-block:: rst .. doxygenfunction:: open_di :project: decl_impl It produces this output: .. doxygenfunction:: open_di :project: decl_impl Failing Example --------------- .. cpp:namespace:: @ex_function_failing This intentionally fails: .. code-block:: rst .. doxygenfunction:: made_up_function :project: structcmd It produces the following warning message: .. warning:: doxygenfunction: Cannot find function "made_up_function" in doxygen xml output ================================================ FILE: documentation/source/group.rst ================================================ .. _group-example: doxygengroup Directive ====================== This directive generates the appropriate output for the contents of a doxygen group. A doxygen group can be declared with specific doxygen markup in the source comments as cover in the `doxygen grouping documentation`_. It takes the standard ``project``, ``path``, ``outline`` and ``no-link`` options and additionally the ``content-only``, ``desc-only``, ``members``, ``protected-members``, ``private-members``, ``undoc-members`` and ``inner`` options. ``content-only`` If this flag is specified, then the directive does not output the name of the group or the group description and instead outputs the contents of the group. This can be useful if the groups are only used for organizational purposes and not to provide additional information. ``desc-only`` If specified, only the description and name of the group will be displayed. ``members`` If specified, the public members of any classes in the group output will be displayed. Unlike the ``doxygenclass`` ``members`` option, this does not optionally take a list of member names to display as this will be applied across multiple classes within the group. ``protected-members`` If specified, the protected members of any classes in the group output will be displayed. ``private-members`` If specified, the private members of any classes in the group output will be displayed. ``undoc-members`` If specified, the undocumented members of any classes in the group output will be displayed provided the appropriate ``members`` or ``private-members`` options are specified as well. If you would like to always specify some combination of ``members``, ``protected-members``, ``private-members`` and ``undoc-members`` then you can use the :confval:`breathe_default_members` configuration variable to set it in the ``conf.py``. ``inner`` If specified, the groups that were defined inside this group, by either defining them inside the scope of another group, or by using the Doxygen \ingroup command, are also parsed and loaded. .. _doxygen grouping documentation: https://www.doxygen.nl/manual/grouping.html Basic Example ------------- .. cpp:namespace:: @ex_group_basic The plain ``doxygengroup`` directive will output the group name and description and any top level publicly visible members of the group. .. code-block:: rst .. doxygengroup:: mygroup :project: group It produces this output: .. doxygengroup:: mygroup :project: group Content-Only Example -------------------- .. cpp:namespace:: @ex_group_content_only The ``content-only`` option changes the output to only include the content of the group and not the group name or description. So this: .. code-block:: rst .. doxygengroup:: mygroup :project: group :content-only: Produces this output: .. doxygengroup:: mygroup :project: group :content-only: :no-link: .. note:: As you can see from the output, section headings like 'Functions' are missing from the ``:content-only:`` display. This is due to an implementation detail. If post an issue on github if you'd like it addressed. Members Example --------------- .. cpp:namespace:: @ex_group_members The ``members`` option changes the output to include the public members of any classes. The output for any class in the group should be the same as if it had be produced by the :ref:`doxygenclass directive ` with the ``members`` option specified. .. code-block:: rst .. doxygengroup:: mygroup :project: group :members: It produces this output: .. doxygengroup:: mygroup :project: group :members: :no-link: Protected Members Example ------------------------- .. cpp:namespace:: @ex_group_members_protected The ``protected-members`` option changes the output to include the protected members of any classes. The output for any class in the group should be the same as if it had be produced by the :ref:`doxygenclass directive ` with the ``protected-members`` option specified. .. code-block:: rst .. doxygengroup:: mygroup :project: group :protected-members: It produces this output: .. doxygengroup:: mygroup :project: group :protected-members: :no-link: Private-Members Example ----------------------- .. cpp:namespace:: @ex_group_members_private The ``private-members`` option changes the output to include the private members of any classes. The output for any class in the group should be the same as if it had be produced by the :ref:`doxygenclass directive ` with the ``private-members`` option specified. .. code-block:: rst .. doxygengroup:: mygroup :project: group :private-members: Produces this output: .. doxygengroup:: mygroup :project: group :private-members: :no-link: Undocumented Members Example ---------------------------- .. cpp:namespace:: @ex_group_members_undocumented The ``undoc-members`` option changes the output to include any undocumentated members from the sections (public, protected, private) that are being displayed for the classes in the group output. .. code-block:: rst .. doxygengroup:: mygroup :project: group :private-members: :undoc-members: Produces this output: .. doxygengroup:: mygroup :project: group :private-members: :undoc-members: :no-link: .. note:: Undocumented classes are still not shown in the output due to an implementation issue. Please post an issue on github if you would like this resolved. Inner Example ------------- .. cpp:namespace:: @ex_group_inner The ``inner`` option changes the output to include groups that are defined inside other groups. .. code-block:: rst .. doxygengroup:: mygroup :project: group :inner: Produces this output: .. doxygengroup:: mygroup :project: group :inner: :no-link: Outline Example --------------- .. cpp:namespace:: @ex_group_outline This displays only the names of the members of the group and not their documentation. The other options determine which members are displayed. .. code-block:: rst .. doxygengroup:: mygroup :project: group :members: :outline: It produces this output: .. doxygengroup:: mygroup :project: group :members: :outline: :no-link: Failing Example --------------- .. cpp:namespace:: @ex_group_failing This intentionally fails: .. code-block:: rst .. doxygengroup:: madeupgroup :project: group It produces the following warning message: .. warning:: Cannot find file "madeupgroup" in doxygen xml output for project "group" from directory: ../../examples/specific/group/xml/ ================================================ FILE: documentation/source/groups.rst ================================================ Groups ====== .. cpp:namespace:: @ex_groups Breathe has basic support for the grouping functionality that Doxygen provides. Using the example from the Doxygen docs: .. literalinclude:: code/groups.h :language: cpp If we reference this with a directive, for example: .. code-block:: rst .. doxygenclass:: UserDefinedGroupTest :project: userdefined :members: :protected-members: It renders as: .. doxygenclass:: UserDefinedGroupTest :project: userdefined :members: :protected-members: .. note:: Any groups which are not named in the original source code will appear as **Unnamed Group** in the final output. This is different to Doxygen which will number the groups and so name them as Group1, Group2, Group3, etc. ================================================ FILE: documentation/source/index.rst ================================================ .. BreatheExample documentation master file, created by sphinx-quickstart on Tue Feb 3 18:20:48 2009. You can adapt this file completely to your liking, but it should at least contain the root `toctree` directive. Breathe's documentation ======================= Breathe provides a bridge between the Sphinx and Doxygen documentation systems. It is an easy way to include Doxygen information in a set of documentation generated by Sphinx. The aim is to produce an autodoc like support for people who enjoy using Sphinx but work with languages other than Python. The system relies on the Doxygen's xml output. .. only:: documentation_build_readthedocs_latest .. warning:: This documentation is built from the latest `Breathe github project `_ code. It does not necessarily reflect a released version of Breathe on PyPI. .. only:: documentation_build_development .. warning:: This build of the documentation is not from a specific tagged release of Breathe. It reflects a work in progress state of Breathe. Please see the `github repository `_ for a more official source of information. Overview -------- * **Simple setup** - one doxygen config value, one Sphinx config value and one directive and you'll be on your way. * **High and low level directives** - reference the whole project, just a class or just a function with different directives. * **Support for multiple doxygen projects** - set it up to be aware of different projects and reference them by name or path for each directive. * **Allows embedded reStructuredText in doxygen markup** - some extra doxygen aliases allow you to add ``\rst`` - ``\endrst`` blocks to your comments and have the contents interpreted as reStructuredText. * **Basic support for Sphinx domains** - Link to functions in the breathe output with a standard Sphinx domain reference. Setup & Usage ------------- .. toctree:: :maxdepth: 1 quickstart directives differences readthedocs Features -------- .. toctree:: :maxdepth: 1 markups latexmath codeblocks domains customcss groups lists tables template dot_graphs Contributing ------------ .. toctree:: :maxdepth: 1 contributing codeguide credits Example/Test Pages ------------------ .. toctree:: :maxdepth: 1 testpages Download -------- Breathe is available from: * `PyPI, the Python Package Index `_ * `Github `_ License ------- Breathe is under the `BSD license `_. In a Nutshell ------------- You write code that looks a little like this: .. literalinclude:: code/nutshell.h :language: cpp Then you run this: .. code-block:: text doxygen With a setting that says this: .. code-block:: text GENERATE_XML = YES Then in your Sphinx documentation, you add something like this: .. code-block:: rst .. doxygenclass:: Nutshell :project: nutshell :members: With a ``conf.py`` setting like this: .. code-block:: python breathe_projects = { "nutshell": "../../examples/specific/nutshell/xml/", } And Breathe registered as an extension in ``conf.py`` like this: .. code-block:: rst extensions = ["breathe"] You get something like this: ---- .. cpp:namespace:: @ex_index .. doxygenclass:: Nutshell :project: nutshell :members: ---- Sound reasonable? To get started, go checkout the :doc:`quickstart guide `. ================================================ FILE: documentation/source/inline.rst ================================================ Inline Parameter Documentation ============================== This is currently flawed as it doesn't know where to sensibly put the parameters in the final description. It currently defaults to the top of the detail description block which means it comes between the brief description text and the detailed description text which is less than ideal, but attempts to programmatically figure out where the detail description text ends have failed. Something for future work. Example ------- .. cpp:namespace:: @ex_inline .. doxygenindex:: :path: ../../examples/specific/inline/xml ================================================ FILE: documentation/source/latexmath.rst ================================================ Latex Math ========== Breathe has basic support for latex math markup in the doxygen comments. A class with a comment like: .. code-block:: cpp /** * @brief A class * * A inline formula: \f$ f(x) = a + b \f$ * * A display style formula: * @f[ * \int_a^b f(x) dx = F(b) - F(a) * @f] */ class MathHelper { public: MathHelper() {} ~MathHelper() {} } Will be renderer as: .. cpp:namespace:: @ex_latexmath .. doxygenclass:: MathHelper :project: latexmath :members: :undoc-members: Without any additional configuration except for including a math extension in the Sphinx ``conf.py``: .. code-block:: python extensions = ["breathe", "sphinx.ext.mathjax"] The specific environment formula fails when using ``sphinx.ext.pngmath`` so more work is needed. Implementation -------------- Breathe uses a internal reStructuredText node provided by ``sphinx.ext.mathbase`` which is then picked up and rendered by the extension chosen in the ``conf.py``. It does not pass any additional options through to the node, so settings like ``label`` and ``nowrap`` are currently not supported. Credits ------- Thank you to `dazzlezhang `_ for providing examples and a full run down of necessary details. It made the implementation much easier. ================================================ FILE: documentation/source/lists.rst ================================================ Lists ====== Breathe has support for lists in the doxygen documentation. They are output as follows. .. cpp:namespace:: @ex_lists_plus For unordered lists with list items prefixed with **+**: .. code-block:: rst .. doxygenclass:: SimpleList_1 :project: lists It renders as: ---- .. doxygenclass:: SimpleList_1 :project: lists ---- .. cpp:namespace:: @ex_lists_dash Unordered lists with list items prefixed with **-** render as: ---- .. doxygenclass:: SimpleList_2 :project: lists ---- .. cpp:namespace:: @ex_lists_star Unordered lists with list items prefixed with **\*** render as: ---- .. doxygenclass:: SimpleList_3 :project: lists ---- .. cpp:namespace:: @ex_lists_html Unordered lists defined using HTML tags **
  • ** render as: ---- .. doxygenclass:: SimpleList_6 :project: lists ---- .. cpp:namespace:: @ex_lists_auto Auto-numbered lists with list items prefixed with **-#** render as: ---- .. doxygenclass:: SimpleList_4 :project: lists ---- .. cpp:namespace:: @ex_lists_arabic Numbered lists with list items prefixed with Arabic numerals **1. 2. ...** render as: ---- .. doxygenclass:: SimpleList_5 :project: lists ---- .. note:: Numbered lists support for the moment only Arabic numerals. Nested lists are supported in all combinations, as long as they are valid doxygen markup. Below are a couple of examples of different nested lists documentation and their corresponding breathe output. .. cpp:namespace:: @ex_lists_nested_1 Documentation looking like this: .. literalinclude:: code/nested_list_1.h :language: cpp renders as: ---- .. doxygenclass:: NestedLists_1 :project: lists ---- .. cpp:namespace:: @ex_lists_nested_2 Documentation looking like this: .. literalinclude:: code/nested_list_2.h :language: cpp renders as: ---- .. doxygenclass:: NestedLists_2 :project: lists ---- .. cpp:namespace:: @ex_lists_nested_3 Documentation looking like this: .. literalinclude:: code/nested_list_3.h :language: cpp renders as: ---- .. doxygenclass:: NestedLists_3 :project: lists ---- .. cpp:namespace:: @ex_lists_nested_4 Documentation looking like this: .. literalinclude:: code/nested_list_4.h :language: cpp renders as: ---- .. doxygenclass:: NestedLists_4 :project: lists ---- .. cpp:namespace:: @ex_lists_nested_5 Documentation looking like this: .. literalinclude:: code/nested_list_5.h :language: cpp renders as: ---- .. doxygenclass:: NestedLists_5 :project: lists ================================================ FILE: documentation/source/markups.rst ================================================ Supported Markups ================= All comments in your code must be formatted in a doxygen-compliant way so that doxygen can do its job. Doxygen provides support for formatting your text with tags, such as ``\b`` for adding bold text, this information appears in the xml output and Breathe attempts to reproduce it accurately. In addition to this, is it possible to add reStructuredText into your comments within appropriately demarcated sections. reStructuredText ---------------- .. cpp:namespace:: @ex_markups_rst Breathe supports reStructuredText within doxygen **verbatim** blocks which begin with the markup **embed:rst**. This means that a comment block like this: .. code-block:: cpp /*! Inserting additional reStructuredText information. \verbatim embed:rst .. note:: This reStructuredText has been handled correctly. \endverbatim */ Will be rendered as: .. doxygenfunction:: TestClass::rawVerbatim :project: rst :no-link: Handling Leading Asterisks ~~~~~~~~~~~~~~~~~~~~~~~~~~ .. cpp:namespace:: @ex_markups_leading_star Note that doxygen captures **all** content in a **verbatim** block. This can be rather an annoyance if you use a leading-asterisk style of comment block such as the following: .. code-block:: cpp /*! * Inserting additional reStructuredText information. * * \verbatim embed:rst * Some example code * * .. code-block:: cpp * * int example(int x) { * return x * 2; * } * \endverbatim */ As the leading asterisks are captured in the **verbatim** block this will appear to be an incorrectly formatted bullet list. Due to the syntactical problems Sphinx will issue warnings and the block will render as: .. Here we fake the bad output without actually using a bad example otherwise we'll get warnings in the build output. void **rawBadAsteriskVerbatim**\ () Inserting additional reStructuredText information. - Some example code: - int example(int x) { - return x \* 2; - } To prevent this, use an **embed:rst:leading-asterisk** tag: .. code-block:: cpp /*! * Inserting additional reStructuredText information. * * \verbatim embed:rst:leading-asterisk * Some example code:: * * int example(int x) { * return x * 2; * } * \endverbatim */ This will appropriately handle the leading asterisks and render as: ---- .. doxygenfunction:: TestClass::rawLeadingAsteriskVerbatim :project: rst ---- Handling Leading Slashes ~~~~~~~~~~~~~~~~~~~~~~~~ .. cpp:namespace:: @ex_markups_leading_slash Similar troubles can be encountered when using comment blocks that start with a triple forward slash. For example: .. code-block:: cpp /// Some kind of method /// /// @param something a parameter /// @returns the same value provided in something param /// /// @verbatim embed:rst:leading-slashes /// .. code-block:: c /// :linenos: /// /// bool foo(bool something) { /// return something; /// }; /// /// @endverbatim /// @note Documentation using `///` should begin and end in a blank line. For these kinds of blocks, you can use an **embed:rst:leading-slashes** tag as shown in the above example. This will appropriately handle the leading slashes and render as: ---- .. doxygenfunction:: TestClass::rawLeadingSlashesVerbatim :project: rst ---- Inline rST ~~~~~~~~~~ .. cpp:namespace:: @ex_markups_inline Normal verbatim elements result in block elements. But sometimes you'll want to generate rST references where they need to be rendered inline with the text. For example: .. code-block:: cpp /// Some kind of method /// /// @param something a parameter /// @returns the same value provided in something param /// /// @verbatim embed:rst:inline some inline text @endverbatim For these kinds of references, you can use an **embed:rst:inline** tag as shown in the above example. This will appropriately handle the leading slashes and render as: ---- .. doxygenfunction:: TestClass::rawInlineVerbatim :project: rst .. doxygenfunction:: TestClass::rawVerbatim :project: rst :outline: ---- Aliases ~~~~~~~ .. cpp:namespace:: @ex_markups_aliases To make these blocks appear as more appropriate doxygen-like markup in your comments you can add the following aliases to your doxygen configuration file: .. code-block:: text ALIASES = "rst=^^\verbatim embed:rst^^" ALIASES += "endrst=\endverbatim" Which allow you to write comments like: .. code-block:: cpp /*! Inserting additional reStructuredText information. \rst This is some funky non-xml compliant text: <& !>< .. note:: This reStructuredText has been handled correctly. \endrst This is just a standard verbatim block with code: \verbatim child = 0; while( child = parent->IterateChildren( child ) ) \endverbatim */ Which will be rendered as: .. doxygenfunction:: TestClass::function :project: rst .. note:: The character sequence ``^^`` in an ALIAS definition inserts a line break. The leading ``^^`` ensures that a RST ``\verbatim`` block never starts in a brief description (which would break the output), even if you put it on the first line of a comment. The trailing ``^^`` allows to start with RST content on the same line of the ``\rst`` command. The ALIAS given above only works for comment blocks without a leading comment character on each line. If you use a comment style with a leading comment character on each new line, use these aliases instead: .. code-block:: text ALIASES = "rst=^^\verbatim embed:rst:leading-asterisk^^" ALIASES += "endrst=\endverbatim" Due to an `undocumented behavior in doxygen `_, all leading comment characters (``*``, ``///`` or ``//!``) encountered in a verbatim section will be converted to asterisk (``*`` ) by Doxygen, when ``\verbatim`` is part of an alias. Therefore, the ALIAS above works in all comment blocks with leading line comment characters. If you want to hide reStructuredText output from Doxygen html and only include it in sphinx, use the Doxygen command ``\xmlonly``. This is an example alias that enables all RST sections in XML only: .. code-block:: text ALIASES = "rst=^^\xmlonlyembed:rst:leading-asterisk^^" ALIASES += "endrst=\endxmlonly" ================================================ FILE: documentation/source/members.rst ================================================ Members Tests ============= All Members ----------- .. cpp:namespace:: @ex_members_all .. doxygenclass:: testnamespace::NamespacedClassTest :path: ../../examples/specific/members/xml :members: Specific Members ---------------- .. cpp:namespace:: @ex_members_specific .. doxygenclass:: testnamespace::NamespacedClassTest :path: ../../examples/specific/members/xml :members: functionS, anotherFunction :no-link: No Members ---------- .. cpp:namespace:: @ex_members_no .. doxygenclass:: testnamespace::NamespacedClassTest :path: ../../examples/specific/members/xml :no-link: Struct Members ---------------- .. cpp:namespace:: @ex_members_struct .. doxygenfunction:: testnamespace::MyClass::MyClass() :path: ../../examples/specific/struct_function/xml .. doxygenfunction:: testnamespace::MyClass::MyClass(const MyClass&) :path: ../../examples/specific/struct_function/xml .. doxygenvariable:: testnamespace::MyClass::myMemberVar :path: ../../examples/specific/struct_function/xml ================================================ FILE: documentation/source/namespace.rst ================================================ .. _namespace-example: doxygennamespace Directive ========================== This directive generates the appropriate output for the contents of a namespace. It takes the standard ``project``, ``path``, ``outline`` and ``no-link`` options and additionally the ``content-only``, ``desc-only``, ``members``, ``protected-members``, ``private-members`` and ``undoc-members`` options. ``content-only`` If this flag is specified, then the directive does not output the name of the namespace or the namespace description and instead outputs the contents of the namespace. This can be useful for structuring your documentation but leaving out the namespace declaration itself which is often undocumented. ``desc-only`` If specified, only the description and name of the namespace will be displayed. ``members`` If specified, the public members of any classes in the namespace output will be displayed. Unlike the ``doxygenclass`` ``members`` option, this does not optionally take a list of member names to display as this will be applied across multiple classes within the namespace. ``protected-members`` If specified, the protected members of any classes in the namespace output will be displayed. ``private-members`` If specified, the private members of any classes in the namespace output will be displayed. ``undoc-members`` If specified, the undocumented members of any classes in the namespace output will be displayed provided the appropriate ``members`` or ``private-members`` options are specified as well. If you would like to always specify some combination of ``members``, ``protected-members``, ``private-members`` and ``undoc-members`` then you can use the :ref:`breathe_default_members ` configuration variable to set it in the ``conf.py``. Basic Example ------------- .. cpp:namespace:: @ex_namespace_basic The plain ``doxygennamespace`` directive will output the namespace name and description and any top level publicly visible members of the namespace. .. code-block:: rst .. doxygennamespace:: foo :project: namespace It produces this output: .. doxygennamespace:: foo :project: namespace Content-Only Example -------------------- .. cpp:namespace:: @ex_namespace_content_only The ``content-only`` option changes the output to only include the content of the namespace and not the namespace name or description. So this: .. code-block:: rst .. doxygennamespace:: foo :project: namespace :content-only: Produces this output: .. doxygennamespace:: foo :project: namespace :content-only: :no-link: .. note:: As you can see from the output, section headings like 'Functions' are missing from the ``:content-only:`` display. This is due to an implementation detail. Open an issue on github if you'd like it addressed. Members Example --------------- .. cpp:namespace:: @ex_namespace_members The ``members`` option changes the output to include the public members of any classes. The output for any class in the namespace should be the same as if it had be produced by the :ref:`doxygenclass directive ` with the ``members`` option specified. .. code-block:: rst .. doxygennamespace:: foo :project: namespace :members: It produces this output: .. doxygennamespace:: foo :project: namespace :members: :no-link: Protected Members Example ------------------------- .. cpp:namespace:: @ex_namespace_members_protected The ``protected-members`` option changes the output to include the protected members of any classes. The output for any class in the namespace should be the same as if it had be produced by the :ref:`doxygenclass directive ` with the ``protected-members`` option specified. .. code-block:: rst .. doxygennamespace:: foo :project: namespace :protected-members: It produces this output: .. doxygennamespace:: foo :project: namespace :protected-members: :no-link: Private-Members Example ----------------------- .. cpp:namespace:: @ex_namespace_members_private The ``private-members`` option changes the output to include the private members of any classes. The output for any class in the namespace should be the same as if it had be produced by the :ref:`doxygenclass directive ` with the ``private-members`` option specified. .. code-block:: rst .. doxygennamespace:: foo :project: namespace :private-members: Produces this output: .. doxygennamespace:: foo :project: namespace :private-members: :no-link: Undocumented Members Example ---------------------------- .. cpp:namespace:: @ex_namespace_members_undocumented The ``undoc-members`` option changes the output to include any undocumentated members from the sections (public, protected, private) that are being displayed for the classes in the namespace output. .. code-block:: rst .. doxygennamespace:: foo :project: namespace :private-members: :undoc-members: Produces this output: .. doxygennamespace:: foo :project: namespace :private-members: :undoc-members: :no-link: .. note:: Undocumented classes are still not shown in the output due to an implementation issue. Please post an issue on github if you would like this resolved. Outline Example --------------- .. cpp:namespace:: @ex_namespace_outline This displays only the names of the members of the namespace and not their documentation. The other options determine which members are displayed. .. code-block:: rst .. doxygennamespace:: foo :project: namespace :members: :outline: It produces this output: .. doxygennamespace:: foo :project: namespace :members: :outline: :no-link: Nested Example -------------- .. cpp:namespace:: @ex_namespace_nested The referenced namespace can be nested in another namespace. .. code-block:: rst .. doxygennamespace:: foo::ns :project: namespace Produces this output: .. doxygennamespace:: foo::ns :project: namespace :no-link: Failing Example --------------- .. cpp:namespace:: @ex_namespace_failing This intentionally fails: .. code-block:: rst .. doxygennamespace:: madeupnamespace :project: namespace It produces the following warning message: .. warning:: doxygennamespace: Cannot find namespace “madeupnamespace” in doxygen xml output for project “namespace” from directory: ../../examples/specific/namespacefile/xml/ ================================================ FILE: documentation/source/page.rst ================================================ .. _page-example: doxygenpage Directive ===================== This directive generates the appropriate output for the contents of a doxygen page. A doxygen page is created for each "key" of every \\xrefitem command used for markup in the source comments. For more information check the `doxygen documentation`_. It takes the standard ``project`` and ``path`` options. .. code-block:: rst .. doxygenpage:: :project: ... :path: ... .. _doxygen documentation: https://www.doxygen.nl/manual/commands.html#cmdxrefitem Basic Example ------------- .. cpp:namespace:: @ex_page_basic The plain ``doxygenpage`` directive will output the page name and description and any variable entries which were defined to be part of this page (with an \xrefitem usage). .. code-block:: rst .. doxygenpage:: xrefsample :project: xrefsect It produces this output: .. doxygenpage:: xrefsample :project: xrefsect Failing Example --------------- .. cpp:namespace:: @ex_page_failing This intentionally fails: .. code-block:: rst .. doxygengroup:: madeuppage :project: xrefsect It produces the following warning message: .. warning:: Cannot find file "madeuppage" in doxygen xml output for project "xrefsect" from directory: ../../examples/specific/xrefsect/xml/ ================================================ FILE: documentation/source/quickstart.rst ================================================ Quick Start =========== For this quick start we assume the following prerequisites: * breathe was downloaded and extracted somewhere * doxygen was installed and doxygen output (XML format) was generated for the project that is to be documented (set GENERATE_XML tag to YES) We assume the following paths: * documentation root path: :file:`/home/me/docproj/` * breathe path: :file:`/home/me/docproj/ext/breathe/` * doxygen xml output: :file:`/home/me/docproj/doxyxml/` The documentation path should contain a folder :file:`source` containing the :file:`conf.py` file. The doxygen xml output folder should contain the :file:`index.xml` output file generated by doxygen. The following steps are required to integrate breathe functionality: #. Add the breathe path to your conf.py by adding the following line: .. code-block:: python sys.path.append("/home/me/docproj/ext/breathe/") #. Add breathe as an extension the line could look like this: .. code-block:: python extensions = ['sphinx.ext.pngmath', 'sphinx.ext.todo', 'breathe' ] #. Tell breathe about the projects: .. code-block:: python breathe_projects = {"myproject": "/home/me/docproj/doxyxml/"} #. Specify a default project: .. code-block:: python breathe_default_project = "myproject" Once this is done you may use the following commands to include documentation for different constructs: .. code-block:: rst .. doxygenindex:: .. doxygenfunction:: .. doxygenstruct:: .. doxygenenum:: .. doxygentypedef:: .. doxygenclass:: For each of these commands the following directives may be specified: ``project`` Specifies which project, as defined in the breathe_projects config value, should be used for this directive. This overrides the default. ``path`` Directly specifies the path to the folder with the doxygen output. This overrides the project and default project. ================================================ FILE: documentation/source/readthedocs.rst ================================================ Running on Read the Docs ========================= `Read the Docs`_ is an excellent site for hosting project documentation. It provides hooks into common project hosting sites like Github_ & Bitbucket_ and can rebuild your documentation automatically whenever you push new code. The site is designed for documentation written with Sphinx and supports Sphinx extensions via a correctly configured ``setup.py`` file. As Breathe is a Sphinx extension you can use it on Read the Docs. However, as Breathe requires doxygen XML files, some additional configuration is required. Doxygen Support --------------- Read the Docs do not explicitly support doxygen however they have had requests for it to be supported and it is currently installed on their build servers. Generating Doxygen XML Files ---------------------------- We assume that you are not checking your doxygen XML files into your source control system and so you will need to generate them on the Read the Docs server before Sphinx starts processing your documentation. One simple way of achieving this is to add the following code to your ``conf.py`` file: .. code-block:: python import subprocess, os read_the_docs_build = os.environ.get('READTHEDOCS', None) == 'True' if read_the_docs_build: subprocess.call('cd ../doxygen; doxygen', shell=True) The first line uses the ``READTHEDOCS`` environment variable to determine whether or not we are building on the Read the Docs servers. Read the Docs set this environment variable `specifically for this purpose`_. Then, if we are in a Read the Docs build, execute a simple shell command to build the doxygen xml for your project. This is a very simple example; the command will be determined by your project set up but something like this works for the Breathe documentation. As this is then executed right at the start of the ``sphinx-build`` process then all your doxygen XML files will be in place for the build. A More Involved Setup --------------------- If you'd rather do something more involved then you can run ``doxygen`` as part of a ``builder-inited`` event hook which you can install from your ``conf.py`` file by adding a ``setup`` function as shown below. This is an approximation of the code that Breathe has in its ``conf.py`` in order to run ``doxygen`` on the Read the Docs server. .. code-block:: python import subprocess, sys def run_doxygen(folder): """Run the doxygen make command in the designated folder""" try: retcode = subprocess.call("cd %s; make" % folder, shell=True) if retcode < 0: sys.stderr.write("doxygen terminated by signal %s" % (-retcode)) except OSError as e: sys.stderr.write("doxygen execution failed: %s" % e) def generate_doxygen_xml(app): """Run the doxygen make commands if we're on the ReadTheDocs server""" read_the_docs_build = os.environ.get('READTHEDOCS', None) == 'True' if read_the_docs_build: run_doxygen("../../examples/doxygen") run_doxygen("../../examples/specific") run_doxygen("../../examples/tinyxml") def setup(app): # Add hook for building doxygen xml when needed app.connect("builder-inited", generate_doxygen_xml) .. _Read the Docs: https://readthedocs.org/ .. _Github: https://github.com .. _Bitbucket: https://bitbucket.org .. _specifically for this purpose: https://docs.readthedocs.org/en/latest/faq.html#how-do-i-change-behavior-for-read-the-docs ================================================ FILE: documentation/source/specific.rst ================================================ Specific Examples Test Suite ============================ Template Type Alias ------------------- .. cpp:namespace:: @ex_specific_alias_template .. doxygentypedef:: IsFurry :path: ../../examples/specific/template_type_alias/xml .. doxygentypedef:: IsFuzzy :path: ../../examples/specific/template_type_alias/xml Typedef Examples ---------------- .. cpp:namespace:: @ex_specific_typedef .. doxygenindex:: :path: ../../examples/specific/typedef/xml Namespaced Function Examples ---------------------------- .. cpp:namespace:: @ex_specific_namespaced_function .. doxygenfunction:: TestNamespaceClasses::NamespacedClassTest::function :path: ../../examples/specific/class/xml .. doxygenfunction:: TestNamespaceClasses::ClassTest::function :path: ../../examples/specific/class/xml .. doxygenfunction:: TestNamespaceClasses::ClassTest::anotherFunction :path: ../../examples/specific/class/xml .. doxygenfunction:: ClassTest::function :path: ../../examples/specific/class/xml .. doxygenfunction:: ClassTest::anotherFunction :path: ../../examples/specific/class/xml .. doxygenfunction:: f0 :path: ../../examples/specific/class/xml .. doxygenfunction:: f0< std::string > :path: ../../examples/specific/class/xml .. doxygenfunction:: NS1::f1 :path: ../../examples/specific/class/xml .. doxygenfunction:: NS1::f1< std::string > :path: ../../examples/specific/class/xml .. doxygenfunction:: NS1::NS2::f2 :path: ../../examples/specific/class/xml .. doxygenfunction:: NS1::NS2::f2< std::string > :path: ../../examples/specific/class/xml Extern Examples --------------- .. cpp:namespace:: @ex_specific_extern_examples .. doxygenfunction:: cache_tree_free :project: c_file .. doxygenstruct:: cache_tree :project: c_file :outline: Fixed Width Font ---------------- .. cpp:namespace:: @ex_specific_fixed_width .. doxygenclass:: Out :path: ../../examples/specific/fixedwidthfont/xml :members: Function Overloads ------------------ .. cpp:namespace:: @ex_specific_function_overloads .. doxygenfunction:: f(int, int) :project: functionOverload .. doxygenfunction:: f(double, double) :project: functionOverload .. doxygenfunction:: test::g(int,int) :project: functionOverload .. doxygenfunction:: test::g(double, double) :project: functionOverload .. doxygenfunction:: h(std::string, MyType) :project: functionOverload .. doxygenfunction:: h(std::string, MyOtherType) :project: functionOverload .. doxygenfunction:: h(std::string, const int) :project: functionOverload .. doxygenfunction:: h(std::string, const T, const U) :project: functionOverload Program Listing --------------- .. cpp:namespace:: @ex_specific_program_listing .. doxygenclass:: Vector :project: programlisting .. doxygenfunction:: center :project: programlisting Image ----- .. cpp:namespace:: @ex_specific_image .. doxygenclass:: ImageClass :project: image Array Parameter --------------- .. doxygenfunction:: foo :project: array .. doxygenfunction:: bar :project: array C Struct -------- .. doxygenfile:: c_struct.h :project: c_struct C Union ------- .. doxygenfile:: c_union.h :project: c_union C Enum ------ .. doxygenenum:: GSM_BackupFormat :project: c_enum C Typedef --------- .. doxygenfile:: c_typedef.h :project: c_typedef C Macro ------- .. doxygenfile:: c_macro.h :project: c_macro C++ Macro --------- .. doxygenfile:: define.h :project: define Multifile --------- .. cpp:namespace:: @ex_specific_multifile .. doxygenfunction:: TestTemplateFunction :project: multifile Interface Class --------------- .. cpp:namespace:: @ex_specific_interface .. doxygeninterface:: InterfaceClass :project: interface C++ Anonymous Entities ---------------------- .. cpp:namespace:: @ex_specific_cpp_anon .. doxygenfile:: cpp_anon.h :project: cpp_anon C++ Union --------- .. cpp:namespace:: @ex_specific_cpp_union .. doxygenfile:: cpp_union.h :project: cpp_union C++ Enums --------- .. cpp:namespace:: @ex_specific_cpp_enum .. doxygenfile:: cpp_enum.h :project: cpp_enum C++ Functions ------------- .. cpp:namespace:: @ex_specific_cpp_function .. doxygenfile:: cpp_function.h :project: cpp_function C++ Friend Classes ------------------ .. cpp:namespace:: @ex_specific_cpp_friendclass .. doxygenfile:: cpp_friendclass.h :project: cpp_friendclass C++ Inherited Members --------------------- .. cpp:namespace:: @ex_specific_cpp_inherited_members .. doxygenclass:: Base :project: cpp_inherited_members .. doxygenclass:: A :project: cpp_inherited_members .. doxygenclass:: B :project: cpp_inherited_members C++ Template Specialization with Namespace ------------------------------------------ .. cpp:namespace:: @ex_specific_cpp_ns_template_specialization .. doxygenfile:: cpp_ns_template_specialization.h :project: cpp_ns_template_specialization C++ Trailing Return Type ------------------------ .. cpp:namespace:: @ex_specific_cpp_trailing_return_type .. doxygenfile:: cpp_trailing_return_type.h :project: cpp_trailing_return_type C++ Constexpr Handling ------------------------ .. cpp:namespace:: @ex_specific_cpp_constexpr_hax Test for issue 717. .. doxygenfile:: cpp_constexpr_hax.h :project: cpp_constexpr_hax C++ Function Lookup ------------------- .. cpp:namespace:: @ex_specific_cpp_function_lookup .. doxygenfunction:: fNoexcept() :project: cpp_function_lookup .. doxygenfunction:: fFinal() :project: cpp_function_lookup .. doxygenfunction:: fOverride() :project: cpp_function_lookup This one should actually have ``[[myattr]]`` but Doxygen seems to not put attributes into the XML: .. doxygenfunction:: fAttr() :project: cpp_function_lookup .. doxygenfunction:: fFInit() :project: cpp_function_lookup .. doxygenfunction:: fTrailing() :project: cpp_function_lookup .. doxygenfunction:: fInit(int) :project: cpp_function_lookup .. doxygenfunction:: fPlain(int) :project: cpp_function_lookup .. doxygenfunction:: fPtr(int*) :project: cpp_function_lookup .. doxygenfunction:: fLRef(int&) :project: cpp_function_lookup .. doxygenfunction:: fRRef(int&&) :project: cpp_function_lookup .. doxygenfunction:: fParamPack(T...) :project: cpp_function_lookup .. doxygenfunction:: fMemPtr(int A::*) :project: cpp_function_lookup .. doxygenfunction:: fParen(void (*)()) :project: cpp_function_lookup .. doxygenfunction:: fParenPlain(void (*)(int)) :project: cpp_function_lookup Doxygen xrefsect ---------------- .. doxygenfile:: xrefsect.h :project: xrefsect Doxygen simplesect ------------------ .. doxygenfile:: simplesect.h :project: simplesect ================================================ FILE: documentation/source/spelling_wordlist.txt ================================================ STL TVal autodoxygenindex cdata cfile config doxygen doxygenenum doxygenfunction doxygenstruct doxygentypedef doxygenfile doxygenunion doxygenvariable doxygengroup doxygendefine doxygenclass doxygenindex bitmasks madeupgroup enum foo foos frob frobs namespace namespaces namespaced reStructuredText tabsize tinyXml tinyxml tinyXML programmatically struct py conf inline License autodoc renderers github heesch brandl goodger wildcard wildcards xml Func Memgrp Docstring Autolink GVal attributeB ================================================ FILE: documentation/source/struct.rst ================================================ .. This is more or less the class documentation with s/class/struct/g .. _struct-example: doxygenstruct Directive ======================= This directive generates the appropriate output for a single struct. It takes the standard ``project``, ``path``, ``outline`` and ``no-link`` options and additionally the ``members``, ``protected-members``, ``private-members``, ``undoc-members``, ``membergroups`` and ``members-only`` options. ``members`` Designed to behavior in a similar manner to the ``members`` option for the ``autostruct`` directive that comes with the Sphinx ``autodoc`` extension. If you do not specify this option you will not get any information about the struct members, just the general struct documentation. If you provide it without arguments, then Breathe adds all the public members and their documentation. If you specify it with **comma separated** arguments, then Breathe will treat the arguments as names of members and provide documentation for only those members that have been named. ``protected-members`` If specified, the protected members of the struct will be displayed. ``private-members`` If specified, the private members of the struct will be displayed. ``undoc-members`` If specified, the undocumented members of the struct will be displayed. ``membergroups`` If specified, only the groups in a space-delimited list following this directive will be displayed. ``members-only`` This will allow to show only the members, not the struct information. Child classes and structs are also not shown. If you would like to always specify some combination of ``members``, ``protected-members``, ``private-members`` and ``undoc-members`` then you can use the :ref:`breathe_default_members ` configuration variable to set it in the ``conf.py``. Basic Example ------------- .. cpp:namespace:: @ex_struct_basic This displays the struct documentation without any members: .. code-block:: rst .. doxygenstruct:: StructTest :project: struct It produces this output: .. doxygenstruct:: StructTest :project: struct Members Example --------------- .. cpp:namespace:: @ex_struct_members This directive call will display the struct documentation with all the public members: .. code-block:: rst .. doxygenstruct:: StructTest :project: struct :members: It produces this output: .. doxygenstruct:: StructTest :project: struct :members: :no-link: Specific Members Example ------------------------ .. cpp:namespace:: @ex_struct_members_specific This displays the struct documentation with only the members listed in the ``:members:`` option: .. code-block:: rst .. doxygenstruct:: StructTest :project: struct :members: publicFunction, protectedFunction It produces this output: .. doxygenstruct:: StructTest :project: struct :members: publicFunction, protectedFunction :no-link: Protected Members ----------------- .. cpp:namespace:: @ex_struct_members_protected This displays only the protected members of the struct. Normally this is combined with the ``:members:`` option to show the public members as well. .. code-block:: rst .. doxygenstruct:: StructTest :project: struct :protected-members: It produces this output: .. doxygenstruct:: StructTest :project: struct :protected-members: :no-link: Private Members --------------- .. cpp:namespace:: @ex_struct_members_private This displays only the private members of the struct. Normally this is combined with the ``:members:`` option to show the public members as well. .. code-block:: rst .. doxygenstruct:: StructTest :project: struct :private-members: It produces this output: .. doxygenstruct:: StructTest :project: struct :private-members: :no-link: Undocumented Members -------------------- .. cpp:namespace:: @ex_struct_members_undocumented This displays the undocumented members of the struct which are suppressed by default. Undocumented public members are only shown if the ``:members:`` option is also used. The same goes for the undocumented private members and the ``private-members`` option. .. code-block:: rst .. doxygenstruct:: StructTest :project: struct :members: :private-members: :undoc-members: It produces this output: .. doxygenstruct:: StructTest :project: struct :members: :private-members: :undoc-members: :no-link: .. note:: Undocumented internal classes are still not shown in the output due to an implementation issue. Please post an issue on github if you would like this resolved. Membergroups ------------ .. cpp:namespace:: @ex_struct_membersgroups Lists one or more names member groups. See the :ref:`doxygenclass documentation `. Members-only ------------ See the :ref:`doxygenclass documentation `. Outline Example --------------- .. cpp:namespace:: @ex_struct_outline This displays only the names of the struct members and not their documentation. The ``:members:`` and ``:private-members:`` options determine which members are displayed. .. code-block:: rst .. doxygenstruct:: StructTest :project: struct :members: :outline: It produces this output: .. doxygenstruct:: StructTest :project: struct :members: :outline: :no-link: Failing Example --------------- .. cpp:namespace:: @ex_struct_failing This intentionally fails: .. code-block:: rst .. doxygenstruct:: made_up_struct :project: struct :members: It produces the following warning message: .. warning:: doxygenstruct: Cannot find struct "made_up_struct" in doxygen xml output for project “struct” from directory: ../../examples/doxygen/struct/xml/ ================================================ FILE: documentation/source/tables.rst ================================================ Tables ====== Breathe has support for tables in the doxygen documentation. They are output as follows. .. cpp:namespace:: @ex_tables_simple A simple Markdown syntax table: .. code-block:: rst .. doxygenclass:: Table_1 :project: tables It renders as: ---- .. doxygenclass:: Table_1 :project: tables ---- .. cpp:namespace:: @ex_tables_aligned A Markdown syntax table with alignment: .. code-block:: rst .. doxygenclass:: Table_2 :project: tables It renders as: ---- .. doxygenclass:: Table_2 :project: tables ---- .. cpp:namespace:: @ex_tables_rowspan A Markdown syntax table with rowspan (and alignment): .. code-block:: rst .. doxygenclass:: Table_3 :project: tables It renders as: ---- .. only:: html .. doxygenclass:: Table_3 :project: tables ---- .. cpp:namespace:: @ex_tables_colspan A Markdown syntax table with colspan (and alignment): .. code-block:: rst .. doxygenclass:: Table_4 :project: tables It renders as: ---- .. only:: html .. doxygenclass:: Table_4 :project: tables ---- .. cpp:namespace:: @ex_tables_doxygen A Doxygen syntax table: .. code-block:: rst .. doxygenclass:: Table_5 :project: tables It renders as: ---- .. only:: html .. doxygenclass:: Table_5 :project: tables ================================================ FILE: documentation/source/template.rst ================================================ Template ======== .. cpp:namespace:: @ex_template_first Breathe has support for class and function templates. They are output as follows. For a class with a single template parameter: .. code-block:: rst .. doxygenclass:: templateclass :project: template_class :members: It renders as: ---- .. doxygenclass:: templateclass :project: template_class :members: ---- .. cpp:namespace:: @ex_template_multiple With multiple template parameters it renders as: ---- .. doxygenclass:: anothertemplateclass :project: template_class_non_type :members: ---- .. cpp:namespace:: @ex_template_function_single A function with single template parameter renders as: ---- .. doxygenfunction:: function1 :project: template_function ---- .. cpp:namespace:: @ex_template_function_single_specialization If specialized for a given type it renders as: ---- .. doxygenfunction:: function1< std::string > :project: template_function ---- .. cpp:namespace:: @ex_template_function_multiple With multiple template parameters it renders as: ---- .. doxygenfunction:: function2 :project: template_function ================================================ FILE: documentation/source/testpages.rst ================================================ Test Pages ========== .. toctree:: :maxdepth: 1 doxygen tinyxml specific embeddedrst inline members ================================================ FILE: documentation/source/tinyxml.rst ================================================ TinyXML Test Suite ================== .. cpp:namespace:: @ex_tinyxml .. doxygenindex:: ================================================ FILE: documentation/source/typedef.rst ================================================ .. _typedef-example: doxygentypedef Directive Example ================================ Working Example --------------- .. cpp:namespace:: @ex_typedef_example This should work: .. code-block:: rst .. doxygentypedef:: UINT32 :project: structcmd It produces this output: .. doxygentypedef:: UINT32 :project: structcmd Example with Namespace ---------------------- .. cpp:namespace:: @ex_typedef_namespace This should work: .. code-block:: rst .. doxygentypedef:: foo::ns::MyInt :project: namespace It produces this output: .. doxygentypedef:: foo::ns::MyInt :project: namespace Failing Example --------------- .. cpp:namespace:: @ex_typedef_failing This intentionally fails: .. code-block:: rst .. doxygentypedef:: made_up_typedef :project: restypedef It produces the following warning message: .. warning:: doxygentypedef: Cannot find typedef "made_up_typedef" in doxygen xml output ================================================ FILE: documentation/source/union.rst ================================================ .. _union-example: doxygenunion Directive Example ============================== Working Example --------------- .. cpp:namespace:: @ex_union_example This should work: .. code-block:: rst .. doxygenunion:: SeparateUnion :project: union It produces this output: .. doxygenunion:: SeparateUnion :project: union Example with Namespace ---------------------- .. cpp:namespace:: @ex_union_namespace This should work: .. code-block:: rst .. doxygenunion:: foo::MyUnion :project: union It produces this output: .. doxygenunion:: foo::MyUnion :project: union Failing Example --------------- .. cpp:namespace:: @ex_union_failing This intentionally fails: .. code-block:: rst .. doxygenunion:: made_up_union :project: union It produces the following warning message: .. warning:: doxygenunion: Cannot find union "made_up_union" in doxygen XML output for project "union" from directory: ../../examples/specific/union/xml/ ================================================ FILE: documentation/source/variable.rst ================================================ .. _variable-example: doxygenvariable Directive Example ================================= Working Example --------------- .. cpp:namespace:: @ex_variable_example This should work: .. code-block:: rst .. doxygenvariable:: global_cache_tree :project: c_file It produces this output: .. doxygenvariable:: global_cache_tree :project: c_file Failing Example --------------- .. cpp:namespace:: @ex_variable_failing This intentionally fails: .. code-block:: rst .. doxygenvariable:: made_up_variable :project: define It produces the following warning message: .. warning:: doxygenvariable: Cannot find variable “made_up_variable” in doxygen XML output for project "tinyxml" from directory: ../../examples/tinyxml/tinyxml/xml/ ================================================ FILE: examples/doxygen/.gitignore ================================================ afterdoc author autolink class concept define diagrams docstring enum example file func group include jdstyle manual memgrp overload page par parblock pyexample qtstyle relates restypedef structcmd tag template interface ================================================ FILE: examples/doxygen/Makefile ================================================ # # This file was generated from Makefile.in on Sat Dec 13 12:17:28 GMT 2008 # DOXYGEN ?= `which doxygen` TMAKEPATH = ENV = env TMAKEPATH=$(TMAKEPATH) TMAKE = MAKE = /usr/bin/make PERL = /usr/bin/perl RM = rm -f CP = cp VERSION = 1.5.7.1 INSTALL = /tmp INSTTOOL = /usr/bin/install DOXYDOCS = .. DOCDIR = $(INSTALL)/share/doc/packages/doxygen QTDIR = HAVE_DOT = /usr/bin/dot all: class/xml/index.xml \ concept/xml/index.xml \ define/xml/index.xml \ enum/xml/index.xml \ file/xml/index.xml \ func/xml/index.xml \ page/xml/index.xml \ relates/xml/index.xml \ author/xml/index.xml \ par/xml/index.xml \ parblock/xml/index.xml \ overload/xml/index.xml \ example/xml/index.xml \ include/xml/index.xml \ qtstyle/xml/index.xml \ jdstyle/xml/index.xml \ structcmd/xml/index.xml \ autolink/xml/index.xml \ restypedef/xml/index.xml \ afterdoc/xml/index.xml \ template/xml/index.xml \ tag/xml/index.xml \ group/xml/index.xml \ diagrams/xml/index.xml \ memgrp/xml/index.xml \ docstring/xml/index.xml \ pyexample/xml/index.xml \ manual/xml/index.xml \ interface/xml/index.xml clean: rm -rf class concept define enum file func page relates author \ par parblock overload example include qtstyle jdstyle structcmd \ autolink tag restypedef afterdoc template tag group diagrams \ memgrp docstring pyexample manual interface class/xml/index.xml: class.h class.cfg $(DOXYGEN) class.cfg concept/xml/index.xml: concept.h concept.cfg $(DOXYGEN) concept.cfg define/xml/index.xml: define.h define.cfg $(DOXYGEN) define.cfg enum/xml/index.xml: enum.h enum.cfg $(DOXYGEN) enum.cfg file/xml/index.xml: file.h file.cfg $(DOXYGEN) file.cfg func/xml/index.xml: func.h func.cfg $(DOXYGEN) func.cfg page/xml/index.xml: page.doc page.cfg $(DOXYGEN) page.cfg relates/xml/index.xml: relates.cpp relates.cfg $(DOXYGEN) relates.cfg author/xml/index.xml: author.cpp author.cfg $(DOXYGEN) author.cfg par/xml/index.xml: par.cpp par.cfg $(DOXYGEN) par.cfg parblock/xml/index.xml: parblock.cpp parblock.cfg $(DOXYGEN) parblock.cfg overload/xml/index.xml: overload.cpp overload.cfg $(DOXYGEN) overload.cfg example/xml/index.xml: example.cpp example_test.cpp example.cfg $(DOXYGEN) example.cfg include/xml/index.xml: include.cpp example_test.cpp include.cfg $(DOXYGEN) include.cfg qtstyle/xml/index.xml: qtstyle.cpp qtstyle.cfg $(DOXYGEN) qtstyle.cfg jdstyle/xml/index.xml: jdstyle.cpp jdstyle.cfg $(DOXYGEN) jdstyle.cfg structcmd/xml/index.xml: structcmd.h structcmd.cfg $(DOXYGEN) structcmd.cfg autolink/xml/index.xml: autolink.cpp autolink.cfg $(DOXYGEN) autolink.cfg tag/xml/index.xml: tag.cpp tag.cfg example/xml/index.xml $(DOXYGEN) tag.cfg # sed -e "1,1s#perl#$(PERL)#g" tag/xml/installdox >tag/xml/installdox.perl # cd tag/xml ; $(PERL) installdox.perl -lexample.tag@../../example/xml restypedef/xml/index.xml: restypedef.cpp restypedef.cfg $(DOXYGEN) restypedef.cfg afterdoc/xml/index.xml: afterdoc.h afterdoc.cfg $(DOXYGEN) afterdoc.cfg template/xml/index.xml: templ.cpp templ.cfg $(DOXYGEN) templ.cfg group/xml/index.xml: group.cpp group.cfg $(DOXYGEN) group.cfg memgrp/xml/index.xml: memgrp.cpp memgrp.cfg $(DOXYGEN) memgrp.cfg pyexample/xml/index.xml: pyexample.py pyexample.cfg $(DOXYGEN) pyexample.cfg manual/xml/index.xml: manual.c manual.cfg $(DOXYGEN) manual.cfg docstring/xml/index.xml: docstring.py docstring.cfg $(DOXYGEN) docstring.cfg interface/xml/index.xml: interface.h interface.cfg $(DOXYGEN) interface.cfg diagrams/xml/index.xml: diagrams_a.h diagrams_b.h diagrams_c.h diagrams_d.h diagrams_e.h diagrams.cfg ifneq ($(HAVE_DOT),) $(DOXYGEN) diagrams.cfg endif ================================================ FILE: examples/doxygen/afterdoc.cfg ================================================ PROJECT_NAME = "AfterDocs" OUTPUT_DIRECTORY = afterdoc GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = afterdoc.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/doxygen/afterdoc.h ================================================ /*! A test class */ class Test1 { public: /** An enum type. * The documentation block cannot be put after the enum! */ enum EnumType { int EVal1, /**< enum value 1 */ int EVal2 /**< enum value 2 */ }; void member(); //!< a member function. protected: int value; /*!< an integer value */ }; ================================================ FILE: examples/doxygen/author.cfg ================================================ PROJECT_NAME = "Author Command" OUTPUT_DIRECTORY = author GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = author.cpp QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/doxygen/author.cpp ================================================ /*! \class WindowsNT * \brief Windows Nice Try. * \author Bill Gates * \author Several species of small furry animals gathered together * in a cave and grooving with a picture. * \version 4.0 * \date 1996-1998 * \bug It crashes a lot and requires huge amounts of memory. * \bug The class introduces the more bugs, the longer it is used. * \warning This class may explode in your face. * \warning If you inherit anything from this class, you're doomed. */ class WindowsNT {}; ================================================ FILE: examples/doxygen/autolink.cfg ================================================ PROJECT_NAME = "Automatic link generation" OUTPUT_DIRECTORY = autolink GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = autolink.cpp QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/doxygen/autolink.cpp ================================================ /*! \file autolink.cpp Testing automatic link generation. A link to a member of the Test2 class: Test2::member, More specific links to the each of the overloaded members: Test2::member(int) and Test2#member(int,int) A link to a protected member variable of Test2: Test2#var, A link to the global variable #globVar. A link to the global enumeration type #GlobEnum. A link to the define #ABS_NUMBER(x). A link to the destructor of the Test2 class: Test2::~Test2, A link to the typedef ::Test2TypeDef. A link to the enumeration type Test2::EType A link to some enumeration values Test2::Val1 and ::GVal2 */ /*! Since this documentation block belongs to the class Test2 no link to Test2 is generated. Two ways to link to a constructor are: #Test2 and Test2(). Links to the destructor are: #~Test2 and ~Test2(). A link to a member in this class: member(). More specific links to the each of the overloaded members: member(int) and member(int,int). A link to the variable #var. A link to the global typedef ::Test2TypeDef. A link to the global enumeration type #GlobEnum. A link to the define ABS_NUMBER(x). A link to a variable \link #var using another text\endlink as a link. A link to the enumeration type #EType. A link to some enumeration values: \link Test2::Val1 Val1 \endlink and ::GVal1. And last but not least a link to a file: autolink.cpp. \sa Inside a see also section any word is checked, so EType, Val1, GVal1, ~Test2 and member will be replaced by links in HTML. */ class Test2 { public: Test2(); //!< constructor ~Test2(); //!< destructor void member(int); /**< A member function. Details. */ void member(int,int); /**< An overloaded member function. Details */ /** An enum type. More details */ enum EType { Val1, /**< enum value 1 */ Val2 /**< enum value 2 */ }; protected: int var; /**< A member variable */ }; /*! details. */ Test2::Test2() { } /*! details. */ Test2::~Test2() { } /*! A global variable. */ int globVar; /*! A global enum. */ enum GlobEnum { GVal1, /*!< global enum value 1 */ GVal2 /*!< global enum value 2 */ }; /*! * A macro definition. */ #define ABS_NUMBER(x) (((x)>0)?(x):-(x)) typedef Test2 Test2TypeDef; /*! \fn typedef Test2 Test2TypeDef * A type definition. */ ================================================ FILE: examples/doxygen/class.cfg ================================================ PROJECT_NAME = "Class Command" OUTPUT_DIRECTORY = class GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = class.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/doxygen/class.h ================================================ /* A dummy class */ class Test3 { }; /*! \class Test class.h "inc/class.h" * \brief This is a test class. * * Some details about the Test class */ ================================================ FILE: examples/doxygen/concept.cfg ================================================ PROJECT_NAME = "Concept Command" OUTPUT_DIRECTORY = concept GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = concept.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/doxygen/concept.h ================================================ template concept Hashable = requires(T a) { { std::hash{}(a) } -> std::convertible_to; }; /*! \concept Hashable concept.h "inc/concept.h" * \brief This is a test concept. * * It was stolen from the first example from * https://en.cppreference.com/w/cpp/language/constraints */ ================================================ FILE: examples/doxygen/define.cfg ================================================ PROJECT_NAME = "Define Command" OUTPUT_DIRECTORY = define GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = define.h ENABLE_PREPROCESSING = YES QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/doxygen/define.h ================================================ /*! \file define.h \brief testing defines This is to test the documentation of defines. */ /*! \def MAX(x,y) Computes the maximum of \a x and \a y. */ /*! Computes the absolute value of its argument \a x. */ #define ABS(x) (((x)>0)?(x):-(x)) #define MAX(x,y) ((x)>(y)?(x):(y)) #define MIN(x,y) ((x)>(y)?(y):(x)) /*!< Computes the minimum of \a x and \a y. */ #define NOARGS (void*) /*!< Define with no arguments */ ================================================ FILE: examples/doxygen/diagrams.cfg ================================================ PROJECT_NAME = "Diagrams" OUTPUT_DIRECTORY = diagrams HAVE_DOT = YES EXTRACT_ALL = YES GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO ENABLE_PREPROCESSING = YES INPUT = . FILE_PATTERNS = diagrams_*.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/doxygen/diagrams_a.h ================================================ #ifndef _DIAGRAMS_A_H #define _DIAGRAMS_A_H class A { public: A *m_self; }; #endif ================================================ FILE: examples/doxygen/diagrams_b.h ================================================ #ifndef _DIAGRAMS_B_H #define _DIAGRAMS_B_H class A; class B { public: A *m_a; }; #endif ================================================ FILE: examples/doxygen/diagrams_c.h ================================================ #ifndef _DIAGRAMS_C_H #define _DIAGRAMS_C_H #include "diagrams_c.h" class D; class C : public A { public: D *m_d; }; #endif ================================================ FILE: examples/doxygen/diagrams_d.h ================================================ #ifndef _DIAGRAM_D_H #define _DIAGRAM_D_H #include "diagrams_a.h" #include "diagrams_b.h" class C; class D : virtual protected A, private B { public: C m_c; }; #endif ================================================ FILE: examples/doxygen/diagrams_e.h ================================================ #ifndef _DIAGRAM_E_H #define _DIAGRAM_E_H #include "diagrams_d.h" class E : public D {}; #endif ================================================ FILE: examples/doxygen/docstring.cfg ================================================ PROJECT_NAME = "Python" OUTPUT_DIRECTORY = docstring EXTRACT_ALL = YES GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO OPTIMIZE_OUTPUT_JAVA = YES INPUT = docstring.py QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/doxygen/docstring.py ================================================ """@package docstring Documentation for this module. More details. """ from __future__ import annotations def func(): """Documentation for a function. More details. """ pass class PyClass: """Documentation for a class. More details. """ def __init__(self): """The constructor.""" self._memVar = 0 def PyMethod(self): """Documentation for a method.""" pass ================================================ FILE: examples/doxygen/enum.cfg ================================================ PROJECT_NAME = "Enum Command" OUTPUT_DIRECTORY = enum GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = enum.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/doxygen/enum.h ================================================ class Test4 { public: enum TEnum { Val1, Val2 }; /*! Another enum, with inline docs */ enum AnotherEnum { V1, /*!< value 1 */ V2 /*!< value 2 */ }; /*! Another enum, with inline docs (using an explicit datatype) */ enum class AnotherScopedEnum : long { V1, /*!< value 1 */ V2 /*!< value 2 */ }; }; /*! \class Test4 * The class description. */ /*! \enum Test4::TEnum * A description of the enum type. */ /*! \var Test4::TEnum Test4::Val1 * The description of the first enum value. */ ================================================ FILE: examples/doxygen/example.cfg ================================================ PROJECT_NAME = "Example Command" OUTPUT_DIRECTORY = example GENERATE_TAGFILE = example.tag GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = example.cpp EXAMPLE_PATH = example_test.cpp QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/doxygen/example.cpp ================================================ /** A Test5 class. * More details about this class. */ class Test5 { public: /** An example member function. * More details about this function. */ void example(); }; void Test5::example() {} /** \example example_test.cpp * This is an example of how to use the Test5 class. * More details about this example. */ ================================================ FILE: examples/doxygen/example_test.cpp ================================================ void main() { Test7 t; t.example(); } ================================================ FILE: examples/doxygen/file.cfg ================================================ PROJECT_NAME = "File Command" OUTPUT_DIRECTORY = file GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = file.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/doxygen/file.h ================================================ /** \file file.h * A brief file description. * A more elaborated file description. */ /** * A global integer value. * More details about this value. */ extern int globalValue; ================================================ FILE: examples/doxygen/func.cfg ================================================ PROJECT_NAME = "Fn Command" OUTPUT_DIRECTORY = func GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = func.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/doxygen/func.h ================================================ class Test6 { public: const char *member(char,int) throw(std::out_of_range); }; const char *Test6::member(char c,int n) throw(std::out_of_range) {} /*! \class Test6 * \brief Test6 class. * * Details about Test6. */ /*! \fn const char *Test6::member(char c,int n) * \brief A member function. * \param c a character. * \param n an integer. * \exception std::out_of_range parameter is out of range. * \return a character pointer. */ /*! \brief Doing important things with parameter directions * * \param[out] a output * \param[in] b input * \param[in, out] c input but gets rewritten */ void myFunc(int * a, int * b, int * c); ================================================ FILE: examples/doxygen/group.cfg ================================================ PROJECT_NAME = "Grouping" OUTPUT_DIRECTORY = group GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = group.cpp QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/doxygen/group.cpp ================================================ /** @defgroup group1 The First Group * This is the first group * @{ */ /** @brief class C1 in group 1 */ class C1 {}; /** @brief class C2 in group 1 */ class C2 {}; /** function in group 1 */ void func1() {} /** @} */ // end of group1 /** * @defgroup group2 The Second Group * This is the second group */ /** @defgroup group3 The Third Group * This is the third group */ /** @defgroup group4 The Fourth Group * @ingroup group3 * Group 4 is a subgroup of group 3 */ /** * @ingroup group2 * @brief class C3 in group 2 */ class C3 {}; /** @ingroup group2 * @brief class C4 in group 2 */ class C4 {}; /** @ingroup group3 * @brief class C5 in @link group3 the third group@endlink. */ class C5 {}; /** @ingroup group1 group2 group3 group4 * namespace N1 is in four groups * @sa @link group1 The first group@endlink, group2, group3, group4 * * Also see @ref mypage2 */ namespace N1 {}; /** @file * @ingroup group3 * @brief this file in group 3 */ /** @defgroup group5 The Fifth Group * This is the fifth group * @{ */ /** @page mypage1 This is a section in group 5 * Text of the first section */ /** @page mypage2 This is another section in group 5 * Text of the second section */ /** @} */ // end of group5 /** @addtogroup group1 * * More documentation for the first group. * @{ */ /** another function in group 1 */ void func2() {} /** yet another function in group 1 */ void func3() {} /** @} */ // end of group1 ================================================ FILE: examples/doxygen/include.cfg ================================================ PROJECT_NAME = "Include Command" OUTPUT_DIRECTORY = include GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = include.cpp EXAMPLE_PATH = example_test.cpp QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/doxygen/include.cpp ================================================ /*! A test class. */ class Test7 { public: /// a member function void example(); }; /*! \page example * \dontinclude example_test.cpp * Our main function starts like this: * \skip main * \until { * First we create a object \c t of the Test7 class. * \skipline Test7 * Then we call the example member function * \line example * After that our little test routine ends. * \line } */ ================================================ FILE: examples/doxygen/interface.cfg ================================================ PROJECT_NAME = "Interface Command" OUTPUT_DIRECTORY = interface GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = interface.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/doxygen/interface.h ================================================ /* A dummy interface, which is really just a specially treated class in C++ */ class TestInterface { }; /*! \interface TestInterface interface.h "inc/interface.h" * \brief This is a test interface. * * Some details about the TestInterface interface */ ================================================ FILE: examples/doxygen/jdstyle.cfg ================================================ PROJECT_NAME = "JavaDoc Style" OUTPUT_DIRECTORY = jdstyle GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = jdstyle.cpp QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/doxygen/jdstyle.cpp ================================================ /** * A test class. A more elaborate class description. */ class Test8 { public: /** * An enum. * More detailed enum description. */ enum TEnum { TVal1, /**< enum value TVal1. */ TVal2, /**< enum value TVal2. */ TVal3 /**< enum value TVal3. */ } *enumPtr, /**< enum pointer. Details. */ enumVar; /**< enum variable. Details. */ /** * A constructor. * A more elaborate description of the constructor. */ Test8(); /** * A destructor. * A more elaborate description of the destructor. */ ~Test8(); /** * a normal member taking two arguments and returning an integer value. * @param a an integer argument. * @param s a constant character pointer. * @see Test8() * @see ~Test8() * @see testMeToo() * @see publicVar() * @return The test results */ int testMe(int a,const char *s); /** * A pure virtual member. * @see testMe() * @param c1 the first argument. * @param c2 the second argument. */ virtual void testMeToo(char c1,char c2) = 0; /** * a public variable. * Details. */ int publicVar; /** * a function variable. * Details. */ int (*handler)(int a,int b); }; ================================================ FILE: examples/doxygen/make.bat ================================================ @ECHO OFF set DOXYGEN=doxygen for /f "delims=" %%i in ('where doxygen') do set DOXYGEN=%%i set PERL=perl for /f "delims=" %%i in ('where perl') do set PERL=%%i set HAVE_DOT=dot for /f "delims=" %%i in ('where dot') do set HAVE_DOT=%%i @REM echo DOXYGEN : %DOXYGEN% @REM echo PERL : %PERL% @REM echo HAVE_DOT : %HAVE_DOT% if "%1" == "" ( call :all goto end ) if "%1" == "all" ( call :all goto end ) if "%1" == "clean" ( call :clean goto end ) goto end :all call :doxygen class.cfg call :doxygen concept.cfg call :doxygen define.cfg call :doxygen enum.cfg call :doxygen file.cfg call :doxygen func.cfg call :doxygen page.cfg call :doxygen relates.cfg call :doxygen author.cfg call :doxygen par.cfg call :doxygen parblock.cfg call :doxygen overload.cfg call :doxygen example.cfg call :doxygen include.cfg call :doxygen qtstyle.cfg call :doxygen jdstyle.cfg call :doxygen structcmd.cfg call :doxygen autolink.cfg call :doxygen restypedef.cfg call :doxygen afterdoc.cfg call :doxygen templ.cfg call :doxygen tag.cfg call :doxygen group.cfg call :doxygen diagrams.cfg call :doxygen memgrp.cfg call :doxygen docstring.cfg call :doxygen pyexample.cfg call :doxygen manual.cfg call :doxygen interface.cfg goto end :clean call :rmdir class call :rmdir concept call :rmdir define call :rmdir enum call :rmdir file call :rmdir func call :rmdir page call :rmdir relates call :rmdir author call :rmdir par call :rmdir parblock call :rmdir overload call :rmdir example call :rmdir include call :rmdir qtstyle call :rmdir jdstyle call :rmdir structcmd call :rmdir autolink call :rmdir restypedef call :rmdir afterdoc call :rmdir template call :rmdir tag call :rmdir group call :rmdir diagrams call :rmdir memgrp call :rmdir docstring call :rmdir pyexample call :rmdir manual call :rmdir interface goto end :doxygen set CFG=%~1 echo Running doxygen: %CFG% "%DOXYGEN%" %CFG% goto end :rmdir set DIR=%~1 if exist "%DIR%" ( echo Removing directory: %DIR% rmdir /s/q "%DIR%" ) goto end :end ================================================ FILE: examples/doxygen/manual.c ================================================ /** * \file manual.c */ typedef struct Object Object; //!< Object type typedef struct Vehicle Vehicle; //!< Vehicle type typedef struct Car Car; //!< Car type typedef struct Truck Truck; //!< Truck type /*! * Base object class. */ struct Object { int ref; //!< \private Reference count. }; /*! * Increments object reference count by one. * \public \memberof Object */ static Object * objRef(Object *obj); /*! * Decrements object reference count by one. * \public \memberof Object */ static Object * objUnref(Object *obj); /*! * Vehicle class. * \extends Object */ struct Vehicle { Object base; //!< \protected Base class. }; /*! * Starts the vehicle. * \public \memberof Vehicle */ void vehicleStart(Vehicle *obj); /*! * Stops the vehicle. * \public \memberof Vehicle */ void vehicleStop(Vehicle *obj); /*! * Car class. * \extends Vehicle */ struct Car { Vehicle base; //!< \protected Base class. }; /*! * Truck class. * \extends Vehicle */ struct Truck { Vehicle base; //!< \protected Base class. }; /*! * Main function. * * Ref vehicleStart(), objRef(), objUnref(). */ int main(void) { Car c; vehicleStart((Vehicle*) &c); } ================================================ FILE: examples/doxygen/manual.cfg ================================================ PROJECT_NAME = "Manual inheritance and membership" OUTPUT_DIRECTORY = manual GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = manual.c QUIET = YES JAVADOC_AUTOBRIEF = YES EXTRACT_PRIVATE = YES EXTRACT_STATIC = YES TYPEDEF_HIDES_STRUCT = YES INLINE_SOURCES = YES REFERENCED_BY_RELATION = YES REFERENCES_RELATION = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/doxygen/memgrp.cfg ================================================ PROJECT_NAME = "Member Grouping" OUTPUT_DIRECTORY = memgrp GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = memgrp.cpp QUIET = YES DISTRIBUTE_GROUP_DOC = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES WARN_IF_UNDOCUMENTED = NO ================================================ FILE: examples/doxygen/memgrp.cpp ================================================ /** A class. Details */ class Test9 { public: //@{ /** Same documentation for both members. Details */ void func1InGroup1(); void func2InGroup1(); //@} /** Function without group. Details. */ void ungroupedFunction(); // intentionally not documenting this member? void func1InGroup2(); protected: void func2InGroup2(); }; void Test9::func1InGroup1() {} void Test9::func2InGroup1() {} /** @name Group2 * Description of group 2. */ //@{ /** Function 2 in group 2. Details. */ void Test9::func2InGroup2() {} /** Function 1 in group 2. Details. */ void Test9::func1InGroup2() {} //@} /*! \file * docs for this file */ //@{ //! one description for all members of this group //! (because DISTRIBUTE_GROUP_DOC is YES in the config file) #define A 1 #define B 2 void glob_func(); //@} ================================================ FILE: examples/doxygen/overload.cfg ================================================ PROJECT_NAME = "Overloaded Command" OUTPUT_DIRECTORY = overload GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO SORT_MEMBER_DOCS = NO INPUT = overload.cpp QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/doxygen/overload.cpp ================================================ class Test10 { public: void drawRect(int,int,int,int); void drawRect(const Rect &r); }; void Test10::drawRect(int x,int y,int w,int h) {} void Test10::drawRect(const Rect &r) {} /*! \class Test10 * \brief A short description. * * More text. */ /*! \fn void Test10::drawRect(int x,int y,int w,int h) * This command draws a rectangle with a left upper corner at ( \a x , \a y ), * width \a w and height \a h. */ /*! * \overload void Test10::drawRect(const Rect &r) */ ================================================ FILE: examples/doxygen/page.cfg ================================================ PROJECT_NAME = "Page Command" OUTPUT_DIRECTORY = page GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = page.doc QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/doxygen/page.doc ================================================ /*! \page page1 A documentation page Leading text. \section sec An example section This page contains the subsections \ref subsection1 and \ref subsection2. For more info see page \ref page2. \subsection subsection1 The first subsection Text. \subsection subsection2 The second subsection More text. */ /*! \page page2 Another page Even more info. */ ================================================ FILE: examples/doxygen/par.cfg ================================================ PROJECT_NAME = "Par Command" OUTPUT_DIRECTORY = par GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = par.cpp QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/doxygen/par.cpp ================================================ /*! \class Test11 * Normal text. * * \par User defined paragraph: * Contents of the paragraph. * * \par * New paragraph under the same heading. * * \note * This note consists of two paragraphs. * This is the first paragraph. * * \par * And this is the second paragraph. * * More normal text. */ class Test11 {}; ================================================ FILE: examples/doxygen/parblock.cfg ================================================ PROJECT_NAME = "Parblock Command" OUTPUT_DIRECTORY = parblock GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = parblock.cpp QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/doxygen/parblock.cpp ================================================ /*! \class Test15 * Normal text. * * \par A paragraph followed by a paragraph block: * \parblock * Contents of the first paragraph in the block. * * New paragraph under the same heading. * \endparblock * * \note * This note consists of three paragraphs in a block. * \parblock * This is the first paragraph in the block. * * And this is the second paragraph in the block. * * And still a third paragraph in the block. * \endparblock * * More normal text. */ class Test15 {}; ================================================ FILE: examples/doxygen/pyexample.cfg ================================================ PROJECT_NAME = "Python" OUTPUT_DIRECTORY = pyexample GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO OPTIMIZE_OUTPUT_JAVA = YES INPUT = pyexample.py QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/doxygen/pyexample.py ================================================ ## @package pyexample # Documentation for this module. # # More details. ## Documentation for a function. # # More details. from __future__ import annotations def func(): pass ## Documentation for a class. # # More details. class PyClass: ## The constructor. def __init__(self): self._memVar = 0 ## Documentation for a method. # @param self The object pointer. def PyMethod(self): pass ## A class variable. classVar = 0 ## @var _memVar # a member variable ================================================ FILE: examples/doxygen/qtstyle.cfg ================================================ PROJECT_NAME = "Qt Style" OUTPUT_DIRECTORY = qtstyle GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = qtstyle.cpp QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/doxygen/qtstyle.cpp ================================================ //! A test class. /*! A more elaborate class description. */ class Test12 { public: //! An enum. /*! More detailed enum description. */ enum TEnum { TVal1, /*!< Enum value TVal1. */ TVal2, /*!< Enum value TVal2. */ TVal3 /*!< Enum value TVal3. */ } //! Enum pointer. /*! Details. */ *enumPtr, //! Enum variable. /*! Details. */ enumVar; //! A constructor. /*! A more elaborate description of the constructor. */ Test12(); //! A destructor. /*! A more elaborate description of the destructor. */ ~Test12(); //! A normal member taking two arguments and returning an integer value. /*! \param a an integer argument. \param s a constant character pointer. \return The test results \sa Test12(), ~Test12(), testMeToo() and publicVar() */ int testMe(int a,const char *s); //! A pure virtual member. /*! \sa testMe() \param c1 the first argument. \param c2 the second argument. */ virtual void testMeToo(char c1,char c2) = 0; //! A public variable. /*! Details. */ int publicVar; //! A function variable. /*! Details. */ int (*handler)(int a,int b); }; ================================================ FILE: examples/doxygen/relates.cfg ================================================ PROJECT_NAME = "Relates Command" OUTPUT_DIRECTORY = relates GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = relates.cpp QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/doxygen/relates.cpp ================================================ /*! * A String class. */ class String { friend int strcmp(const String &,const String &); }; /*! * Compares two strings. */ int strcmp(const String &s1,const String &s2) { } /*! \relates String * A string debug function. */ void stringDebug() { } ================================================ FILE: examples/doxygen/restypedef.cfg ================================================ PROJECT_NAME = "Resolving Typedefs" OUTPUT_DIRECTORY = restypedef GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = restypedef.cpp QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/doxygen/restypedef.cpp ================================================ /*! \file restypedef.cpp * An example of resolving typedefs. */ /*! \struct CoordStruct * A coordinate pair. */ struct CoordStruct { /*! The x coordinate */ float x; /*! The y coordinate */ float y; }; /*! Creates a type name for CoordStruct */ typedef CoordStruct Coord; /*! * This function returns the addition of \a c1 and \a c2, i.e: * (c1.x+c2.x,c1.y+c2.y) */ Coord add(Coord c1,Coord c2) { } ================================================ FILE: examples/doxygen/structcmd.cfg ================================================ PROJECT_NAME = "Structural commands" OUTPUT_DIRECTORY = structcmd GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = structcmd.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/doxygen/structcmd.h ================================================ /*! \file structcmd.h \brief A Documented file. Details. */ /*! \def MAX_NUMBER(a,b) \brief A macro that returns the maximum of \a a and \a b. Details. */ /*! \var typedef unsigned int UINT32 \brief A type definition for a . Details. */ /*! \var int errno \brief Contains the last error code. \warning Not thread safe! */ /*! \fn int open(const char *pathname,int flags) \brief Opens a file descriptor. Detailed description. \param pathname The name of the descriptor. \param flags Opening flags. */ /*! \fn int close(int fd) \brief Closes the file descriptor \a fd. \param fd The descriptor to close. */ /*! \fn size_t write(int fd,const char *buf, size_t count) \brief Writes \a count bytes from \a buf to the file descriptor \a fd. \param fd The descriptor to write to. \param buf The data buffer to write. \param count The number of bytes to write. */ /*! \fn int read(int fd,char *buf,size_t count) \brief Read bytes from a file descriptor. \param fd The descriptor to read from. \param buf The buffer to read into. \param count The number of bytes to read. */ #define MAX_NUMBER(a,b) (((a)>(b))?(a):(b)) typedef unsigned int UINT32; int errno; int open(const char *,int); int close(int); size_t write(int,const char *, size_t); int read(int,char *,size_t); ================================================ FILE: examples/doxygen/tag.cfg ================================================ PROJECT_NAME = "Tag Files" OUTPUT_DIRECTORY = tag GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = tag.cpp TAGFILES = example.tag=../../example/html QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/doxygen/tag.cpp ================================================ /*! A class that is inherited from the external class Test13. */ class Tag : public Test13 { public: /*! an overloaded member. */ void example(); }; ================================================ FILE: examples/doxygen/templ.cfg ================================================ PROJECT_NAME = "Template Test" OUTPUT_DIRECTORY = template GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = templ.cpp QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/doxygen/templ.cpp ================================================ /*! A template class */ template class Test14 { public: Test14(); Test14(const Test14 &); }; /*! complete specialization */ template<> class Test14 { public: Test14(); }; /*! A partial template specialization */ template class Test14 : public Test14 { public: Test14(); }; /*! The constructor of the template class*/ template Test14::Test14() {} /*! The copy constructor */ template Test14::Test14(const Test14 &t) {} /*! The constructor of the partial specialization */ template Test14::Test14() {} /*! The constructor of the specialization */ template<> Test14::Test14() {} ================================================ FILE: examples/specific/.gitignore ================================================ /functypedef /alias /class /inline /nutshell /rst /typedef /members /image /decl_impl */doxygen_sqlite3.db */xml/*.xml */xml/*.xsd */xml/*.xslt ================================================ FILE: examples/specific/Makefile ================================================ # # This file was generated from Makefile.in on Sat Dec 13 12:17:28 GMT 2008 # DOXYGEN ?= `which doxygen` TMAKEPATH = ENV = env TMAKEPATH=$(TMAKEPATH) TMAKE = MAKE = /usr/bin/make PERL = /usr/bin/perl RM = rm -f CP = cp VERSION = 1.5.7.1 INSTALL = /tmp INSTTOOL = /usr/bin/install DOXYDOCS = .. DOCDIR = $(INSTALL)/share/doc/packages/doxygen QTDIR = HAVE_DOT = /usr/bin/dot projects = nutshell alias rst inline namespacefile array inheritance \ members userdefined fixedwidthfont latexmath functionOverload \ image name union group struct struct_function qtsignalsandslots lists \ headings links parameters template_class template_class_non_type \ template_function template_type_alias template_specialisation \ enum define interface xrefsect tables \ cpp_anon cpp_concept cpp_enum cpp_union cpp_function cpp_friendclass \ cpp_inherited_members cpp_trailing_return_type cpp_constexpr_hax \ cpp_function_lookup cpp_ns_template_specialization \ c_file c_struct c_enum c_typedef c_macro c_union membergroups \ simplesect code_blocks dot_graphs special = programlisting decl_impl multifilexml auto class typedef xmls := $(foreach project, $(projects), $(project)/xml/index.xml) special_xmls = $(foreach project, $(special), $(project)/xml/index.xml) all: $(xmls) $(special_xmls) clean: rm -rf $(projects) $(special) # General pattern # --------------- $(xmls): %/xml/index.xml: %.cfg %.h $(DOXYGEN) $< # Special Cases # ------------- programlisting/xml/index.xml: programlisting.h programlisting.cfg programlistinginclude.txt $(DOXYGEN) programlisting.cfg decl_impl/xml/index.xml: decl_impl.h decl_impl.cpp decl_impl.cfg $(DOXYGEN) decl_impl.cfg multifilexml/xml/index.xml: multifile/one/Util.h multifile/two/Util.h multifile.cfg $(DOXYGEN) multifile.cfg auto/xml/index.xml: auto_class.h auto_function.h auto.cfg $(DOXYGEN) auto.cfg class/xml/index.xml: class.h class.cpp class.cfg $(DOXYGEN) class.cfg typedef/xml/index.xml: typedef.h typedef.cfg $(DOXYGEN) typedef.cfg ================================================ FILE: examples/specific/alias.cfg ================================================ PROJECT_NAME = "ALIAS Example" OUTPUT_DIRECTORY = alias GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = alias.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ALIASES = "sideeffect=\par Side Effects^^" ================================================ FILE: examples/specific/alias.h ================================================ /*! @file alias.h */ /** * Foo frob routine. * \par bob this something else * @sideeffect Frobs any foos. * * \par bob this something else * * @sideeffect Frobs any foos. * * @param[out] Frobs any foos. */ void frob_foos(void* Frobs); ================================================ FILE: examples/specific/array.cfg ================================================ PROJECT_NAME = "Array Command" OUTPUT_DIRECTORY = array GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = array.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES OPTIMIZE_OUTPUT_FOR_C = YES ================================================ FILE: examples/specific/array.h ================================================ /** My function */ int foo(int a[5]); /** My other function * * @test This declaration is supposed to be * @code{.c} * int bar(int n, int a[static n]); * @endcode * But, Sphinx fails to recognize `int a[static n])` as a C specific array syntax */ int bar(int n, int a[]); ================================================ FILE: examples/specific/auto.cfg ================================================ PROJECT_NAME = "Auto" OUTPUT_DIRECTORY = auto GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = auto_class.h auto_function.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/specific/auto_class.h ================================================ //! \brief class outside of namespace class AutoClassTest { //! \brief non-namespaced class function void member() {}; //! \brief non-namespaced class other function void anotherMember() {}; }; ================================================ FILE: examples/specific/auto_function.h ================================================ //! \brief non-namespaced class function void autoFunction() {}; //! \brief non-namespaced class other function void anotherAutoFunction() {}; ================================================ FILE: examples/specific/c_enum.cfg ================================================ PROJECT_NAME = "C Enum" OUTPUT_DIRECTORY = c_enum GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = c_enum.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/specific/c_enum.h ================================================ // Example of a enum in C which has different syntax and different support in Sphinx to the C++ enum /** * Backup data. * * \ingroup Backup */ typedef enum { /** * Compatibility with old gboolean used instead of format. * * File type is guessed for extension, non unicode format used * for Gammu backup. */ GSM_Backup_Auto = 0, /** * Compatibility with old gboolean used instead of format. * * File type is guessed for extension, unicode format used * for Gammu backup. */ GSM_Backup_AutoUnicode = 1, /** * LMB format, compatible with Logo manager, can store * phonebooks and logos. */ GSM_Backup_LMB, /** * vCalendar standard, can store todo and calendar entries. */ GSM_Backup_VCalendar, /** * vCard standard, can store phone phonebook entries. */ GSM_Backup_VCard, /** * LDIF (LDAP Data Interchange Format), can store phone * phonebook entries. */ GSM_Backup_LDIF, /** * iCalendar standard, can store todo and calendar entries. */ GSM_Backup_ICS, /** * Gammu own format can store almost anything from phone. * * This is ASCII version of the format, Unicode strings are HEX * encoded. Use GSM_Backup_GammuUCS2 instead if possible. */ GSM_Backup_Gammu, /** * Gammu own format can store almost anything from phone. * * This is UCS2-BE version of the format. */ GSM_Backup_GammuUCS2, /** * vNote standard, can store phone notes. */ GSM_Backup_VNote, } GSM_BackupFormat; ================================================ FILE: examples/specific/c_file.cfg ================================================ PROJECT_NAME = "C File Example" OUTPUT_DIRECTORY = c_file GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = c_file.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES WARN_IF_UNDOCUMENTED = NO ================================================ FILE: examples/specific/c_file.h ================================================ /* Borrowed from git "cache-tree.h" as an example of C code */ #ifndef CACHE_TREE_H #define CACHE_TREE_H #include "tree.h" #include "tree-walk.h" struct cache_tree; struct cache_tree_sub { struct cache_tree *cache_tree; int namelen; int used; char name[FLEX_ARRAY]; }; struct cache_tree { int entry_count; /* negative means "invalid" */ unsigned char sha1[20]; int subtree_nr; int subtree_alloc; struct cache_tree_sub **down; }; /** Shared cache tree instance. */ extern struct cache_tree global_cache_tree; struct cache_tree *cache_tree(void); extern void cache_tree_free(struct cache_tree **); void cache_tree_invalidate_path(struct cache_tree *, const char *); struct cache_tree_sub *cache_tree_sub(struct cache_tree *, const char *); void cache_tree_write(struct strbuf *, struct cache_tree *root); struct cache_tree *cache_tree_read(const char *buffer, unsigned long size); int cache_tree_fully_valid(struct cache_tree *); int cache_tree_update(struct cache_tree *, struct cache_entry **, int, int, int); /** bitmasks to write_cache_as_tree flags */ #define WRITE_TREE_MISSING_OK 1 #define WRITE_TREE_IGNORE_CACHE_TREE 2 /** error return codes */ #define WRITE_TREE_UNREADABLE_INDEX (-1) #define WRITE_TREE_UNMERGED_INDEX (-2) #define WRITE_TREE_PREFIX_ERROR (-3) int write_cache_as_tree(unsigned char *sha1, int flags, const char *prefix); void prime_cache_tree(struct cache_tree **, struct tree *); extern int cache_tree_matches_traversal(struct cache_tree *, struct name_entry *ent, struct traverse_info *info); #endif ================================================ FILE: examples/specific/c_macro.cfg ================================================ PROJECT_NAME = "C Macro" OUTPUT_DIRECTORY = c_macro GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = c_macro.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/specific/c_macro.h ================================================ #define A_C_MACRO "hello world" #define ANOTHER_C_MACRO(name) "hello" name ================================================ FILE: examples/specific/c_struct.cfg ================================================ PROJECT_NAME = "C Struct" OUTPUT_DIRECTORY = c_struct GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = c_struct.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES WARN_IF_UNDOCUMENTED = NO ================================================ FILE: examples/specific/c_struct.h ================================================ struct ACStruct { int i; struct ANestedStruct { int i; }; }; ================================================ FILE: examples/specific/c_typedef.cfg ================================================ PROJECT_NAME = "Function Type Def Command" OUTPUT_DIRECTORY = c_typedef GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = c_typedef.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/specific/c_typedef.h ================================================ /** * Sample typedef for a function pointer */ typedef int (*cTypeDefTestFuncPtr)(void); typedef void* (*cVoidFuncPtr)(float, int); typedef void* cVoidPointer; typedef float* cFloatPointer; typedef float cFloatingPointNumber; /** * @brief Test for a simple C typedef */ typedef int cTestTypedef; ================================================ FILE: examples/specific/c_union.cfg ================================================ PROJECT_NAME = "C Union" OUTPUT_DIRECTORY = c_union GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = c_union.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES WARN_IF_UNDOCUMENTED = NO ================================================ FILE: examples/specific/c_union.h ================================================ union ACUnion { int i; }; ================================================ FILE: examples/specific/class.cfg ================================================ PROJECT_NAME = "Class Command" OUTPUT_DIRECTORY = class GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = class.h class.cpp QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES WARN_IF_UNDOCUMENTED = NO ================================================ FILE: examples/specific/class.cpp ================================================ #include "class.h" /*! More documentation in the impl file */ void ClassTest::function(int myIntParameter) { } /*! More documentation in the impl file */ void ClassTest::anotherFunction() { } ================================================ FILE: examples/specific/class.h ================================================ #include namespace TestNamespaceClasses { //! \brief first class inside of namespace class NamespacedClassTest { public: //! \brief namespaced class function virtual void function() const = 0; static void functionS(); explicit NamespacedClassTest() {} //! \brief namespaced class other function void anotherFunction() {} }; //! \brief second class inside of namespace class ClassTest { public: //! \brief second namespaced class function void function() {} //! \brief second namespaced class other function void anotherFunction() {} }; } // TestNamespaceClasses //! \brief class outside of namespace class OuterClass { public: //! \brief inner class class InnerClass {}; }; //! \brief class outside of namespace class ClassTest { public: /*! \brief non-namespaced class function More details in the header file. */ void function(int myParameter); //! \brief non-namespaced class other function void anotherFunction(); //! \brief namespaced class function virtual void publicFunction() const = 0; virtual void undocumentedPublicFunction() const = 0; //! A public class class PublicClass {}; class UndocumentedPublicClass {}; //! A public struct struct PublicStruct {}; struct UndocumentedPublicStruct {}; protected: //! A protected function void protectedFunction() {} void undocumentedProtectedFunction() {} //! A protected class class ProtectedClass {}; class UndocumentedProtectedClass {}; //! A protected struct struct ProtectedStruct {}; struct UndocumentedProtectedStruct {}; private: //! This is a private function virtual void privateFunction() const = 0; virtual void undocumentedPrivateFunction() const = 0; //! A private class class PrivateClass {}; class UndocumentedPrivateClass {}; //! A private struct struct PrivateStruct {}; struct UndocumentedPrivateStruct {}; }; template void f0(); template<> void f0(); namespace NS1 { template void f1(); template<> void f1(); namespace NS2 { template void f2(); template<> void f2(); } // namespace NS2 } // namespace NS1 ================================================ FILE: examples/specific/code_blocks.cfg ================================================ PROJECT_NAME = "Code Blocks" OUTPUT_DIRECTORY = code_blocks GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = code_blocks.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/specific/code_blocks.h ================================================ /** A function with an unannotated code block with C/C++ code. * * @code * char* buffer = new char[42]; * int charsAdded = sprintf(buffer, "Tabs are normally %d spaces\n", 8); * @endcode */ void with_standard_code_block(); /** A function with an unannotated code block with non-C/C++ code. * * @code * set(user_list A B C) * foreach(element ${user_list}) * message(STATUS "Element is ${element}") * endforeach() * @endcode * * Another code-block that explicitly remains not highlighted. * @code{.unparsed} * Show this as is. * @endcode */ void with_unannotated_cmake_code_block(); /** A function with an annotated cmake code block. * * @code{.cmake} * set(user_list A B C) * foreach(element ${user_list}) * message(STATUS "Element is ${element}") * endforeach() * @endcode */ void with_annotated_cmake_code_block(); ================================================ FILE: examples/specific/cpp_anon.cfg ================================================ PROJECT_NAME = "C++ Anonymous" OUTPUT_DIRECTORY = cpp_anon GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = cpp_anon.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES WARN_IF_UNDOCUMENTED = NO ================================================ FILE: examples/specific/cpp_anon.h ================================================ struct ClassWithAnonEntities { struct { int structMember; }; union { int unionMember; }; enum { Enumerator }; }; ================================================ FILE: examples/specific/cpp_concept.cfg ================================================ PROJECT_NAME = "C++ Concept" OUTPUT_DIRECTORY = cpp_concept GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = cpp_concept.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/specific/cpp_concept.h ================================================ template concept Hashable = requires(T a) { { std::hash{}(a) } -> std::convertible_to; }; ================================================ FILE: examples/specific/cpp_constexpr_hax.cfg ================================================ PROJECT_NAME = "Constexpr Hax" OUTPUT_DIRECTORY = cpp_constexpr_hax GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = cpp_constexpr_hax.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/specific/cpp_constexpr_hax.h ================================================ [[nodiscard]] constexpr static auto f1(std::false_type) {} [[nodiscard]] static constexpr auto f2(std::false_type) {} constexpr static int v1 = 42; static constexpr int v2 = 42; ================================================ FILE: examples/specific/cpp_enum.cfg ================================================ PROJECT_NAME = "C++ Enum" OUTPUT_DIRECTORY = cpp_enum GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = cpp_enum.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/specific/cpp_enum.h ================================================ enum Unscoped : int { UnscopedEnumerator = 42 }; enum struct ScopedStruct : int { Enumerator = 42 }; enum class ScopedClass : int { Enumerator = 42 }; enum class ScopedClassNoUnderlying { Enumerator = 42 }; ================================================ FILE: examples/specific/cpp_friendclass.cfg ================================================ PROJECT_NAME = "C++ Friend Class" OUTPUT_DIRECTORY = cpp_friendclass GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = cpp_friendclass.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES WARN_IF_UNDOCUMENTED = NO ================================================ FILE: examples/specific/cpp_friendclass.h ================================================ struct A {}; struct B {}; struct C { friend class A; friend struct B; }; ================================================ FILE: examples/specific/cpp_function.cfg ================================================ PROJECT_NAME = "C++ Function" OUTPUT_DIRECTORY = cpp_function GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = cpp_function.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES WARN_IF_UNDOCUMENTED = NO ================================================ FILE: examples/specific/cpp_function.h ================================================ struct Foo{}; struct Class { virtual void f1() const volatile & = 0; virtual void f2() const volatile && = 0; static void f3(); void (*f_issue_489)(struct Foo *foo, int value); int f_issue_338() noexcept; }; /** A namespace to demonstrate a namespaced function */ namespace TestNamespaceFunction { /** A function within a namespace. */ void namespaceFunc(); } ================================================ FILE: examples/specific/cpp_function_lookup.cfg ================================================ PROJECT_NAME = "C++ Function Lookup" OUTPUT_DIRECTORY = cpp_function_lookup GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = cpp_function_lookup.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/specific/cpp_function_lookup.h ================================================ // stuff on the paramQual void fNoexcept() noexcept; void fFinal() final; void fOverride() override; void fAttr() [[myattr]]; // TODO: Doxygen seems to strip attributes void fFInit() = default; auto fTrailing() -> int; // different parameters void fInit(int arg = 42); void fPlain(int arg); void fPtr(int *arg); void fLRef(int &arg); void fRRef(int &&arg); template void fParamPack(T ...arg); class A {}; void fMemPtr(int A::*arg); void fParen(void (*arg)()); // different parameters in a function pointer void fParenPlain(void (*arg)(int argInner)); ================================================ FILE: examples/specific/cpp_inherited_members.cfg ================================================ PROJECT_NAME = "C++ Inherited Members" OUTPUT_DIRECTORY = cpp_inherited_members GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = cpp_inherited_members.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES INLINE_INHERITED_MEMB = YES ================================================ FILE: examples/specific/cpp_inherited_members.h ================================================ /** * @file */ /// Base class class Base { public: /// Base-class member function void f_issue_356(); }; /// Class A class A : public Base {}; /// Class B class B : public Base {}; ================================================ FILE: examples/specific/cpp_ns_template_specialization.cfg ================================================ PROJECT_NAME = "C++ Template Specialization with Namespace" OUTPUT_DIRECTORY = cpp_ns_template_specialization GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = cpp_ns_template_specialization.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES WARN_IF_UNDOCUMENTED = NO ================================================ FILE: examples/specific/cpp_ns_template_specialization.h ================================================ namespace ns1 { struct Foo { int value; }; } // namespace ns1 namespace ns2 { template struct Trait { static constexpr bool valid = false; }; template <> struct Trait { static constexpr bool valid = true; }; } // namespace ns2 ================================================ FILE: examples/specific/cpp_trailing_return_type.cfg ================================================ PROJECT_NAME = "C++ Trailing Return Type" OUTPUT_DIRECTORY = cpp_trailing_return_type GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = cpp_trailing_return_type.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/specific/cpp_trailing_return_type.h ================================================ /*! \file cpp_trailing_return_type.h */ /*! needed for references in global function return type */ class Thingy {}; //! \brief Function that creates a thingy. auto f_issue_441() -> Thingy*; ================================================ FILE: examples/specific/cpp_union.cfg ================================================ PROJECT_NAME = "C++ Union" OUTPUT_DIRECTORY = cpp_union GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = cpp_union.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES WARN_IF_UNDOCUMENTED = NO ================================================ FILE: examples/specific/cpp_union.h ================================================ union Union { int i; }; struct Class { union Union { int i; }; }; ================================================ FILE: examples/specific/decl_impl.cfg ================================================ PROJECT_NAME = "Declaration/Implementation" OUTPUT_DIRECTORY = decl_impl GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = decl_impl.h decl_impl.cpp QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/specific/decl_impl.cpp ================================================ /*! More detailed description Yeah and some more. */ int open_di(const char * pathname, int flags) { return 0; } namespace testnamespace { /*! Even more documentation. */ int another_open_di(const char *,int) { return 0; } } ================================================ FILE: examples/specific/decl_impl.h ================================================ /*! \fn int open_di(const char *pathname,int flags) \brief Opens a file descriptor. Detailed description. \param pathname The name of the descriptor. \param flags Opening flags. */ int open_di(const char *,int); namespace testnamespace { /*! \brief Some documentation. More documentation. */ int another_open_di(const char *,int); } ================================================ FILE: examples/specific/define.cfg ================================================ PROJECT_NAME = "Define example" OUTPUT_DIRECTORY = define GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = define.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/specific/define.h ================================================ /** * A simple define without a value */ #define USE_STUFF /** * A define with a simple value */ #define MAX_LENGTH 100 /** * A define with some parameters * * \param A The parameter A * \param B The parameter B * * \returns The maximum of A and B */ #define MAXIMUM(A,B) ((A > B)?(A):(B)) /** * A define which spans multiple lines */ #define SWAP(A,B) { \ (a) ^= (b); \ (b) ^= (a); \ (a) ^= (b); \ } ================================================ FILE: examples/specific/dot_graphs.cfg ================================================ PROJECT_NAME = "Dot Graphs" HAVE_DOT = YES OUTPUT_DIRECTORY = dot_graphs DOTFILE_DIRS = . GENERATE_LATEX = NO GENERATE_RTF = NO GENERATE_MAN = NO CASE_SENSE_NAMES = NO INPUT = dot_graphs.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES WARNINGS = NO ================================================ FILE: examples/specific/dot_graphs.h ================================================ /** * @file dot_graphs.h * * @page dotgraphs Dot Graph Demonstrations * * @section dotcmd Using \@dot command * * @dot "basic graph elements" * digraph G { * bgcolor="purple:pink" label="a graph" fontcolor="white" * subgraph cluster1 { * fillcolor="blue:cyan" label="a cluster" fontcolor="white" style="filled" gradientangle="270" * node [shape=box fillcolor="red:yellow" style="filled" gradientangle=90] * "a node"; * } * } * @enddot * * @section dotfilecmd Using \@dotfile command * * @dotfile "dotfile.dot" "Captions go here" */ ================================================ FILE: examples/specific/dotfile.dot ================================================ digraph G {bgcolor="red:cyan" gradientangle=0 subgraph cluster_0 { style=filled; color=lightgrey; fillcolor="blue:yellow"; gradientangle=90; node [fillcolor="yellow:green" style=filled gradientangle=270] a0; node [fillcolor="green:red"] a1; node [fillcolor="red:cyan"] a2; node [fillcolor="cyan:blue"] a3; a0 -> a1 -> a2 -> a3; label = "process #1"; } subgraph cluster_1 { node [fillcolor="yellow:magenta" style=filled gradientangle=270] b0; node [fillcolor="magenta:cyan"] b1; node [fillcolor="cyan:red"] b2; node [fillcolor="red:blue"] b3; b0 -> b1 -> b2 -> b3; label = "process #2"; color=blue fillcolor="blue:yellow"; style=filled; gradientangle=90; } start -> a0; start -> b0; a1 -> b3; b2 -> a3; a3 -> a0; a3 -> end; b3 -> end; start [shape=Mdiamond , fillcolor="yellow:brown", gradientangle=90, style=radial]; end [shape=Msquare, fillcolor="orange:blue", style=radial, gradientangle=90]; } ================================================ FILE: examples/specific/enum.cfg ================================================ PROJECT_NAME = "Enum Command" OUTPUT_DIRECTORY = enum GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = enum.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/specific/enum.h ================================================ namespace TestEnumNamespace { //! \brief enum inside of namespace enum NamespacedEnumTest { //! \brief namespaced enum value FIRST, SECOND }; } //! \brief enum outside of namespace enum EnumTest { //! \brief enum value VALUE }; ================================================ FILE: examples/specific/fixedwidthfont.cfg ================================================ PROJECT_NAME = "Fixed width font markup doc test" OUTPUT_DIRECTORY = fixedwidthfont GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = fixedwidthfont.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES WARN_IF_UNDOCUMENTED = NO ================================================ FILE: examples/specific/fixedwidthfont.h ================================================ class Out { public: //! \b Constructor \a for Out object Out() {} //! \a Destructor \b for Out object ~Out() {} }; ================================================ FILE: examples/specific/functionOverload.cfg ================================================ PROJECT_NAME = "Function Overload" OUTPUT_DIRECTORY = functionOverload GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = functionOverload.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/specific/functionOverload.h ================================================ #include //! Non overloaded function void simplefunc(); //! Function which takes two int arguments void f(int, int); //! Function which takes two double arguments void f(double, double); namespace test { //! Another function which takes two int arguments void g(int, int); //! Another function which takes two double arguments void g(double, double); } /*! needed for references in global function parameters */ class MyType {}; /*! needed for references in global function parameters */ class MyOtherType {}; //! Another function which takes a custom type void h(std::string, MyType); //! Another function which takes another custom type void h(std::string, MyOtherType o); //! Another function which takes a basic type void h(std::string, float myfloat); //! Another function which takes a const custom type void h(std::string, const MyType& mytype); //! Another function which takes a const basic type void h(std::string, const int myint); //! Another function which takes a const basic type template void h(std::string, const T myType); //! Another function which takes a const basic type template void h(std::string, const T m, const U n); /** * Test function 1. */ void j(int); /** * Test function 2. */ void j(char); ================================================ FILE: examples/specific/group.cfg ================================================ # For a case where Breathe was failing on the doxygen output PROJECT_NAME = "Group Test" OUTPUT_DIRECTORY = group GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = group.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES WARN_IF_UNDOCUMENTED = NO ================================================ FILE: examples/specific/group.h ================================================ /** @defgroup mygroup My Group * This is the first group * @{ */ //! \brief first class inside of namespace class GroupedClassTest { public: //! \brief namespaced class function virtual void publicFunction() const = 0; virtual void undocumentedPublicFunction() const = 0; //! A protected class class PublicClass {}; class UndocumentedPublicClass {}; protected: //! A protected function void protectedFunction() {}; void undocumentedProtectedFunction() {}; //! A protected class class ProtectedClass {}; class UndocumentedProtectedClass {}; private: //! This is a private function virtual void privateFunction() const = 0; virtual void undocumentedPrivateFunction() const = 0; //! A private class class PrivateClass {}; class UndocumentedPrivateClass {}; }; //! This function is in MyGroup void groupedFunction(); /** @} */ // end of mygroup /** @defgroup innergroup Inner Group * @ingroup mygroup * This is an inner group * @{ */ //! \brief inner class inside of namespace class InnerGroupClassTest { public: //! \brief inner namespaced class function void function() {}; private: //! A private function void innerGroupPrivateFunction() {}; class PrivateClass {}; }; /** @} */ // end of innergroup //! \brief second class inside of namespace class UngroupedClassTest { public: //! \brief second namespaced class function void function() {}; private: //! A private function void ungroupedPrivateFunction() {}; class PrivateClass {}; }; //! Ungrouped function void ungroupedFunction(); ================================================ FILE: examples/specific/headerfile.cfg ================================================ PROJECT_NAME = "Headerfile Command" OUTPUT_DIRECTORY = headerfile GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = headerfile.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/specific/headerfile.h ================================================ /** * \class ClassTest * \headerfile hearderfile.h "specific/headerfile.h" * \brief Class showing the header where it is defined */ class ClassTest { public: //! \brief Constructor ClassTest(); }; /** * \class ClassTest2 headerfile.h "specific/headerfile.h" * \brief Another way to show the header file */ class ClassTest2 { public: //! \brief Constructor ClassTest2(); } ================================================ FILE: examples/specific/headings.cfg ================================================ PROJECT_NAME = "Headings" OUTPUT_DIRECTORY = headings GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = headings.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/specific/headings.h ================================================ /*! \brief This is a documentation This is more documentation.

    Header

    Text

    Header Bold Header Text

    Text Header --------- Text ### Header ### Text */ class HeadingsTest {}; ================================================ FILE: examples/specific/image.cfg ================================================ PROJECT_NAME = "Image Test" OUTPUT_DIRECTORY = image GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = image.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES IMAGE_PATH = . ================================================ FILE: examples/specific/image.h ================================================ /** * This is a class with an image in the description. It renders like this: * * \image HTML imageExample.png * * Breathe & Sphinx should automatically copy the image from the doxygen output directory into the * _images folder of the Sphinx output. */ class ImageClass {}; ================================================ FILE: examples/specific/inheritance.cfg ================================================ PROJECT_NAME = "Inheritance" OUTPUT_DIRECTORY = inheritance GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = inheritance.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/specific/inheritance.h ================================================ class BaseA {}; class BaseB {}; /*! \brief This is the main class we're interested in */ class Main : public BaseA, BaseB {}; class ChildA : public Main {}; class ChildB : public Main {}; ================================================ FILE: examples/specific/inline.cfg ================================================ PROJECT_NAME = "Inline Parameter Doc Test" OUTPUT_DIRECTORY = inline GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = inline.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/specific/inline.h ================================================ #include /** A class to demonstrate inline documentation syntax. */ class InlineTest { public: /** A member function. * * Details about member function * * \exception std::out_of_range parameter is out of range. * @return a character pointer. */ const char *member(char c, ///< c a character. int n) ///< n an integer. throw(std::out_of_range); }; ================================================ FILE: examples/specific/interface.cfg ================================================ PROJECT_NAME = "Interface Command" OUTPUT_DIRECTORY = interface GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = interface.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/specific/interface.h ================================================ /* A dummy interface, which is really just a specially treated class in C++ */ class InterfaceClass { }; /*! \interface InterfaceClass interface.h "inc/interface.h" * \brief This is a test interface class. * * Some details about the InterfaceClass interface */ ================================================ FILE: examples/specific/latexmath.cfg ================================================ PROJECT_NAME = "Latex Math Option" OUTPUT_DIRECTORY = latexmath GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = latexmath.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/specific/latexmath.h ================================================ /** * @brief A class * * A inline formula: \f$ f(x) = a + b \f$ * * A display style formula: * @f[ * \int_a^b f(x) dx = F(b) - F(a) * @f] */ class MathHelper { public: MathHelper() {} ~MathHelper() {} } ================================================ FILE: examples/specific/links.cfg ================================================ PROJECT_NAME = "Links Command" OUTPUT_DIRECTORY = links GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = links.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/specific/links.h ================================================ /*! \brief first struct inside of namespace This is a longer description with a link to a webpage in the text https://github.com in order to test out Breathe's handling of links. */ class LinksTest {}; ================================================ FILE: examples/specific/lists.cfg ================================================ PROJECT_NAME = "Lists Option" OUTPUT_DIRECTORY = lists GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = lists.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/specific/lists.h ================================================ /** * \brief This is a list example. * * Following is a list using '+' for bullets: * + One item. * + Two items. * + Three items. * + Four. * * And this is some more text. */ class SimpleList_1 { }; /** * \brief This is a list example. * * Following is a list using '-' for bullets: * - One item. * - Two items. * - Three items. * - Four. * * And this is some more text. */ class SimpleList_2 { }; /** * \brief This is a list example. * * Following is a list using '*' for bullets: * * One item. * * Two items. * * Three items. * * Four. * * And this is some more text. */ class SimpleList_3 { }; /** * \brief This is a list example. * * Following is an auto-numbered list: * -# One item. * -# Two items. * -# Three items. * -# Four. * * And this is some more text. */ class SimpleList_4 { }; /** * \brief This is a list example. * * Following is a numbered list: * 1. One item. * 2. Two items. * 3. Three items. * 4. Four. * * And this is some more text. */ class SimpleList_5 { }; /** * \brief This is a list example. * * Following is an unordered list using 'HTML' tags: *
    • One item. *
    • Two items. *
    • Three items. *
    • Four. *
    * * And this is some more text. */ class SimpleList_6 { }; /** * A list of events: * - mouse events * -# mouse move event * -# mouse click event\n * More info about the click event. * -# mouse double click event * - keyboard events * 1. key down event * 2. key up event * * More text here. */ class NestedLists_1 { }; /** * Text before the list * - list item 1 * - sub item 1 * - sub sub item 1 * - sub sub item 2 * . * The dot above ends the sub sub item list. * * More text for the first sub item * . * The dot above ends the first sub item. * * More text for the first list item * - sub item 2 * - sub item 3 * - list item 2 * . * More text in the same paragraph. * * More text in a new paragraph. */ class NestedLists_2 { }; /*! * A list of events: *
      *
    • mouse events *
        *
      1. mouse move event *
      2. mouse click event
        * More info about the click event. *
      3. mouse double click event *
      *
    • keyboard events *
        *
      1. key down event *
      2. key up event *
      *
    * More text here. */ class NestedLists_3 { }; /** * A list of events: * 1. mouse events * -# mouse move event * 1. swipe event * 2. circle event * 3. wave event * -# mouse click event\n * More info about the click event. * -# mouse double click event * 2. keyboard events * -# key down event * -# key up event * 3. touch events * -# pinch event * -# swipe event * More text here. */ class NestedLists_4 { }; /** * A deeply nested list of events: * 1. mouse events * -# mouse move event * 1. swipe event * -# swipe left * -# swipe right * 2. circle event * 3. wave event * -# mouse click event\n * More info about the click event. * -# mouse double click event * 2. keyboard events * -# key down event * -# key up event * 3. touch events * -# pinch event * -# swipe event * More text here. */ class NestedLists_5 { }; ================================================ FILE: examples/specific/make.bat ================================================ @ECHO OFF set DOXYGEN=doxygen for /f "delims=" %%i in ('where doxygen') do set DOXYGEN=%%i set PERL=perl for /f "delims=" %%i in ('where perl') do set PERL=%%i set HAVE_DOT=dot for /f "delims=" %%i in ('where dot') do set HAVE_DOT=%%i @REM echo DOXYGEN : %DOXYGEN% @REM echo PERL : %PERL% @REM echo HAVE_DOT : %HAVE_DOT% if "%1" == "" ( call :all goto end ) if "%1" == "all" ( call :all goto end ) if "%1" == "clean" ( call :clean goto end ) goto end :all @REM --------------- @REM General Pattern @REM --------------- call :doxygen nutshell.cfg call :doxygen alias.cfg call :doxygen rst.cfg call :doxygen inline.cfg call :doxygen namespacefile.cfg call :doxygen array.cfg call :doxygen inheritance.cfg call :doxygen members.cfg call :doxygen userdefined.cfg call :doxygen fixedwidthfont.cfg call :doxygen latexmath.cfg call :doxygen functionOverload.cfg call :doxygen image.cfg call :doxygen name.cfg call :doxygen union.cfg call :doxygen group.cfg call :doxygen struct.cfg call :doxygen struct_function.cfg call :doxygen qtsignalsandslots.cfg call :doxygen lists.cfg call :doxygen headings.cfg call :doxygen links.cfg call :doxygen parameters.cfg call :doxygen template_class.cfg call :doxygen template_class_non_type.cfg call :doxygen template_function.cfg call :doxygen template_type_alias.cfg call :doxygen template_specialisation.cfg call :doxygen enum.cfg call :doxygen define.cfg call :doxygen interface.cfg call :doxygen xrefsect.cfg call :doxygen tables.cfg call :doxygen cpp_anon.cfg call :doxygen cpp_concept.cfg call :doxygen cpp_enum.cfg call :doxygen cpp_union.cfg call :doxygen cpp_function.cfg call :doxygen cpp_friendclass.cfg call :doxygen cpp_inherited_members.cfg call :doxygen cpp_trailing_return_type.cfg call :doxygen cpp_constexpr_hax.cfg call :doxygen cpp_function_lookup.cfg call :doxygen c_file.cfg call :doxygen c_struct.cfg call :doxygen c_enum.cfg call :doxygen c_typedef.cfg call :doxygen c_macro.cfg call :doxygen c_union.cfg call :doxygen membergroups.cfg call :doxygen simplesect.cfg call :doxygen code_blocks.cfg call :doxygen dot_graphs.cfg @REM ------------- @REM Special Cases @REM ------------- call :doxygen programlisting.cfg call :doxygen decl_impl.cfg call :doxygen multifile.cfg call :doxygen auto.cfg call :doxygen class.cfg call :doxygen typedef.cfg goto end :clean @REM --------------- @REM General Pattern @REM --------------- call :rmdir nutshell call :rmdir alias call :rmdir rst call :rmdir inline call :rmdir namespacefile call :rmdir array call :rmdir inheritance call :rmdir members call :rmdir userdefined call :rmdir fixedwidthfont call :rmdir latexmath call :rmdir functionOverload call :rmdir image call :rmdir name call :rmdir union call :rmdir group call :rmdir struct call :rmdir struct_function call :rmdir qtsignalsandslots call :rmdir lists call :rmdir headings call :rmdir links call :rmdir parameters call :rmdir template_class call :rmdir template_class_non_type call :rmdir template_function call :rmdir template_type_alias call :rmdir template_specialisation call :rmdir enum call :rmdir define call :rmdir interface call :rmdir xrefsect call :rmdir tables call :rmdir cpp_anon call :rmdir cpp_concept call :rmdir cpp_enum call :rmdir cpp_union call :rmdir cpp_function call :rmdir cpp_friendclass call :rmdir cpp_inherited_members call :rmdir cpp_trailing_return_type call :rmdir cpp_constexpr_hax call :rmdir cpp_function_lookup call :rmdir c_file call :rmdir c_struct call :rmdir c_enum call :rmdir c_typedef call :rmdir c_macro call :rmdir c_union call :rmdir membergroups call :rmdir simplesect call :rmdir code_blocks call :rmdir dot_graphs @REM ------------- @REM Special Cases @REM ------------- call :rmdir programlisting call :rmdir decl_impl call :rmdir multifilexml call :rmdir auto call :rmdir class call :rmdir typedef goto end :doxygen set CFG=%~1 echo Running doxygen: %CFG% "%DOXYGEN%" %CFG% goto end :rmdir set DIR=%~1 if exist "%DIR%" ( echo Removing directory: %DIR% rmdir /s/q "%DIR%" ) goto end :end ================================================ FILE: examples/specific/membergroups.cfg ================================================ PROJECT_NAME = "Member Groups" OUTPUT_DIRECTORY = membergroups GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = membergroups.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/specific/membergroups.h ================================================ //! \brief demonstrates member groups class GroupedMembers { public: ///@{ @name myGroup void in_mygroup_one(int myParameter); ///< A function void in_mygroup_two(int myParameter); ///< Another function ///@} void not_in_mygroup(int myParameter); ///< This one is not in myGroup }; ================================================ FILE: examples/specific/members.cfg ================================================ PROJECT_NAME = "Members Option" OUTPUT_DIRECTORY = members GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = members.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/specific/members.h ================================================ namespace testnamespace { //! \brief first class inside of namespace class NamespacedClassTest { public: //! \brief namespaced class function virtual void function() const = 0; explicit NamespacedClassTest() {}; protected: //! Some kind of function static void functionS(); private: //! \brief namespaced class other function void anotherFunction() {}; }; } ================================================ FILE: examples/specific/multifile/one/Util.h ================================================ namespace test { typedef float MyFloat; void TestFunc() {}; } class TestClass { public: /// enum docs enum Enum {}; }; ================================================ FILE: examples/specific/multifile/two/Util.h ================================================ #include "../one/Util.h" namespace test { typedef int MyInt; struct TestStruct {}; } /// The non-type template parameter references a different file template void TestTemplateFunction(); ================================================ FILE: examples/specific/multifile.cfg ================================================ PROJECT_NAME = "Multifile" OUTPUT_DIRECTORY = multifilexml GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = multifile RECURSIVE = YES QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES WARN_IF_UNDOCUMENTED = NO ================================================ FILE: examples/specific/name.cfg ================================================ # For a case where Breathe was failing on the doxygen output PROJECT_NAME = "Name Test" OUTPUT_DIRECTORY = name GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = name.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/specific/name.h ================================================ /*! \brief A failing class * \class Failing */ class Failing { public: /** * @name Some section . * THIS IS CAUSING THE ERROR, must have an empty star line above * * @{ */ int getSomething() const; ///< some docs bool isSomething() const; ///< some more docs //@} }; ================================================ FILE: examples/specific/namespacefile.cfg ================================================ PROJECT_NAME = "Namespace in file" OUTPUT_DIRECTORY = namespacefile GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = namespacefile.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES WARN_IF_UNDOCUMENTED = NO ================================================ FILE: examples/specific/namespacefile.h ================================================ namespace foo { /** This appears in the documentation */ class Bar { public: //! \brief namespaced class function virtual void publicFunction() const = 0; virtual void undocumentedPublicFunction() const = 0; //! A protected class class PublicClass {}; class UndocumentedPublicClass {}; protected: //! A protected function void protectedFunction() {}; void undocumentedProtectedFunction() {}; //! A protected class class ProtectedClass {}; class UndocumentedProtectedClass {}; private: //! This is a private function virtual void privateFunction() const = 0; virtual void undocumentedPrivateFunction() const = 0; //! A private class class PrivateClass {}; class UndocumentedPrivateClass {}; }; /** This also appears */ int baz(); /** More examples in a nested namespace */ namespace ns { typedef int MyInt; enum Letters { A, /**< A documented enumeration constant */ B, C }; /** Documentation here */ struct FooStruct {}; class FooClass { class InnerFoo {}; }; } } /** This is outside the namespace */ class OuterBar { /** This appears as a sub class */ class InnerBar {}; }; /** Function outside of the namespace */ void outerFunction() {}; ================================================ FILE: examples/specific/nutshell.cfg ================================================ PROJECT_NAME = "Breathe in a nutshell example" OUTPUT_DIRECTORY = nutshell GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = nutshell.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/specific/nutshell.h ================================================ /** \file nutshell.h An overly extended example of how to use breathe */ /*! With a little bit of a elaboration, should you feel it necessary. */ class Nutshell { public: //! Our tool set /*! The various tools we can opt to use to crack this particular nut */ enum Tool { kHammer = 0, //!< What? It does the job kNutCrackers, //!< Boring kNinjaThrowingStars //!< Stealthy }; //! Nutshell constructor Nutshell(); //! Nutshell destructor ~Nutshell(); /*! Crack that shell with specified tool \param tool the tool with which to crack the nut */ void crack( Tool tool ); /*! \return Whether or not the nut is cracked */ bool isCracked(); private: //! Our cracked state bool m_isCracked; }; ================================================ FILE: examples/specific/parameters.cfg ================================================ # For a case where Breathe was failing on the doxygen output PROJECT_NAME = "Parameters Test" OUTPUT_DIRECTORY = parameters GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = parameters.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/specific/parameters.h ================================================ /*! My function */ int f(int a, float b, int* c, int (*p)[3]); class MyClass {}; int g(MyClass a, MyClass* b); ================================================ FILE: examples/specific/programlisting.cfg ================================================ PROJECT_NAME = "Program Listing Test" OUTPUT_DIRECTORY = programlisting GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = programlisting.h EXAMPLE_PATH = . QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES EXTRACT_ALL = YES ENABLE_PREPROCESSING = YES MACRO_EXPANSION = YES EXPAND_ONLY_PREDEF = YES PREDEFINED = LEAP_EXPORT="" ================================================ FILE: examples/specific/programlisting.h ================================================ /*! Vector class */ class Vector {}; /*! * The center of the InteractionBox in device coordinates (millimeters). This point * is equidistant from all sides of the box. * * \include programlistinginclude.txt * * @returns The InteractionBox center in device coordinates. * @since 1.0 */ LEAP_EXPORT Vector center() const; ================================================ FILE: examples/specific/programlistinginclude.txt ================================================ Vector boxCenter = interactionBox.center(); Vector max = interactionBox.max(); Vector diff = max - boxCenter; ================================================ FILE: examples/specific/qtsignalsandslots.cfg ================================================ PROJECT_NAME = "Qt Signals & Slots" OUTPUT_DIRECTORY = qtsignalsandslots GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = qtsignalsandslots.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES WARN_IF_UNDOCUMENTED = NO ================================================ FILE: examples/specific/qtsignalsandslots.h ================================================ #ifndef QT_OBJECT_H #define QT_OBJECT_H /*! *\brief Forward declaration of QT API class QT slots and signals typically `#include `, but this example is parsed without QT SDK installed. */ extern class QObject; class QtSignalSlotExample: public QObject { Q_OBJECT public: /*! *\param iShownParameter This is shown in declaration */ void workingFunction( int iShownParameter ) { Q_UNUSED( iShownParameter ; ) } signals: /*! \param iShown This is in function declaration */ void workingSignal( int iShown ); public slots: /*! \param iShown This is in function declaration */ void workingSlot( int iShown ) { iShown; } }; #endif ================================================ FILE: examples/specific/rst.cfg ================================================ PROJECT_NAME = "Rst Test" OUTPUT_DIRECTORY = rst GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = rst.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ALIASES = "rst=\verbatim embed:rst" ALIASES += "endrst=\endverbatim" ALIASES += "inlinerst=\verbatim embed:rst:inline" ================================================ FILE: examples/specific/rst.h ================================================ //! \brief first class inside of namespace class TestClass { public: /*! Inserting additional reStructuredText information. \rst This is some funky non-XML compliant text: <& !>< .. note:: This reStructuredText has been handled correctly. \endrst This is just a standard verbatim block with code: \verbatim child = 0; while( child = parent->IterateChildren( child ) ) \endverbatim */ virtual void function() const = 0; /*! Inserting additional reStructuredText information. \verbatim embed:rst .. note:: This reStructuredText has been handled correctly. \endverbatim */ virtual void rawVerbatim() const = 0; /*! * Inserting additional reStructuredText information. * * \verbatim embed:rst:leading-asterisk * Some example code:: * * int example(int x) { * return x * 2; * } * \endverbatim */ virtual void rawLeadingAsteriskVerbatim() const = 0; /// Some kind of method /// /// @param something a parameter /// /// @verbatim embed:rst:leading-slashes /// .. code-block:: c /// :linenos: /// /// bool foo(bool something) { /// return something; /// }; /// /// @endverbatim /// @note Documentation using `///` should begin and end in a blank line. virtual void rawLeadingSlashesVerbatim(int something) const = 0; /*! Inserting an inline reStructuredText snippet. Linking to another function: \inlinerst :cpp:func:`TestClass::rawVerbatim` \endrst */ virtual void rawInlineVerbatim() const = 0; //! Brief description virtual void testFunction() const {}; }; ================================================ FILE: examples/specific/simplesect.cfg ================================================ PROJECT_NAME = "Simplesect" OUTPUT_DIRECTORY = simplesect GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = simplesect.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/specific/simplesect.h ================================================ /*! \pre stuff must be correct \pre more stuff must be correct \post stuff will be nice \post more stuff will be nice \return nothing \par par, something \warning warning, don't do this \note note, be careful \see see, f_raw \sa sa, f_raw \remark remark, 1 \remark remark, 2 \remarks remarks, 1 \remarks remarks, 2 */ template void f(int a, float b, std::string c); ================================================ FILE: examples/specific/struct.cfg ================================================ PROJECT_NAME = "Struct Command" OUTPUT_DIRECTORY = struct GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = struct.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES WARN_IF_UNDOCUMENTED = NO ================================================ FILE: examples/specific/struct.h ================================================ namespace testnamespace { //! \brief first struct inside of namespace struct NamespacedStructTest { //! \brief namespaced struct function virtual void function() const = 0; static void functionS(); explicit NamespacedStructTest() {}; //! \brief namespaced struct other function void anotherFunction() {}; }; //! \brief second struct inside of namespace struct StructTest { //! \brief second namespaced struct function void function() {}; //! \brief second namespaced struct other function void anotherFunction() {}; //! A public class class PublicClass {}; class UndocumentedPublicClass {}; }; }; //! \brief struct outside of namespace struct StructTest { //! \brief namespaced class function virtual void publicFunction() const = 0; virtual void undocumentedPublicFunction() const = 0; //! A public class class PublicClass {}; class UndocumentedPublicClass {}; protected: //! A protected function void protectedFunction() {}; void undocumentedProtectedFunction() {}; //! A protected class class ProtectedClass {}; class UndocumentedProtectedClass {}; private: //! This is a private function virtual void privateFunction() const = 0; virtual void undocumentedPrivateFunction() const = 0; //! A private class class PrivateClass {}; class UndocumentedPrivateClass {}; }; ================================================ FILE: examples/specific/struct_function.cfg ================================================ PROJECT_NAME = "Struct Function Command" OUTPUT_DIRECTORY = struct_function GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = struct_function.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES WARN_IF_UNDOCUMENTED = NO ================================================ FILE: examples/specific/struct_function.h ================================================ namespace testnamespace { //! \brief templated struct with functions template struct MyClass { //! \brief struct empty constructor MyClass(); //! \brief struct copy constructor MyClass(const MyClass&); int myMemberVar; }; } ================================================ FILE: examples/specific/tables.cfg ================================================ PROJECT_NAME = "Tables Option" OUTPUT_DIRECTORY = tables GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = tables.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/specific/tables.h ================================================ /** * \brief This is a simple Markdown table example. * * Following is a simple table using Markdown syntax. * * First Header | Second Header * ------------- | ------------- * Content Cell | Content Cell * Content Cell | Content Cell * * And this is some more text. */ class Table_1 { }; /** * \brief This is a Markdown table with alignment. * * Following is a table with alignment using Markdown syntax. * * | Right | Center | Left | * | ----: | :----: | :---- | * | 10 | 10 | 10 | * | 1000 | 1000 | 1000 | * * And this is some more text. */ class Table_2 { }; /** * \brief This is a Markdown table with rowspan and alignment. * * Following is a table with rowspan and alignment using Markdown syntax. * * | Right | Center | Left | * | ----: | :----: | :---- | * | 10 | 10 | 10 | * | ^ | 1000 | 1000 | * * And this is some more text. */ class Table_3 { }; /** * \brief This is a Markdown table with colspan and alignment. * * Following is a table with colspan and alignment using Markdown syntax. * * | Right | Center | Left | * | ----: | :----: | :---- | * | 10 | 10 | 10 | * | 1000 ||| * * And this is some more text. */ class Table_4 { }; /** * \brief This is a Doxygen table. * * Following is a table using Doxygen syntax (and all supported features). * * * *
    Complex table
    Column 1 Column 2 Column 3 *
    cell row=1+2,col=1cell row=1,col=2cell row=1,col=3 *
    cell row=2+3,col=2 cell row=2,col=3 *
    cell row=3,col=1 cell row=3+4,col=3 *
    cell row=4,col=1+2 *
    cell row=5,col=1 cell row=5,col=2+3 *
    cell row=6+7,col=1+2 cell row=6,col=3 *
    cell row=7,col=3 *
    cell row=8,col=1 cell row=8,col=2\n * *
    Inner cell row=1,col=1Inner cell row=1,col=2 *
    Inner cell row=2,col=1Inner cell row=2,col=2 *
    *
    cell row=8,col=3 *
      *
    • Item 1 *
    • Item 2 *
    *
    * * And this is some more text. */ class Table_5 { }; ================================================ FILE: examples/specific/template_class.cfg ================================================ PROJECT_NAME = "Template Class" OUTPUT_DIRECTORY = template_class GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = template_class.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/specific/template_class.h ================================================ /** * @brief a class with a template parameter * * @tparam T this is the template parameter */ template class templateclass { public: /// default constructor templateclass() {} /** * @brief constructor with template argument * * @param m the argument */ templateclass(T const & m) : member(m) {} /** * @brief member accepting template argument and returning template argument * * @param t argument of type T * @return returns value of type T */ T method(T const & t); private: /// a member with templated type T member; }; ================================================ FILE: examples/specific/template_class_non_type.cfg ================================================ PROJECT_NAME = "Template Class Non Type" OUTPUT_DIRECTORY = template_class_non_type GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = template_class_non_type.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/specific/template_class_non_type.h ================================================ /** * @brief a class with three template parameters * * @tparam T this is the first template parameter * @tparam U this is the second template parameter * @tparam N this is the third template parameter, it is a non-type parameter */ template class anothertemplateclass { public: /// default constructor anothertemplateclass() {} /** * @brief constructor with two template argument * * @param m1 first argument * @param m2 second argument */ anothertemplateclass(T const & m1, U const & m2) : member1(m1), member2(m2) {} /** * @brief member accepting template argument and returning template argument * * @param t argument * @return returns value of type U */ U method(T const & t); private: /// a member with templated type T member1; /// another member with templated type U member2; }; ================================================ FILE: examples/specific/template_function.cfg ================================================ PROJECT_NAME = "Template Function" OUTPUT_DIRECTORY = template_function GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = template_function.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/specific/template_function.h ================================================ #include /** * @brief a function with one template arguments * * @tparam T this is the template parameter * * @param arg1 argument of type T * * @return return value of type T */ template T function1(T arg1) {} /** * @brief a function with one template argument specialized for `std::string` * * @param arg1 argument of type `std::string` * * @return return value of type `std::string` */ template <> std::string function1(std::string arg1) {} /** * @brief a function with three template arguments * * @tparam T this is the first template parameter * @tparam U this is the second template parameter * @tparam N this is the third template parameter, it is a non-type parameter * * @param arg1 first argument of type T * @param arg2 second argument of type U * * @return return value of type T */ template T function2(T arg1, U arg2) {} ================================================ FILE: examples/specific/template_specialisation.cfg ================================================ PROJECT_NAME = "Template Specialisation" OUTPUT_DIRECTORY = template_specialisation GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = template_specialisation.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/specific/template_specialisation.h ================================================ /*! A generic template class. */ template class TemplateClass { }; /*! A partial specialization of TemplateClass for pointer types. */ template class TemplateClass { }; /*! A generic template class. */ template class SecondTemplateClass { }; /*! A partial specialization of SecondTemplateClass for pointer types. */ template class SecondTemplateClass { }; ================================================ FILE: examples/specific/template_type_alias.cfg ================================================ PROJECT_NAME = "Template Type Alias" OUTPUT_DIRECTORY = template_type_alias GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = template_type_alias.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/specific/template_type_alias.h ================================================ /** * @brief a type alias with one template argument * * @tparam T this is the template parameter * */ template using IsFuzzy = std::is_fuzzy; /** * @brief a type alias with three template arguments * * @tparam T this is the first template parameter * @tparam U this is the second template parameter * @tparam N this is the third template parameter, it is a non-type parameter * */ template using IsFurry = std::is_furry; ================================================ FILE: examples/specific/typedef.cfg ================================================ PROJECT_NAME = "Function Type Def Command" OUTPUT_DIRECTORY = typedef GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = typedef.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES WARN_IF_UNDOCUMENTED = NO ================================================ FILE: examples/specific/typedef.h ================================================ class TypeDefTest { }; /* A dummy typedef */ typedef TypeDefTest (*TypeDefTestFuncPtr)(void); typedef void* (*voidFuncPtr)(float, int); typedef void* voidPointer; typedef float* floatPointer; typedef float floatingPointNumber; typedef int TestTypedef; namespace TypeDefNamespace { typedef char *AnotherTypedef; } class TestClass { public: /** A typedef defined in a class. */ typedef void *MemberTypedef; typedef void (*MemberTypedefFuncPointer)(int, double); }; using TypeAlias = int; ================================================ FILE: examples/specific/union.cfg ================================================ PROJECT_NAME = "Union" OUTPUT_DIRECTORY = union GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = union.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/specific/union.h ================================================ /// A union of two values union SeparateUnion { int size; ///< The size of the thing float depth; ///< How deep it is }; namespace foo { /// A union of two values union MyUnion { int someInt; ///< The int of it all float someFloat; ///< The float side of things }; } /// A class with a union class ClassWithUnion { /// A union with two values union UnionInClass { int intvalue; ///< An int value float floatvalue; ///< A float value }; /// Documented class class ExtraClass { int a_member; float another_member; }; }; ================================================ FILE: examples/specific/userdefined.cfg ================================================ PROJECT_NAME = "User Defined Example" OUTPUT_DIRECTORY = userdefined GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = userdefined.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES DISTRIBUTE_GROUP_DOC = YES WARN_IF_UNDOCUMENTED = NO ================================================ FILE: examples/specific/userdefined.h ================================================ // Example from Doxygen documentation /** A class. More details about the UserDefinedGroupTest class */ class UserDefinedGroupTest { public: //@{ /** Same documentation for both members. Details */ void func1InGroup1(); void func2InGroup1(); //@} /** Function without group. Details. */ void ungroupedFunction(); void func1InCustomGroup(); protected: void func2InCustomGroup(); }; void UserDefinedGroupTest::func1InGroup1() {} void UserDefinedGroupTest::func2InGroup1() {} /** @name Custom Group * Description of custom group */ //@{ /** Function 2 in custom group. Details. */ void UserDefinedGroupTest::func2InCustomGroup() {} /** Function 1 in custom group. Details. */ void UserDefinedGroupTest::func1InCustomGroup() {} //@} ================================================ FILE: examples/specific/using_in_ns.cfg ================================================ PROJECT_NAME = "UsingInNS" OUTPUT_DIRECTORY = using_in_ns GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = using_in_ns.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ================================================ FILE: examples/specific/using_in_ns.h ================================================ // // When declaring a type using a "using" directive inside a namespace, // Doxygen adds a spurious "typedef" in the corresponding XML definition // // $ doxygen --version // 1.8.11 // namespace foo { using foo_int = int; // using foo::foo_int = typedef int } using global_int = int; // using global_int = int ================================================ FILE: examples/specific/xrefsect.cfg ================================================ PROJECT_NAME = "Doxygen xrefsect" OUTPUT_DIRECTORY = xrefsect GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO INPUT = xrefsect.h QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES ALIASES = "xrefsample=\xrefitem xrefsample \"xref Sample\" \"xref Sample\" " ================================================ FILE: examples/specific/xrefsect.h ================================================ /** * @file xrefsect.h * A few examples of xrefsect items support. */ /** * An example of using Doxygen's todo command. * * @todo Implement this function. */ int unimplemented(void); /** * An example of using Doxygen's bug and test commands. * * @bug Does not work yet. * * @test Add proper unit testing first. */ void buggy_function(int param); /** * An example of using Doxygen's deprecated command. * * @deprecated Should not be used on new code. */ void old_function(void); /** * An example of a custom Doxygen xrefitem declared as an ALIAS. * * @xrefsample This text shows up in the xref output. */ void sample_xrefitem_function(void); ================================================ FILE: make.bat ================================================ @ECHO OFF set GENERATED_MOD="breathe\_parser.py" if "%1" == "html" goto html if "%1" == "pdf" goto pdf if "%1" == "data" goto data if "%1" == "clean" goto clean if "%1" == "distclean" goto distclean if "%1" == "test" goto test if "%1" == "dev-test" goto dev-test if "%1" == "ruff" goto ruff if "%1" == "type-check" goto type-check if "%1" == "version-check" goto version-check if "%1" == "%GENERATED_MOD%" goto _parser if "%1" == "all" goto all goto end :html call :data cd documentation call make.bat html cd .. goto end :pdf call :data cd documentation call make.bat latexpdf cd .. goto end :data cd examples\doxygen call make.bat all cd ..\.. cd examples\tinyxml call make.bat all cd ..\.. cd examples\specific call make.bat all cd ..\.. goto end :clean cd examples\doxygen call make.bat clean cd ..\.. cd examples\tinyxml call make.bat clean cd ..\.. cd examples\specific call make.bat clean cd ..\.. if exist "%GENERATED_MOD%" ( echo Removing file: %GENERATED_MOD% del "%DIR%" ) goto end :distclean call :clean cd documentation call make.bat clean cd .. goto end :test cd tests python -m pytest -v cd .. goto end :dev-test call :_parser cd tests set PYTHONPATH=..\;%PYTHONPATH% python -m pytest -v cd .. goto end :ruff ruff check ruff format goto end :type-check mypy --warn-redundant-casts --warn-unused-ignores breathe tests goto end :version-check call :_parser set PYTHONPATH=..\;%PYTHONPATH% python scripts\version-check.py goto end :_parser echo Generating %GENERATED_MOD% from xml_parser_generator\schema.json python xml_parser_generator\setuptools_builder.py goto end :all call :html call :pdf goto end :end ================================================ FILE: pyproject.toml ================================================ [build-system] requires = ["flit_core>=3.7"] build-backend = "flit_core.buildapi" # project metadata [project] name = "breathe" description = "Sphinx Doxygen renderer" readme = "README.rst" license.text = "BSD-3-Clause" requires-python = ">=3.9" # Classifiers list: https://pypi.org/classifiers/ classifiers = [ "Development Status :: 5 - Production/Stable", "Environment :: Console", "Environment :: Web Environment", "Framework :: Sphinx", "Framework :: Sphinx :: Extension", "Intended Audience :: Developers", "Intended Audience :: Education", "License :: OSI Approved :: BSD License", "Operating System :: OS Independent", "Programming Language :: Python", "Programming Language :: Python :: 3", "Programming Language :: Python :: 3 :: Only", "Programming Language :: Python :: 3.9", "Programming Language :: Python :: 3.10", "Programming Language :: Python :: 3.11", "Programming Language :: Python :: 3.12", "Programming Language :: Python :: 3.13", "Topic :: Documentation", "Topic :: Documentation :: Sphinx", "Topic :: Software Development", "Topic :: Software Development :: Documentation", "Topic :: Text Processing", "Topic :: Utilities", ] dependencies = [ "Sphinx>=7.2", ] dynamic = ["version"] [project.optional-dependencies] build = [ "setuptools>=80.9.0" ] docs = [ "furo", "sphinx-copybutton", "sphinxcontrib-spelling", ] lint = [ "ruff==0.9.2", "mypy>=1", "types-docutils", "types-Pygments", "pytest>=8.0", # for mypy "sphinxcontrib-phpdomain", # for mypy # The API that we depend on is only implemented in rogerbarton's fork of sphinx-csharp # and not in the one that is published to PyPI. But we can't publish Breathe to PyPI with # a listed git dependency so we comment it out for the moment # "sphinx-csharp @ git+https://github.com/rogerbarton/sphinx-csharp.git", # for mypy ] test = [ "pytest>=8.0", "Sphinx[test]" ] [project.urls] Changelog = "https://github.com/breathe-doc/breathe/blob/main/CHANGELOG.rst" Code = "https://github.com/breathe-doc/breathe/" Download = "https://pypi.org/project/breathe/" Documentation = "https://breathe.readthedocs.io/" Homepage = "https://www.breathe-doc.org/" "Issue tracker" = "https://github.com/breathe-doc/breathe/issues" [[project.authors]] name = "Michael Jones" email = "m.pricejones@gmail.com" [project.scripts] breathe-apidoc = "breathe.apidoc:main" [tool.flit.module] name = "breathe" [tool.flit.sdist] include = [ "LICENSE", "CHANGELOG.rst", "CONTRIBUTING.rst", "CONTRIBUTORS.rst", # Documentation "documentation/", # Tests "tests/", # Utilities "Makefile", "mkrelease", ] exclude = [ "documentation/build", ] [tool.mypy] files = [ "breathe", "examples", "tests", ] show_column_numbers = true show_error_context = true python_version = "3.9" warn_unused_configs = true warn_redundant_casts = true warn_unused_ignores = true # TODO: Remove if possible [[tool.mypy.overrides]] module = [ "breathe.parser.compound", "breathe.parser.compoundsuper", "breathe.parser.index", "breathe.parser.indexsuper", ] ignore_errors = true ================================================ FILE: scripts/doxygen_cache.py ================================================ """Run Doxygen on all test samples and save the results.""" from __future__ import annotations import os import pathlib import shutil import subprocess from breathe.process import AUTOCFG_TEMPLATE PROJECT_DIR = pathlib.Path(__file__).parent.parent DATA_DIR = PROJECT_DIR / "tests" / "data" EXAMPLES_DIR = DATA_DIR / "examples" CACHE_DIR = EXAMPLES_DIR / "_cache" def run_one(p, name, template, exec): print(f"generating output for {name}") os.chdir(p) out_dir = CACHE_DIR / name out_dir.mkdir(exist_ok=True) doxyfile = out_dir / "Doxyfile" doxycontent = template.format(output=out_dir) extra_opts = pathlib.Path("extra_dox_opts.txt") if extra_opts.exists(): doxycontent += extra_opts.read_text(encoding="utf-8") doxyfile.write_text(doxycontent, encoding="utf-8") subprocess.run([exec, doxyfile], check=True) def make_cache(): template = (EXAMPLES_DIR / "doxyfile_template").read_text(encoding="utf-8") exec = shutil.which("doxygen") if exec is None: raise ValueError("cannot find doxygen executable") CACHE_DIR.mkdir(exist_ok=True) prev_dir = os.getcwd() r = subprocess.run( [exec, "--version"], check=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, text=True ) (CACHE_DIR / "version.txt").write_text(r.stdout, encoding="utf-8") try: for p in EXAMPLES_DIR.glob("test_*"): run_one(p, p.name, template, exec) print("generating output for auto") os.chdir(DATA_DIR / "auto") out_dir = CACHE_DIR / "auto" out_dir.mkdir(exist_ok=True) doxyfile = out_dir / "Doxyfile" doxyfile.write_text( AUTOCFG_TEMPLATE.format( project_name="example", output_dir=str(out_dir), input='"auto_class.h" "auto_function.h"', extra="", ), encoding="utf-8", ) subprocess.run([exec, doxyfile], check=True) for c in "AB": run_one(DATA_DIR / "multi_project" / c, f"multi_project.{c}", template, exec) finally: os.chdir(prev_dir) if __name__ == "__main__": make_cache() ================================================ FILE: scripts/generate_tests_results.py ================================================ """For any test that is missing the "compare.xml" file, run the test, strip the output of certain attributes and save the results as "compare_draft.xml" in the folder where "compare.xml" belongs. The resulting file will need to be inspected manually to make sure it is actually correct. Then it can be saved as "compare.xml". The output file omits a number of attributes from the docutils XML format that are either unimportant for ensuring correctness of Breathe or has a value that depends on an unimportant factor, such as the exact version of Sphinx, or the directories of input files. """ from __future__ import annotations import os import os.path import pathlib import shutil import subprocess import tempfile import docutils.nodes import docutils.writers.docutils_xml from sphinx.testing.util import SphinxTestApp TEST_DATA_DIR = pathlib.Path(__file__).parent.parent / "tests" / "data" CSS_PATH = TEST_DATA_DIR / "docutils.css" C_FILE_SUFFIXES = frozenset((".h", ".c", ".hpp", ".cpp")) # if either of these are changed, tests/data/examples/README.rst should be # updated IGNORED_ELEMENTS = frozenset(()) IGNORED_ATTRIBUTES = frozenset(( "ids", "names", "no-contents-entry", "no-index", "no-index-entry", "no-typesetting", "nocontentsentry", "noindex", "noindexentry", "is_multiline", "multi_line_parameter_list", "add_permalink", "xml:space", "source", "translation_progress", "options", "original_uri", "_toc_name", "_toc_parts", "xmlns:c", "xmlns:changeset", "xmlns:citation", "xmlns:cpp", "xmlns:index", "xmlns:js", "xmlns:math", "xmlns:py", "xmlns:rst", "xmlns:std", )) DEFAULT_CONF = { "project": "test", "breathe_default_project": "example", "breathe_show_include": False, "extensions": ["breathe", "sphinx.ext.graphviz"], } def attlist(self): return sorted( item for item in self.non_default_attributes().items() if item[0] not in IGNORED_ATTRIBUTES ) docutils.nodes.Element.attlist = attlist class Translator(docutils.writers.docutils_xml.XMLTranslator): doctype = "" def __init__(self, document): super().__init__(document) self.ignore = 0 def default_visit(self, node): if self.ignore or node.tagname in IGNORED_ELEMENTS: self.ignore += 1 else: super().default_visit(node) def default_departure(self, node): if self.ignore: self.ignore -= 1 else: super().default_departure(node) def visit_Text(self, node): if not self.ignore: super().visit_Text(node) def depart_Text(self, node): if not self.ignore: super().depart_Text(node) class DirChange: def __init__(self, path): self.path = path self._old_path = os.getcwd() def __enter__(self): os.chdir(self.path) def __exit__(self, *args): os.chdir(self._old_path) class TmpDir: """A wrapper for tempfile.TemporaryDirectory that returns an instance of pathlib.Path in its __enter__ method""" def __init__(self, *args, **kwds): self.base = tempfile.TemporaryDirectory(*args, **kwds) def __enter__(self): return pathlib.Path(self.base.__enter__()) def __exit__(self, *args): self.base.__exit__(*args) def get_individual_tests(): return (TEST_DATA_DIR / "examples").glob("test_*") def filter_c_files(input_dir): for p in input_dir.iterdir(): if p.suffix in C_FILE_SUFFIXES: full = str(p) if '"' in full: raise ValueError("quotations marks not allowed in path names") yield f'"{full}"' def conf_overrides(extra): conf = DEFAULT_CONF.copy() conf.update(extra) return conf class Doxygen: def __init__(self): exe = shutil.which("doxygen") if exe is None: raise ValueError("cannot find doxygen executable") self.exe = exe self.template = (TEST_DATA_DIR / "examples" / "doxyfile_template").read_text( encoding="utf-8" ) def run_one(self, tmp_path, outname): doxyfile = tmp_path / "Doxyfile" doxycontent = self.template.format(output=tmp_path) extra_opts = pathlib.Path("extra_dox_opts.txt") if extra_opts.exists(): doxycontent += extra_opts.read_text(encoding="utf-8") doxyfile.write_text(doxycontent, encoding="utf-8") subprocess.run([self.exe, doxyfile], check=True) if outname != "xml": os.rename(tmp_path / "xml", tmp_path / outname) def run_sphinx_and_copy_output(tmp_path, input_path, overrides): dest = tmp_path / "conf.py" ec = pathlib.Path("extra_conf.py") if ec.exists(): shutil.copyfile(ec, dest) else: dest.touch() app = SphinxTestApp(buildername="xml", srcdir=tmp_path, confoverrides=conf_overrides(overrides)) app.set_translator("xml", Translator, True) app.build() app.cleanup() # shutil.copyfile(tmp_path / '_build' / 'xml' / 'index.xml', input_path / 'compare_draft.xml') # copy index.xml to compare_draft.xml with an extra stylesheet line with ( open(tmp_path / "_build" / "xml" / "index.xml", encoding="utf-8") as f_in, open(input_path / "compare_draft.xml", "w", encoding="utf-8") as f_out, ): itr_in = iter(f_in) line = next(itr_in) assert line assert line.startswith("\n') line = next(itr_in) # the next line is a comment with the docutils version, which we don't # need and is misleading because the output is altered by us if not line.startswith(" class AutoClassTest class outside of namespace Private Functions inline void member non-namespaced class function inline void anotherMember non-namespaced class other function Functions void autoFunction non-namespaced class function void anotherAutoFunction non-namespaced class other function ================================================ FILE: tests/data/auto/input.rst ================================================ .. autodoxygenfile:: auto_class.h .. autodoxygenfile:: auto_function.h ================================================ FILE: tests/data/classSample.xml ================================================ Sample sample.hpp int int public_field public_field Sample::public_field Something int int protected_field protected_field Sample::protected_field int int private_field private_field Sample::private_field Something int int public_method (int x) public_method Sample::public_method int x int int protected_method (int x) protected_method Sample::protected_method int x Something int int private_method (int x) private_method Sample::private_method int x Sampleprivate_field Sampleprivate_method Sampleprotected_field Sampleprotected_method Samplepublic_field Samplepublic_method ================================================ FILE: tests/data/docutils.css ================================================ /* A style sheet for the "compare.xml" files in the example tests. This lets the files be viewed in a browser and makes them much easier to inspect manually. */ document { display: block; } desc { display: block; margin-bottom: 1em; } desc_content { display: block; margin-left: 2em; } desc_signature_line { display: block; } desc_sig_keyword { color: rgb(9, 9, 167); } desc_sig_keyword_type { color: rgb(57, 93, 160); } desc_name { color: darkcyan; } paragraph { display: block; margin: 1em 0; } emphasis { font-style: italic; } strong { font-weight: bold; } comment { color: darkgreen; } index::before { display: block; content: attr(entries); color: gray; font-style: italic; } desc_parameterlist::before { content: "("; } desc_parameterlist::after { content: ")"; } desc_parameter + desc_parameter::before { content: ", "; } table tgroup { display: table; } table thead { display: table-header-group; } table tbody { display: table-row-group; } table row { display: table-row; } table entry { display: table-cell; padding: 0 0.3em; } colspec { display: none; } section { display: block; } section title { font-size: 1.5em; display: block; font-weight: bold; } section section title { font-size: 1.17em; } section section section title { font-size: 1em; } ================================================ FILE: tests/data/ellipsis.xml ================================================ double double amici::spline_pos (double t, int num,...) spline_pos double t int num ... ================================================ FILE: tests/data/examples/README.rst ================================================ tests/data/examples ============================ Each direct child folder of "tests/data/examples", that starts with "test\_", contains a test. The tests are read and run by "tests/test_examples.py" when run by pytest. Each test works by first creating an input file for Doxygen based on the template "tests/data/examples/doxyfile_template", in a temporary folder. If a file named "extra_dox_opts.txt" exists in the test folder, its contents are appended to the input file. Doxygen is run with the current directory set to the test folder, thus any source code files are automatically read by Doxygen. The output is stored in the temporary folder. Sphinx is run on "input.rst" with the following options: .. code-block:: python project = "test" breathe_default_project = "example" breathe_show_include = False extensions = ["breathe", "sphinx.ext.graphviz"] breathe_projects = {"example": SOME_TEMPORARY_FOLDER} plus any options specified by "extra_conf.py", if such a file exists in the test folder. The output format is set to XML. The test folder contains "compare.xml" and may contain one or more "compare-{version}.xml" files, where ``{version}`` is a dotted version number. The version number is compared to the version of Doxygen being used. The file with the greatest version number that is not greater than the version of Doxygen, is used, or "compare.xml" is used if no such file exists. The output of Sphinx and the "compare" file are compared as a series of start tags, end tags and text nodes. Other kinds of content are ignored. The contents of the files must match, except the output can have attributes that do not appear in the "compare" file, as long as every attribute that is in the "compare" file is also in the output (the order of the attributes does not matter); and the text nodes have all leading and trailing whitespace stripped before being compared. Certain attributes in Sphinx's XML output are either irrelevant or are dependant on unimportant factors, such as the location of the input file or the version of docutils. Thus, the "compare" files are always stripped of the following attributes: - ``ids`` - ``names`` - ``no-contents-entry`` - ``no-index`` - ``no-index-entry`` - ``no-typesetting`` - ``nocontentsentry`` - ``noindex`` - ``noindexentry`` - ``is_multiline`` - ``multi_line_parameter_list`` - ``add_permalink`` - ``xml:space`` - ``source`` - ``translation_progress`` - ``options`` - ``original_uri`` - ``_toc_name`` - ``_toc_parts`` - ``xmlns:c`` - ``xmlns:changeset`` - ``xmlns:citation`` - ``xmlns:cpp`` - ``xmlns:index`` - ``xmlns:js`` - ``xmlns:math`` - ``xmlns:py`` - ``xmlns:rst`` - ``xmlns:std`` Writing the "compare" files by hand is tedious and error-prone. It is far easier to simply run Sphinx, check that it is correct (if it's not, fix the problem and run it again) and take out the unneeded attributes. To this end: the script "scripts/generate_test_results.py" exists. When run, for each test, it checks if "compare.xml" exists. If it doesn't, it creates "compare_draft.xml", by running Sphinx, taking the XML output and stripping the unneeded attributes. Checking that the generated "compare_draft.xml" is correct is also tedious, thus it includes a link to a style sheet ("tests/data/docutils.css"). This allows the file to be opened in a browser (I have only tested Firefox and Chrome) and is made much more readable. ================================================ FILE: tests/data/examples/doxyfile_template ================================================ PROJECT_NAME = "example" HAVE_DOT = YES DOTFILE_DIRS = "." GENERATE_LATEX = NO GENERATE_MAN = NO GENERATE_RTF = NO CASE_SENSE_NAMES = NO OUTPUT_DIRECTORY = "{output}" IMAGE_PATH = "." QUIET = YES JAVADOC_AUTOBRIEF = YES GENERATE_HTML = NO GENERATE_XML = YES WARN_IF_UNDOCUMENTED = NO ALIASES = "rst=\verbatim embed:rst" ALIASES += "endrst=\endverbatim" ALIASES += "inlinerst=\verbatim embed:rst:inline" ================================================ FILE: tests/data/examples/test_alias/alias.h ================================================ /*! @file alias.h */ /** * Foo frob routine. * \par bob this something else * @sideeffect Frobs any foos. * * \par bob this something else * * @sideeffect Frobs any foos. * * @param[out] Frobs any foos. */ void frob_foos(void* Frobs); ================================================ FILE: tests/data/examples/test_alias/compare.xml ================================================ Functions void frob_foosvoid *Frobs Foo frob routine. bob this something elseSide EffectsFrobs any foos.bob this something elseSide EffectsFrobs any foos. Parameters Frobs[out] any foos. ================================================ FILE: tests/data/examples/test_alias/extra_dox_opts.txt ================================================ ALIASES = "sideeffect=\par Side Effects^^" ================================================ FILE: tests/data/examples/test_alias/input.rst ================================================ .. doxygenfile:: alias.h ================================================ FILE: tests/data/examples/test_array/array.h ================================================ /** My function */ int foo(int a[5]); /** My other function * * @test This declaration is supposed to be * @code{.c} * int bar(int n, int a[static n]); * @endcode * But, Sphinx fails to recognize `int a[static n])` as a C specific array syntax */ int bar(int n, int a[]); ================================================ FILE: tests/data/examples/test_array/compare.xml ================================================ int fooint a[5] My function. int barint nint a[] My other function. Test:This declaration is supposed to be int bar(int n, int a[static n]); But, Sphinx fails to recognize int a[static n]) as a C specific array syntax ================================================ FILE: tests/data/examples/test_array/input.rst ================================================ .. doxygenfunction:: foo .. doxygenfunction:: bar ================================================ FILE: tests/data/examples/test_c_enum/c_enum.h ================================================ // Example of a enum in C which has different syntax and different support in Sphinx to the C++ enum /** * Backup data. * * \ingroup Backup */ typedef enum { /** * Compatibility with old gboolean used instead of format. * * File type is guessed for extension, non unicode format used * for Gammu backup. */ GSM_Backup_Auto = 0, /** * Compatibility with old gboolean used instead of format. * * File type is guessed for extension, unicode format used * for Gammu backup. */ GSM_Backup_AutoUnicode = 1, /** * LMB format, compatible with Logo manager, can store * phonebooks and logos. */ GSM_Backup_LMB, /** * vCalendar standard, can store todo and calendar entries. */ GSM_Backup_VCalendar, /** * vCard standard, can store phone phonebook entries. */ GSM_Backup_VCard, /** * LDIF (LDAP Data Interchange Format), can store phone * phonebook entries. */ GSM_Backup_LDIF, /** * iCalendar standard, can store todo and calendar entries. */ GSM_Backup_ICS, /** * Gammu own format can store almost anything from phone. * * This is ASCII version of the format, Unicode strings are HEX * encoded. Use GSM_Backup_GammuUCS2 instead if possible. */ GSM_Backup_Gammu, /** * Gammu own format can store almost anything from phone. * * This is UCS2-BE version of the format. */ GSM_Backup_GammuUCS2, /** * vNote standard, can store phone notes. */ GSM_Backup_VNote, } GSM_BackupFormat; ================================================ FILE: tests/data/examples/test_c_enum/compare.xml ================================================ enum GSM_BackupFormat Backup data. Values: enumerator GSM_Backup_Auto Compatibility with old gboolean used instead of format. File type is guessed for extension, non unicode format used for Gammu backup. enumerator GSM_Backup_AutoUnicode Compatibility with old gboolean used instead of format. File type is guessed for extension, unicode format used for Gammu backup. enumerator GSM_Backup_LMB LMB format, compatible with Logo manager, can store phonebooks and logos. enumerator GSM_Backup_VCalendar vCalendar standard, can store todo and calendar entries. enumerator GSM_Backup_VCard vCard standard, can store phone phonebook entries. enumerator GSM_Backup_LDIF LDIF (LDAP Data Interchange Format), can store phone phonebook entries. enumerator GSM_Backup_ICS iCalendar standard, can store todo and calendar entries. enumerator GSM_Backup_Gammu Gammu own format can store almost anything from phone. This is ASCII version of the format, Unicode strings are HEX encoded. Use GSM_Backup_GammuUCS2 instead if possible. enumerator GSM_Backup_GammuUCS2 Gammu own format can store almost anything from phone. This is UCS2-BE version of the format. enumerator GSM_Backup_VNote vNote standard, can store phone notes. enumerator GSM_Backup_Auto Compatibility with old gboolean used instead of format. File type is guessed for extension, non unicode format used for Gammu backup. enumerator GSM_Backup_AutoUnicode Compatibility with old gboolean used instead of format. File type is guessed for extension, unicode format used for Gammu backup. enumerator GSM_Backup_LMB LMB format, compatible with Logo manager, can store phonebooks and logos. enumerator GSM_Backup_VCalendar vCalendar standard, can store todo and calendar entries. enumerator GSM_Backup_VCard vCard standard, can store phone phonebook entries. enumerator GSM_Backup_LDIF LDIF (LDAP Data Interchange Format), can store phone phonebook entries. enumerator GSM_Backup_ICS iCalendar standard, can store todo and calendar entries. enumerator GSM_Backup_Gammu Gammu own format can store almost anything from phone. This is ASCII version of the format, Unicode strings are HEX encoded. Use GSM_Backup_GammuUCS2 instead if possible. enumerator GSM_Backup_GammuUCS2 Gammu own format can store almost anything from phone. This is UCS2-BE version of the format. enumerator GSM_Backup_VNote vNote standard, can store phone notes. ================================================ FILE: tests/data/examples/test_c_enum/input.rst ================================================ .. doxygenenum:: GSM_BackupFormat .. doxygenenumvalue:: GSM_Backup_Auto .. doxygenenumvalue:: GSM_Backup_AutoUnicode .. doxygenenumvalue:: GSM_Backup_LMB .. doxygenenumvalue:: GSM_Backup_VCalendar .. doxygenenumvalue:: GSM_Backup_VCard .. doxygenenumvalue:: GSM_Backup_LDIF .. doxygenenumvalue:: GSM_Backup_ICS .. doxygenenumvalue:: GSM_Backup_Gammu .. doxygenenumvalue:: GSM_Backup_GammuUCS2 .. doxygenenumvalue:: GSM_Backup_VNote ================================================ FILE: tests/data/examples/test_c_file/c_file.h ================================================ /* Borrowed from git "cache-tree.h" as an example of C code */ #ifndef CACHE_TREE_H #define CACHE_TREE_H #include "tree.h" #include "tree-walk.h" struct cache_tree; struct cache_tree_sub { struct cache_tree *cache_tree; int namelen; int used; char name[FLEX_ARRAY]; }; struct cache_tree { int entry_count; /* negative means "invalid" */ unsigned char sha1[20]; int subtree_nr; int subtree_alloc; struct cache_tree_sub **down; }; /** Shared cache tree instance. */ extern struct cache_tree global_cache_tree; struct cache_tree *cache_tree(void); extern void cache_tree_free(struct cache_tree **); void cache_tree_invalidate_path(struct cache_tree *, const char *); struct cache_tree_sub *cache_tree_sub(struct cache_tree *, const char *); void cache_tree_write(struct strbuf *, struct cache_tree *root); struct cache_tree *cache_tree_read(const char *buffer, unsigned long size); int cache_tree_fully_valid(struct cache_tree *); int cache_tree_update(struct cache_tree *, struct cache_entry **, int, int, int); /** bitmasks to write_cache_as_tree flags */ #define WRITE_TREE_MISSING_OK 1 #define WRITE_TREE_IGNORE_CACHE_TREE 2 /** error return codes */ #define WRITE_TREE_UNREADABLE_INDEX (-1) #define WRITE_TREE_UNMERGED_INDEX (-2) #define WRITE_TREE_PREFIX_ERROR (-3) int write_cache_as_tree(unsigned char *sha1, int flags, const char *prefix); void prime_cache_tree(struct cache_tree **, struct tree *); extern int cache_tree_matches_traversal(struct cache_tree *, struct name_entry *ent, struct traverse_info *info); #endif ================================================ FILE: tests/data/examples/test_c_file/compare.xml ================================================ Defines WRITE_TREE_MISSING_OK bitmasks to write_cache_as_tree flags WRITE_TREE_IGNORE_CACHE_TREE WRITE_TREE_UNREADABLE_INDEX error return codes WRITE_TREE_UNMERGED_INDEX WRITE_TREE_PREFIX_ERROR Functions struct cache_tree *cache_treevoid void cache_tree_freestruct cache_tree** void cache_tree_invalidate_pathstruct cache_tree*const char* struct cache_tree_sub *cache_tree_substruct cache_tree*const char* void cache_tree_writestruct strbuf*struct cache_tree *root struct cache_tree *cache_tree_readconst char *bufferunsigned long size int cache_tree_fully_validstruct cache_tree* int cache_tree_updatestruct cache_tree*struct cache_entry**intintint int write_cache_as_treeunsigned char *sha1int flagsconst char *prefix void prime_cache_treestruct cache_tree**struct tree* int cache_tree_matches_traversalstruct cache_tree*struct name_entry *entstruct traverse_info *info Variables struct cache_tree global_cache_tree Shared cache tree instance. struct cache_tree_sub struct cache_tree ================================================ FILE: tests/data/examples/test_c_file/input.rst ================================================ .. doxygenfile:: c_file.h ================================================ FILE: tests/data/examples/test_class/class.cpp ================================================ #include "class.h" /*! More documentation in the impl file */ void ClassTest::function(int myIntParameter) { } /*! More documentation in the impl file */ void ClassTest::anotherFunction() { } ================================================ FILE: tests/data/examples/test_class/class.h ================================================ #include namespace TestNamespaceClasses { //! \brief first class inside of namespace class NamespacedClassTest { public: //! \brief namespaced class function virtual void function() const = 0; static void functionS(); explicit NamespacedClassTest() {} //! \brief namespaced class other function void anotherFunction() {} }; //! \brief second class inside of namespace class ClassTest { public: //! \brief second namespaced class function void function() {} //! \brief second namespaced class other function void anotherFunction() {} }; } // TestNamespaceClasses //! \brief class outside of namespace class OuterClass { public: //! \brief inner class class InnerClass {}; }; //! \brief class outside of namespace class ClassTest { public: /*! \brief non-namespaced class function More details in the header file. */ void function(int myParameter); //! \brief non-namespaced class other function void anotherFunction(); //! \brief namespaced class function virtual void publicFunction() const = 0; virtual void undocumentedPublicFunction() const = 0; //! A public class class PublicClass {}; class UndocumentedPublicClass {}; //! A public struct struct PublicStruct {}; struct UndocumentedPublicStruct {}; protected: //! A protected function void protectedFunction() {} void undocumentedProtectedFunction() {} //! A protected class class ProtectedClass {}; class UndocumentedProtectedClass {}; //! A protected struct struct ProtectedStruct {}; struct UndocumentedProtectedStruct {}; private: //! This is a private function virtual void privateFunction() const = 0; virtual void undocumentedPrivateFunction() const = 0; //! A private class class PrivateClass {}; class UndocumentedPrivateClass {}; //! A private struct struct PrivateStruct {}; struct UndocumentedPrivateStruct {}; }; template void f0(); template<> void f0(); namespace NS1 { template void f1(); template<> void f1(); namespace NS2 { template void f2(); template<> void f2(); } // namespace NS2 } // namespace NS1 ================================================ FILE: tests/data/examples/test_class/compare.xml ================================================ Functions template<typename T>void f0 template<>void f0<std::string> class OuterClass class outside of namespace class InnerClass inner class class ClassTest class outside of namespace Public Functions void functionint myParameter non-namespaced class function More details in the header file. More documentation in the impl file void anotherFunction non-namespaced class other function More documentation in the impl file virtual void publicFunction const = 0 namespaced class function virtual void undocumentedPublicFunction const = 0 Protected Functions inline void protectedFunction A protected function. inline void undocumentedProtectedFunction Private Functions virtual void privateFunction const = 0 This is a private function. virtual void undocumentedPrivateFunction const = 0 class PrivateClass A private class. struct PrivateStruct A private struct. class ProtectedClass A protected class. struct ProtectedStruct A protected struct. class PublicClass A public class. struct PublicStruct A public struct. class UndocumentedPrivateClass struct UndocumentedPrivateStruct class UndocumentedProtectedClass struct UndocumentedProtectedStruct class UndocumentedPublicClass struct UndocumentedPublicStruct namespace TestNamespaceClasses class ClassTest second class inside of namespace Public Functions inline void function second namespaced class function inline void anotherFunction second namespaced class other function class NamespacedClassTest first class inside of namespace Public Functions virtual void function const = 0 namespaced class function inline explicit NamespacedClassTest inline void anotherFunction namespaced class other function Public Static Functions static void functionS namespace NS1 Functions template<typename T>void f1 template<>void f1<std::string> namespace NS2 Functions template<typename T>void f2 template<>void f2<std::string> ================================================ FILE: tests/data/examples/test_class/input.rst ================================================ .. doxygenfile:: class.h ================================================ FILE: tests/data/examples/test_code_blocks/code_blocks.h ================================================ /** A function with an unannotated code block with C/C++ code. * * @code * char* buffer = new char[42]; * int charsAdded = sprintf(buffer, "Tabs are normally %d spaces\n", 8); * @endcode */ void with_standard_code_block(); /** A function with an unannotated code block with non-C/C++ code. * * @code * set(user_list A B C) * foreach(element ${user_list}) * message(STATUS "Element is ${element}") * endforeach() * @endcode * * Another code-block that explicitly remains not highlighted. * @code{.unparsed} * Show this as is. * @endcode */ void with_unannotated_cmake_code_block(); /** A function with an annotated cmake code block. * * @code{.cmake} * set(user_list A B C) * foreach(element ${user_list}) * message(STATUS "Element is ${element}") * endforeach() * @endcode */ void with_annotated_cmake_code_block(); ================================================ FILE: tests/data/examples/test_code_blocks/compare.xml ================================================ Functions void with_standard_code_block A function with an unannotated code block with C/C++ code. char* buffer = new char[42]; int charsAdded = sprintf(buffer, "Tabs are normally %d spaces\n", 8); void with_unannotated_cmake_code_block A function with an unannotated code block with non-C/C++ code. set(user_list A B C) foreach(element ${user_list}) message(STATUS "Element is ${element}") endforeach() Another code-block that explicitly remains not highlighted. Show this as is. void with_annotated_cmake_code_block A function with an annotated cmake code block. set(user_list A B C) foreach(element ${user_list}) message(STATUS "Element is ${element}") endforeach() ================================================ FILE: tests/data/examples/test_code_blocks/input.rst ================================================ .. doxygenfile:: code_blocks.h ================================================ FILE: tests/data/examples/test_cpp_concept/compare.xml ================================================ template<typename T>concept Hashable ================================================ FILE: tests/data/examples/test_cpp_concept/cpp_concept.h ================================================ template concept Hashable = requires(T a) { { std::hash{}(a) } -> std::convertible_to; }; ================================================ FILE: tests/data/examples/test_cpp_concept/input.rst ================================================ .. doxygenconcept:: Hashable ================================================ FILE: tests/data/examples/test_cpp_enum/compare.xml ================================================ enum Unscoped Values: enumerator UnscopedEnumerator enum class ScopedStruct : int Values: enumerator Enumerator enum class ScopedClass : int Values: enumerator Enumerator enum class ScopedClassNoUnderlying Values: enumerator Enumerator ================================================ FILE: tests/data/examples/test_cpp_enum/cpp_enum.h ================================================ enum Unscoped : int { UnscopedEnumerator = 42 }; enum struct ScopedStruct : int { Enumerator = 42 }; enum class ScopedClass : int { Enumerator = 42 }; enum class ScopedClassNoUnderlying { Enumerator = 42 }; ================================================ FILE: tests/data/examples/test_cpp_enum/input.rst ================================================ .. doxygenenum:: Unscoped .. doxygenenum:: ScopedStruct .. doxygenenum:: ScopedClass .. doxygenenum:: ScopedClassNoUnderlying ================================================ FILE: tests/data/examples/test_cpp_friendclass/compare.xml ================================================ struct A struct B struct C Friends friend class A friend struct B ================================================ FILE: tests/data/examples/test_cpp_friendclass/cpp_friendclass.h ================================================ struct A {}; struct B {}; struct C { friend class A; friend struct B; }; ================================================ FILE: tests/data/examples/test_cpp_friendclass/input.rst ================================================ .. doxygenstruct:: A .. doxygenstruct:: B .. doxygenstruct:: C :members: :undoc-members: ================================================ FILE: tests/data/examples/test_cpp_function/compare.xml ================================================ struct Foo struct Class Public Functions virtual void f1 volatile const & = 0 virtual void f2 volatile const && = 0 int f_issue_338 noexcept int anon_paramsintintint xchar* Public Members void (*f_issue_489)(struct Foo *foo, int value) Public Static Functions static void f3 namespace TestNamespaceFunction A namespace to demonstrate a namespaced function. Functions void namespaceFunc A function within a namspace. ================================================ FILE: tests/data/examples/test_cpp_function/cpp_function.h ================================================ struct Foo{}; struct Class { virtual void f1() const volatile & = 0; virtual void f2() const volatile && = 0; static void f3(); void (*f_issue_489)(struct Foo *foo, int value); int f_issue_338() noexcept; int anon_params(int, int, int x, char*); }; /** A namespace to demonstrate a namespaced function */ namespace TestNamespaceFunction { /** A function within a namspace. */ void namespaceFunc(); } ================================================ FILE: tests/data/examples/test_cpp_function/input.rst ================================================ .. doxygenfile:: cpp_function.h ================================================ FILE: tests/data/examples/test_cpp_inherited_members/compare.xml ================================================ class Base Base class. Subclassed by A, B Public Functions void f_issue_356 Base-class member function. class A : public Base Class A. Public Functions void f_issue_356 Base-class member function. class B : public Base Class B. Public Functions void f_issue_356 Base-class member function. ================================================ FILE: tests/data/examples/test_cpp_inherited_members/cpp_inherited_members.h ================================================ /** * @file */ /// Base class class Base { public: /// Base-class member function void f_issue_356(); }; /// Class A class A : public Base {}; /// Class B class B : public Base {}; ================================================ FILE: tests/data/examples/test_cpp_inherited_members/extra_dox_opts.txt ================================================ INLINE_INHERITED_MEMB = YES ================================================ FILE: tests/data/examples/test_cpp_inherited_members/input.rst ================================================ .. doxygenfile:: cpp_inherited_members.h ================================================ FILE: tests/data/examples/test_cpp_trailing_return_type/compare-1.11.0.xml ================================================ Functions Thingy *f_issue_441 Function that creates a thingy. class Thingy needed for references in global function return type ================================================ FILE: tests/data/examples/test_cpp_trailing_return_type/compare.xml ================================================ Functions auto f_issue_441 -> Thingy* Function that creates a thingy. class Thingy needed for references in global function return type ================================================ FILE: tests/data/examples/test_cpp_trailing_return_type/cpp_trailing_return_type.h ================================================ /*! \file cpp_trailing_return_type.h */ /*! needed for references in global function return type */ class Thingy {}; //! \brief Function that creates a thingy. auto f_issue_441() -> Thingy*; ================================================ FILE: tests/data/examples/test_cpp_trailing_return_type/input.rst ================================================ .. doxygenfile:: cpp_trailing_return_type.h ================================================ FILE: tests/data/examples/test_define/compare.xml ================================================ USE_STUFF A simple define without a value. MAX_LENGTH A define with a simple value. MAXIMUMAB A define with some parameters. Parameters A – The parameter A B – The parameter B Returns The maximum of A and B SWAPAB A define which spans multiple lines. ================================================ FILE: tests/data/examples/test_define/define.h ================================================ /** * A simple define without a value */ #define USE_STUFF /** * A define with a simple value */ #define MAX_LENGTH 100 /** * A define with some parameters * * \param A The parameter A * \param B The parameter B * * \returns The maximum of A and B */ #define MAXIMUM(A,B) ((A > B)?(A):(B)) /** * A define which spans multiple lines */ #define SWAP(A,B) { \ (a) ^= (b); \ (b) ^= (a); \ (a) ^= (b); \ } ================================================ FILE: tests/data/examples/test_define/input.rst ================================================ .. doxygendefine:: USE_STUFF .. doxygendefine:: MAX_LENGTH .. doxygendefine:: MAXIMUM .. doxygendefine:: SWAP ================================================ FILE: tests/data/examples/test_diagrams/compare.xml ================================================ class A Inheritance diagram for A:
    Collaboration diagram for A:
    Subclassed by C, D Public Members A *m_self
    class B Inheritance diagram for B:
    Collaboration diagram for B:
    Subclassed by D Public Members A *m_a
    class C : public A Inheritance diagram for C:
    Collaboration diagram for C:
    Public Members D *m_d
    class D : protected virtual A, private B Inheritance diagram for D:
    Collaboration diagram for D:
    Subclassed by E Public Members C m_c
    class E : public D Inheritance diagram for E:
    Collaboration diagram for E:
    file diagrams_a.h This graph shows which files directly or indirectly include diagrams_a.h:
    file diagrams_b.h This graph shows which files directly or indirectly include diagrams_b.h:
    file diagrams_c.h Include dependency graph for diagrams_c.h:
    This graph shows which files directly or indirectly include diagrams_c.h:
    file diagrams_d.h Include dependency graph for diagrams_d.h:
    This graph shows which files directly or indirectly include diagrams_d.h:
    file diagrams_e.h Include dependency graph for diagrams_e.h:
    ================================================ FILE: tests/data/examples/test_diagrams/diagrams_a.h ================================================ #ifndef _DIAGRAMS_A_H #define _DIAGRAMS_A_H class A { public: A *m_self; }; #endif ================================================ FILE: tests/data/examples/test_diagrams/diagrams_b.h ================================================ #ifndef _DIAGRAMS_B_H #define _DIAGRAMS_B_H class A; class B { public: A *m_a; }; #endif ================================================ FILE: tests/data/examples/test_diagrams/diagrams_c.h ================================================ #ifndef _DIAGRAMS_C_H #define _DIAGRAMS_C_H #include "diagrams_c.h" class D; class C : public A { public: D *m_d; }; #endif ================================================ FILE: tests/data/examples/test_diagrams/diagrams_d.h ================================================ #ifndef _DIAGRAM_D_H #define _DIAGRAM_D_H #include "diagrams_a.h" #include "diagrams_b.h" class C; class D : virtual protected A, private B { public: C m_c; }; #endif ================================================ FILE: tests/data/examples/test_diagrams/diagrams_e.h ================================================ #ifndef _DIAGRAM_E_H #define _DIAGRAM_E_H #include "diagrams_d.h" class E : public D {}; #endif ================================================ FILE: tests/data/examples/test_diagrams/extra_dox_opts.txt ================================================ ENABLE_PREPROCESSING = YES ================================================ FILE: tests/data/examples/test_diagrams/input.rst ================================================ .. doxygenindex:: :allow-dot-graphs: ================================================ FILE: tests/data/examples/test_dot_graphs/compare.xml ================================================ page Dot Graph Demonstrations
    Using @dot command
    basic graph elements
    Using @dotfile command
    Captions go here
    ================================================ FILE: tests/data/examples/test_dot_graphs/dot_graphs.h ================================================ /** * @file dot_graphs.h * * @page dotgraphs Dot Graph Demonstrations * * @section dotcmd Using \@dot command * * @dot "basic graph elements" * digraph G { * bgcolor="purple:pink" label="a graph" fontcolor="white" * subgraph cluster1 { * fillcolor="blue:cyan" label="a cluster" fontcolor="white" style="filled" gradientangle="270" * node [shape=box fillcolor="red:yellow" style="filled" gradientangle=90] * "a node"; * } * } * @enddot * * @section dotfilecmd Using \@dotfile command * * @dotfile "dotfile.dot" "Captions go here" */ ================================================ FILE: tests/data/examples/test_dot_graphs/dotfile.dot ================================================ digraph G {bgcolor="red:cyan" gradientangle=0 subgraph cluster_0 { style=filled; color=lightgrey; fillcolor="blue:yellow"; gradientangle=90; node [fillcolor="yellow:green" style=filled gradientangle=270] a0; node [fillcolor="green:red"] a1; node [fillcolor="red:cyan"] a2; node [fillcolor="cyan:blue"] a3; a0 -> a1 -> a2 -> a3; label = "process #1"; } subgraph cluster_1 { node [fillcolor="yellow:magenta" style=filled gradientangle=270] b0; node [fillcolor="magenta:cyan"] b1; node [fillcolor="cyan:red"] b2; node [fillcolor="red:blue"] b3; b0 -> b1 -> b2 -> b3; label = "process #2"; color=blue fillcolor="blue:yellow"; style=filled; gradientangle=90; } start -> a0; start -> b0; a1 -> b3; b2 -> a3; a3 -> a0; a3 -> end; b3 -> end; start [shape=Mdiamond , fillcolor="yellow:brown", gradientangle=90, style=radial]; end [shape=Msquare, fillcolor="orange:blue", style=radial, gradientangle=90]; } ================================================ FILE: tests/data/examples/test_dot_graphs/input.rst ================================================ .. doxygenpage:: dotgraphs ================================================ FILE: tests/data/examples/test_group/compare-1.10.0.xml ================================================ group mygroup This is the first group Functions void groupedFunction This function is in MyGroup. class GroupedClassTest first class inside of namespace Public Functions virtual void publicFunction const = 0 namespaced class function class PublicClass A protected class. class UndocumentedPublicClass group innergroup This is an inner group class InnerGroupClassTest inner class inside of namespace Public Functions inline void function inner namespaced class function ================================================ FILE: tests/data/examples/test_group/compare.xml ================================================ group My Group This is the first group. Functions void groupedFunction This function is in MyGroup. class GroupedClassTest first class inside of namespace Public Functions virtual void publicFunction const = 0 namespaced class function class PublicClass A protected class. class UndocumentedPublicClass group Inner Group This is an inner group. class InnerGroupClassTest inner class inside of namespace Public Functions inline void function inner namespaced class function ================================================ FILE: tests/data/examples/test_group/group.h ================================================ /** @defgroup mygroup My Group * This is the first group * @{ */ //! \brief first class inside of namespace class GroupedClassTest { public: //! \brief namespaced class function virtual void publicFunction() const = 0; virtual void undocumentedPublicFunction() const = 0; //! A protected class class PublicClass {}; class UndocumentedPublicClass {}; protected: //! A protected function void protectedFunction() {}; void undocumentedProtectedFunction() {}; //! A protected class class ProtectedClass {}; class UndocumentedProtectedClass {}; private: //! This is a private function virtual void privateFunction() const = 0; virtual void undocumentedPrivateFunction() const = 0; //! A private class class PrivateClass {}; class UndocumentedPrivateClass {}; }; //! This function is in MyGroup void groupedFunction(); /** @} */ // end of mygroup /** @defgroup innergroup Inner Group * @ingroup mygroup * This is an inner group * @{ */ //! \brief inner class inside of namespace class InnerGroupClassTest { public: //! \brief inner namespaced class function void function() {}; private: //! A private function void innerGroupPrivateFunction() {}; class PrivateClass {}; }; /** @} */ // end of innergroup //! \brief second class inside of namespace class UngroupedClassTest { public: //! \brief second namespaced class function void function() {}; private: //! A private function void ungroupedPrivateFunction() {}; class PrivateClass {}; }; //! Ungrouped function void ungroupedFunction(); ================================================ FILE: tests/data/examples/test_group/input.rst ================================================ .. doxygengroup:: mygroup :members: .. doxygengroup:: innergroup :members: ================================================ FILE: tests/data/examples/test_group_content_only/compare.xml ================================================ struct Structy Hello. ================================================ FILE: tests/data/examples/test_group_content_only/group_content_only.hpp ================================================ /// @defgroup structy_group StructyGroup /// @{ /// Hello typedef struct { const unsigned char* data_1; unsigned int size_1; const unsigned char* data_2; unsigned int size_2; } Structy; /// @} ================================================ FILE: tests/data/examples/test_group_content_only/input.rst ================================================ .. doxygengroup:: structy_group :content-only: ================================================ FILE: tests/data/examples/test_group_member_ref/compare.xml ================================================ The XML output of Doxygen changed in 1.9.7, see https://github.com/breathe-doc/breathe/pull/934 void func2 another function in group 1 ================================================ FILE: tests/data/examples/test_group_member_ref/group.cpp ================================================ /** @defgroup group1 The First Group * This is the first group * @{ */ /** @brief class C1 in group 1 */ class C1 {}; /** @brief class C2 in group 1 */ class C2 {}; /** function in group 1 */ void func1() {} /** @} */ // end of group1 /** * @defgroup group2 The Second Group * This is the second group */ /** @defgroup group3 The Third Group * This is the third group */ /** @defgroup group4 The Fourth Group * @ingroup group3 * Group 4 is a subgroup of group 3 */ /** * @ingroup group2 * @brief class C3 in group 2 */ class C3 {}; /** @ingroup group2 * @brief class C4 in group 2 */ class C4 {}; /** @ingroup group3 * @brief class C5 in @link group3 the third group@endlink. */ class C5 {}; /** @ingroup group1 group2 group3 group4 * namespace N1 is in four groups * @sa @link group1 The first group@endlink, group2, group3, group4 * * Also see @ref mypage2 */ namespace N1 {}; /** @file * @ingroup group3 * @brief this file in group 3 */ /** @defgroup group5 The Fifth Group * This is the fifth group * @{ */ /** @page mypage1 This is a section in group 5 * Text of the first section */ /** @page mypage2 This is another section in group 5 * Text of the second section */ /** @} */ // end of group5 /** @addtogroup group1 * * More documentation for the first group. * @{ */ /** another function in group 1 */ void func2() {} /** yet another function in group 1 */ void func3() {} /** @} */ // end of group1 ================================================ FILE: tests/data/examples/test_group_member_ref/input.rst ================================================ The XML output of Doxygen changed in 1.9.7, see https://github.com/breathe-doc/breathe/pull/934 .. doxygenfunction:: func2 ================================================ FILE: tests/data/examples/test_headings/compare.xml ================================================ class HeadingsTest1 This is a documentation. This is more documentation. Header Text Header Bold Header Text Text Header Text Header Text class HeadingsTest2
    Header 1 Blah
    Header 2 Blah blah
    Header 3 Blah blah blah
    class HeadingsTest3 Header3 Text class HeadingsTest4
    Header 1 Text Header 3 Text text text
    ================================================ FILE: tests/data/examples/test_headings/extra_dox_opts.txt ================================================ WARN_IF_DOC_ERROR = NO ================================================ FILE: tests/data/examples/test_headings/headings.h ================================================ /*! \brief This is a documentation This is more documentation.

    Header

    Text

    Header Bold Header Text

    Text Header --------- Text ### Header ### Text */ class HeadingsTest1 {}; /*! # Header 1 Blah ## Header 2 Blah blah ### Header 3 Blah blah blah */ class HeadingsTest2 {}; /*! ### Header3 ### Text */ class HeadingsTest3 {}; /*! # Header 1 Text ### Header 3 Text text text */ class HeadingsTest4 {}; ================================================ FILE: tests/data/examples/test_headings/input.rst ================================================ .. doxygenclass:: HeadingsTest1 .. doxygenclass:: HeadingsTest2 .. doxygenclass:: HeadingsTest3 .. doxygenclass:: HeadingsTest4 ================================================ FILE: tests/data/examples/test_html_entities/compare.xml ================================================ void noop x ¡¢£¤¥¦§¨©ª «¬­®¯°±²³´µ¶ ·¸¹º»¼½¾¿À ÁÂÃÄÅÆÇÈÉÊ ËÌÍÎÏÐÑÒÓÔ ÕÖרÙÚÛÜÝÞ ßàáâãäåæçè éêëìíîïðñò óôõö÷øùúûü ýþÿƒΑΒΓΔΕΖΗ ΘΙΚΛΜΝΞΟΠΡΣΤ ΥΦΧΨΩαβγδεζ ηθικλμνξοπρς στυφχψωϑϒϖ• …′″‾⁄℘ℑℜ™ℵ ←↑→↓↔↵⇐⇑⇒⇓⇔∀ ∂∃∅∇∈∉∋∏∑−∗√ ∝∞∠∧∨∩∪∫∴∼≅≈≠ ≡≤≥⊂⊃⊄⊆⊇⊕⊗⊥⋅⌈ ⌉⌊⌋⟨⟩◊♠♣♥♦Œ œŠšŸˆ˜   ‌‍ ‎‏–—‘’‚“”„† ‡‰‹›€™’x ================================================ FILE: tests/data/examples/test_html_entities/entities.h ================================================ /** * x ¡¢£¤¥¦§¨©ª * «¬­®¯°±²³´µ¶ * ·¸¹º»¼½¾¿À * ÁÂÃÄÅÆÇÈÉÊ * ËÌÍÎÏÐÑÒÓÔ * ÕÖרÙÚÛÜÝÞ * ßàáâãäåæçè * éêëìíîïðñò * óôõö÷øùúûü * ýþÿƒΑΒΓΔΕΖΗ * ΘΙΚΛΜΝΞΟΠΡΣΤ * ΥΦΧΨΩαβγδεζ * ηθικλμνξοπρς * στυφχψωϑϒϖ• * …′″‾⁄℘ℑℜ™ℵ * ←↑→↓↔↵⇐⇑⇒⇓⇔∀ * ∂∃∅∇∈∉∋∏∑−∗√ * ∝∞∠∧∨∩∪∫∴∼≅≈≠ * ≡≤≥⊂⊃⊄⊆⊇⊕⊗⊥⋅⌈ * ⌉⌊⌋⟨⟩◊♠♣♥♦Œ * œŠšŸˆ˜   ‌‍ * ‎‏–—‘’‚“”„† * ‡‰‹›€&tm;'x */ void noop(); ================================================ FILE: tests/data/examples/test_html_entities/input.rst ================================================ .. doxygenfunction:: noop ================================================ FILE: tests/data/examples/test_image/compare.xml ================================================ class ImageClass This is a class with an image in the description. It renders like this: Breathe & Sphinx should automatically copy the image from the doxygen output directory into the _images folder of the Sphinx output. ================================================ FILE: tests/data/examples/test_image/image.h ================================================ /** * This is a class with an image in the description. It renders like this: * * \image HTML pixel.png * * Breathe & Sphinx should automatically copy the image from the doxygen output directory into the * _images folder of the Sphinx output. */ class ImageClass {}; ================================================ FILE: tests/data/examples/test_image/input.rst ================================================ .. doxygenclass:: ImageClass ================================================ FILE: tests/data/examples/test_inheritance/compare.xml ================================================ class BaseA Subclassed by ChildV1, ChildV2, Main class BaseB Subclassed by Main class Main : public BaseA, private BaseB This is the main class we’re interested in. Subclassed by ChildA, ChildB class ChildA : public Main class ChildB : public Main class ChildV1 : public virtual BaseA Subclassed by ChildV3 class ChildV2 : public virtual BaseA Subclassed by ChildV3 class ChildV3 : public ChildV1, private ChildV2 ================================================ FILE: tests/data/examples/test_inheritance/inheritance.h ================================================ class BaseA {}; class BaseB {}; /*! \brief This is the main class we're interested in */ class Main : public BaseA, BaseB {}; class ChildA : public Main {}; class ChildB : public Main {}; class ChildV1 : virtual public BaseA {}; class ChildV2 : virtual public BaseA {}; class ChildV3 : public ChildV1, ChildV2 {}; ================================================ FILE: tests/data/examples/test_inheritance/input.rst ================================================ .. doxygenfile:: inheritance.h ================================================ FILE: tests/data/examples/test_inline/compare.xml ================================================ class InlineTest A class to demonstrate inline documentation syntax. Public Functions const char *memberchar cint n A member function. Details about member function Parameters c – c a character. n – n an integer. Throws std::out_of_range – parameter is out of range. Returns a character pointer. ================================================ FILE: tests/data/examples/test_inline/inline.h ================================================ #include /** A class to demonstrate inline documentation syntax. */ class InlineTest { public: /** A member function. * * Details about member function * * \exception std::out_of_range parameter is out of range. * @return a character pointer. */ const char *member(char c, ///< c a character. int n) ///< n an integer. throw(std::out_of_range); }; ================================================ FILE: tests/data/examples/test_inline/input.rst ================================================ .. doxygenclass:: InlineTest :members: ================================================ FILE: tests/data/examples/test_latexmath/compare.xml ================================================ class MathHelper A class. A inline formula: f(x) = a + b A display style formula: \int_a^b f(x) dx = F(b) - F(a) ================================================ FILE: tests/data/examples/test_latexmath/input.rst ================================================ .. doxygenclass:: MathHelper ================================================ FILE: tests/data/examples/test_latexmath/latexmath.h ================================================ /** * @brief A class * * A inline formula: \f$ f(x) = a + b \f$ * * A display style formula: * @f[ * \int_a^b f(x) dx = F(b) - F(a) * @f] */ class MathHelper { public: MathHelper() {} ~MathHelper() {} } ================================================ FILE: tests/data/examples/test_links/compare.xml ================================================ class LinksTest first struct inside of namespace This is a longer description with a link to a webpage in the text http://www.github.com in order to test out Breathe’s handling of links. ================================================ FILE: tests/data/examples/test_links/input.rst ================================================ .. doxygenclass:: LinksTest ================================================ FILE: tests/data/examples/test_links/links.h ================================================ /*! \brief first struct inside of namespace This is a longer description with a link to a webpage in the text http://www.github.com in order to test out Breathe's handling of links. */ class LinksTest {}; ================================================ FILE: tests/data/examples/test_lists/compare.xml ================================================ class SimpleList_1 This is a list example. Following is a list using ‘+’ for bullets:One item.Two items.Three items.Four. And this is some more text. class SimpleList_2 This is a list example. Following is a list using ‘-’ for bullets:One item.Two items.Three items.Four. And this is some more text. class SimpleList_3 This is a list example. Following is a list using ‘*’ for bullets:One item.Two items.Three items.Four. And this is some more text. class SimpleList_4 This is a list example. Following is an auto-numbered list:One item.Two items.Three items.Four. And this is some more text. class SimpleList_5 This is a list example. Following is a numbered list:One item.Two items.Three items.Four. And this is some more text. class SimpleList_6 This is a list example. Following is an unordered list using ‘HTML’ tags: One item. Two items. Three items. Four. And this is some more text. class NestedLists_1 A list of events: mouse eventsmouse move eventmouse click eventMore info about the click event.mouse double click eventkeyboard eventskey down eventkey up event More text here. class NestedLists_2 Text before the list. list item 1sub item 1sub sub item 1sub sub item 2 The dot above ends the sub sub item list.More text for the first sub item The dot above ends the first sub item.More text for the first list itemsub item 2sub item 3list item 2 More text in the same paragraph. More text in a new paragraph. class NestedLists_3 A list of events: mouse events mouse move event mouse click eventMore info about the click event.mouse double click event keyboard events key down event key up event More text here. class NestedLists_4 A list of events: mouse eventsmouse move eventswipe eventcircle eventwave eventmouse click eventMore info about the click event.mouse double click eventkeyboard eventskey down eventkey up eventtouch eventspinch eventswipe event More text here. class NestedLists_5 A deeply nested list of events: mouse eventsmouse move eventswipe eventswipe leftswipe rightcircle eventwave eventmouse click eventMore info about the click event.mouse double click eventkeyboard eventskey down eventkey up eventtouch eventspinch eventswipe event More text here. ================================================ FILE: tests/data/examples/test_lists/input.rst ================================================ .. doxygenfile:: lists.h ================================================ FILE: tests/data/examples/test_lists/lists.h ================================================ /** * \brief This is a list example. * * Following is a list using '+' for bullets: * + One item. * + Two items. * + Three items. * + Four. * * And this is some more text. */ class SimpleList_1 { }; /** * \brief This is a list example. * * Following is a list using '-' for bullets: * - One item. * - Two items. * - Three items. * - Four. * * And this is some more text. */ class SimpleList_2 { }; /** * \brief This is a list example. * * Following is a list using '*' for bullets: * * One item. * * Two items. * * Three items. * * Four. * * And this is some more text. */ class SimpleList_3 { }; /** * \brief This is a list example. * * Following is an auto-numbered list: * -# One item. * -# Two items. * -# Three items. * -# Four. * * And this is some more text. */ class SimpleList_4 { }; /** * \brief This is a list example. * * Following is a numbered list: * 1. One item. * 2. Two items. * 3. Three items. * 4. Four. * * And this is some more text. */ class SimpleList_5 { }; /** * \brief This is a list example. * * Following is an unordered list using 'HTML' tags: *
    • One item. *
    • Two items. *
    • Three items. *
    • Four. *
    * * And this is some more text. */ class SimpleList_6 { }; /** * A list of events: * - mouse events * -# mouse move event * -# mouse click event\n * More info about the click event. * -# mouse double click event * - keyboard events * 1. key down event * 2. key up event * * More text here. */ class NestedLists_1 { }; /** * Text before the list * - list item 1 * - sub item 1 * - sub sub item 1 * - sub sub item 2 * . * The dot above ends the sub sub item list. * * More text for the first sub item * . * The dot above ends the first sub item. * * More text for the first list item * - sub item 2 * - sub item 3 * - list item 2 * . * More text in the same paragraph. * * More text in a new paragraph. */ class NestedLists_2 { }; /*! * A list of events: *
      *
    • mouse events *
        *
      1. mouse move event *
      2. mouse click event
        * More info about the click event. *
      3. mouse double click event *
      *
    • keyboard events *
        *
      1. key down event *
      2. key up event *
      *
    * More text here. */ class NestedLists_3 { }; /** * A list of events: * 1. mouse events * -# mouse move event * 1. swipe event * 2. circle event * 3. wave event * -# mouse click event\n * More info about the click event. * -# mouse double click event * 2. keyboard events * -# key down event * -# key up event * 3. touch events * -# pinch event * -# swipe event * More text here. */ class NestedLists_4 { }; /** * A deeply nested list of events: * 1. mouse events * -# mouse move event * 1. swipe event * -# swipe left * -# swipe right * 2. circle event * 3. wave event * -# mouse click event\n * More info about the click event. * -# mouse double click event * 2. keyboard events * -# key down event * -# key up event * 3. touch events * -# pinch event * -# swipe event * More text here. */ class NestedLists_5 { }; ================================================ FILE: tests/data/examples/test_membergroups/compare.xml ================================================ class GroupedMembers demonstrates member groups myGroup void in_mygroup_oneint myParameter A function. void in_mygroup_twoint myParameter Another function. Public Functions void not_in_mygroupint myParameter This one is not in myGroup. ================================================ FILE: tests/data/examples/test_membergroups/input.rst ================================================ .. doxygenclass:: GroupedMembers :members: ================================================ FILE: tests/data/examples/test_membergroups/membergroups.h ================================================ //! \brief demonstrates member groups class GroupedMembers { public: ///@{ @name myGroup void in_mygroup_one(int myParameter); ///< A function void in_mygroup_two(int myParameter); ///< Another function ///@} void not_in_mygroup(int myParameter); ///< This one is not in myGroup }; ================================================ FILE: tests/data/examples/test_param_dirs/compare.xml ================================================ int processvoid *ivoid *ovoid *io Parameters i[in] Input o[out] Output io[inout] Input and output ================================================ FILE: tests/data/examples/test_param_dirs/input.rst ================================================ .. doxygenfunction:: process ================================================ FILE: tests/data/examples/test_param_dirs/param_dirs.h ================================================ /** * @param[in] i Input * @param[out] o Output * @param[in,out] io Input and output */ int process(void *i, void *o, void *io); ================================================ FILE: tests/data/examples/test_qtsignalsandslots/compare.xml ================================================ class QtSignalSlotExample : public QObject Public Functions inline void workingFunctionint iShownParameter Parameters iShownParameter – This is shown in declaration Public Slots inline void workingSlotint iShown Parameters iShown – This is in function declaration Signals void workingSignalint iShown Parameters iShown – This is in function declaration ================================================ FILE: tests/data/examples/test_qtsignalsandslots/input.rst ================================================ .. doxygenfile:: qtsignalsandslots.h ================================================ FILE: tests/data/examples/test_qtsignalsandslots/qtsignalsandslots.h ================================================ #ifndef QT_OBJECT_H #define QT_OBJECT_H /*! *\brief Forward declaration of QT API class QT slots and signals typically `#include `, but this example is parsed without QT SDK installed. */ extern class QObject; class QtSignalSlotExample: public QObject { Q_OBJECT public: /*! *\param iShownParameter This is shown in declaration */ void workingFunction( int iShownParameter ) { Q_UNUSED( iShownParameter ; ) } signals: /*! \param iShown This is in function declaration */ void workingSignal( int iShown ); public slots: /*! \param iShown This is in function declaration */ void workingSlot( int iShown ) { iShown; } }; #endif ================================================ FILE: tests/data/examples/test_rst/compare.xml ================================================ class TestClass first class inside of namespace Public Functions virtual void function const = 0 Inserting additional reStructuredText information. This is some funky non-XML compliant text: <& !>< This is just a standard verbatim block with code: child = 0; while( child = parent->IterateChildren( child ) ) This reStructuredText has been handled correctly. virtual void rawVerbatim const = 0 Inserting additional reStructuredText information. This reStructuredText has been handled correctly. virtual void rawLeadingAsteriskVerbatim const = 0 Inserting additional reStructuredText information. Some example code:int example(int x) { return x * 2; } virtual void rawLeadingSlashesVerbatimint something const = 0 Some kind of method. bool foo(bool something) { return something; }; Documentation using /// should begin and end in a blank line. Parameters something – a parameter virtual void rawInlineVerbatim const = 0 Inserting an inline reStructuredText snippet. Linking to another function: TestClass::rawVerbatim() inline virtual void testFunction const Brief description. ================================================ FILE: tests/data/examples/test_rst/input.rst ================================================ .. doxygenclass:: TestClass :members: ================================================ FILE: tests/data/examples/test_rst/rst.h ================================================ //! \brief first class inside of namespace class TestClass { public: /*! Inserting additional reStructuredText information. \rst This is some funky non-XML compliant text: <& !>< .. note:: This reStructuredText has been handled correctly. \endrst This is just a standard verbatim block with code: \verbatim child = 0; while( child = parent->IterateChildren( child ) ) \endverbatim */ virtual void function() const = 0; /*! Inserting additional reStructuredText information. \verbatim embed:rst .. note:: This reStructuredText has been handled correctly. \endverbatim */ virtual void rawVerbatim() const = 0; /*! * Inserting additional reStructuredText information. * * \verbatim embed:rst:leading-asterisk * Some example code:: * * int example(int x) { * return x * 2; * } * \endverbatim */ virtual void rawLeadingAsteriskVerbatim() const = 0; /// Some kind of method /// /// @param something a parameter /// /// @verbatim embed:rst:leading-slashes /// .. code-block:: c /// :linenos: /// /// bool foo(bool something) { /// return something; /// }; /// /// @endverbatim /// @note Documentation using `///` should begin and end in a blank line. virtual void rawLeadingSlashesVerbatim(int something) const = 0; /*! Inserting an inline reStructuredText snippet. Linking to another function: \inlinerst :cpp:func:`TestClass::rawVerbatim` \endrst */ virtual void rawInlineVerbatim() const = 0; //! Brief description virtual void testFunction() const {}; }; ================================================ FILE: tests/data/examples/test_simplesect/compare.xml ================================================ template<typename T1, typename T2>void fint afloat bstd::string c see, f_raw sa, f_raw Remarkremark, 1 Remarkremark, 2 Remarkremarks, 1 Remarkremarks, 2 par, something note, be careful warning, don’t do this Pre stuff must be correct Pre more stuff must be correct Post stuff will be nice Post more stuff will be nice Returns nothing ================================================ FILE: tests/data/examples/test_simplesect/input.rst ================================================ .. doxygenfunction:: f ================================================ FILE: tests/data/examples/test_simplesect/simplesect.h ================================================ /*! \pre stuff must be correct \pre more stuff must be correct \post stuff will be nice \post more stuff will be nice \return nothing \par par, something \warning warning, don't do this \note note, be careful \see see, f_raw \sa sa, f_raw \remark remark, 1 \remark remark, 2 \remarks remarks, 1 \remarks remarks, 2 */ template void f(int a, float b, std::string c); ================================================ FILE: tests/data/examples/test_tables/compare.xml ================================================ class Table_1 This is a simple Markdown table example. Following is a simple table using Markdown syntax. First Header Second Header Content Cell Content Cell Content Cell Content Cell
    And this is some more text.
    class Table_2 This is a Markdown table with alignment. Following is a table with alignment using Markdown syntax. Right Center Left 10 10 10 1000 1000 1000
    And this is some more text.
    class Table_3 This is a Markdown table with rowspan and alignment. Following is a table with rowspan and alignment using Markdown syntax. Right Center Left 10 10 10 1000 1000
    And this is some more text.
    class Table_4 This is a Markdown table with colspan and alignment. Following is a table with colspan and alignment using Markdown syntax. Right Center Left 10 10 10 1000
    And this is some more text.
    class Table_5 This is a Doxygen table. Following is a table using Doxygen syntax (and all supported features). Column 1 Column 2 Column 3 cell row=1+2,col=1cell row=1,col=2cell row=1,col=3 cell row=2+3,col=2 cell row=2,col=3 cell row=3,col=1 cell row=3+4,col=3 cell row=4,col=1+2 cell row=5,col=1 cell row=5,col=2+3 cell row=6+7,col=1+2 cell row=6,col=3 cell row=7,col=3 cell row=8,col=1 cell row=8,col=2
    Inner cell row=1,col=1Inner cell row=1,col=2 Inner cell row=2,col=1Inner cell row=2,col=2
    cell row=8,col=3 Item 1 Item 2 And this is some more text.
    ================================================ FILE: tests/data/examples/test_tables/input.rst ================================================ .. doxygenfile:: tables.h ================================================ FILE: tests/data/examples/test_tables/tables.h ================================================ /** * \brief This is a simple Markdown table example. * * Following is a simple table using Markdown syntax. * * First Header | Second Header * ------------- | ------------- * Content Cell | Content Cell * Content Cell | Content Cell * * And this is some more text. */ class Table_1 { }; /** * \brief This is a Markdown table with alignment. * * Following is a table with alignment using Markdown syntax. * * | Right | Center | Left | * | ----: | :----: | :---- | * | 10 | 10 | 10 | * | 1000 | 1000 | 1000 | * * And this is some more text. */ class Table_2 { }; /** * \brief This is a Markdown table with rowspan and alignment. * * Following is a table with rowspan and alignment using Markdown syntax. * * | Right | Center | Left | * | ----: | :----: | :---- | * | 10 | 10 | 10 | * | ^ | 1000 | 1000 | * * And this is some more text. */ class Table_3 { }; /** * \brief This is a Markdown table with colspan and alignment. * * Following is a table with colspan and alignment using Markdown syntax. * * | Right | Center | Left | * | ----: | :----: | :---- | * | 10 | 10 | 10 | * | 1000 ||| * * And this is some more text. */ class Table_4 { }; /** * \brief This is a Doxygen table. * * Following is a table using Doxygen syntax (and all supported features). * * * *
    Complex table
    Column 1 Column 2 Column 3 *
    cell row=1+2,col=1cell row=1,col=2cell row=1,col=3 *
    cell row=2+3,col=2 cell row=2,col=3 *
    cell row=3,col=1 cell row=3+4,col=3 *
    cell row=4,col=1+2 *
    cell row=5,col=1 cell row=5,col=2+3 *
    cell row=6+7,col=1+2 cell row=6,col=3 *
    cell row=7,col=3 *
    cell row=8,col=1 cell row=8,col=2\n * *
    Inner cell row=1,col=1Inner cell row=1,col=2 *
    Inner cell row=2,col=1Inner cell row=2,col=2 *
    *
    cell row=8,col=3 *
      *
    • Item 1 *
    • Item 2 *
    *
    * * And this is some more text. */ class Table_5 { }; ================================================ FILE: tests/data/examples/test_template_class_non_type/compare.xml ================================================ template<typename T, typename U, int N>class anothertemplateclass a class with three template parameters Template Parameters T – this is the first template parameter U – this is the second template parameter N – this is the third template parameter, it is a non-type parameter Public Functions inline anothertemplateclass default constructor inline anothertemplateclassT const &m1U const &m2 constructor with two template argument Parameters m1 – first argument m2 – second argument U methodT const &t member accepting template argument and returning template argument Parameters t – argument Returns returns value of type U ================================================ FILE: tests/data/examples/test_template_class_non_type/input.rst ================================================ .. doxygenclass:: anothertemplateclass :members: ================================================ FILE: tests/data/examples/test_template_class_non_type/template_class_non_type.h ================================================ /** * @brief a class with three template parameters * * @tparam T this is the first template parameter * @tparam U this is the second template parameter * @tparam N this is the third template parameter, it is a non-type parameter */ template class anothertemplateclass { public: /// default constructor anothertemplateclass() {} /** * @brief constructor with two template argument * * @param m1 first argument * @param m2 second argument */ anothertemplateclass(T const & m1, U const & m2) : member1(m1), member2(m2) {} /** * @brief member accepting template argument and returning template argument * * @param t argument * @return returns value of type U */ U method(T const & t); private: /// a member with templated type T member1; /// another member with templated type U member2; }; ================================================ FILE: tests/data/examples/test_template_function/compare.xml ================================================ Functions template<typename T>T function1T arg1 a function with one template arguments Template Parameters T – this is the template parameter Parameters arg1 – argument of type T Returns return value of type T template<>std::string function1<std::string>std::string arg1 a function with one template argument specialized for std::string Parameters arg1 – argument of type std::string Returns return value of type std::string template<typename T, typename U, int N>T function2T arg1U arg2 a function with three template arguments Template Parameters T – this is the first template parameter U – this is the second template parameter N – this is the third template parameter, it is a non-type parameter Parameters arg1 – first argument of type T arg2 – second argument of type U Returns return value of type T template<typename T = void, typename, int>void function3 a function with unnamed arguments and an argument with a default value ================================================ FILE: tests/data/examples/test_template_function/input.rst ================================================ .. doxygenfile:: template_function.h ================================================ FILE: tests/data/examples/test_template_function/template_function.h ================================================ #include /** * @brief a function with one template arguments * * @tparam T this is the template parameter * * @param arg1 argument of type T * * @return return value of type T */ template T function1(T arg1) {} /** * @brief a function with one template argument specialized for `std::string` * * @param arg1 argument of type `std::string` * * @return return value of type `std::string` */ template <> std::string function1(std::string arg1) {} /** * @brief a function with three template arguments * * @tparam T this is the first template parameter * @tparam U this is the second template parameter * @tparam N this is the third template parameter, it is a non-type parameter * * @param arg1 first argument of type T * @param arg2 second argument of type U * * @return return value of type T */ template T function2(T arg1, U arg2) {} /** * @brief a function with unnamed arguments and an argument with a default * value */ template void function3() {} ================================================ FILE: tests/data/examples/test_template_type_alias/compare.xml ================================================ Typedefs template<typename T>using IsFuzzy = std::is_fuzzy<T> a type alias with one template argument Template Parameters T – this is the template parameter template<typename T, typename U, int N>using IsFurry = std::is_furry<T, U, N> a type alias with three template arguments Template Parameters T – this is the first template parameter U – this is the second template parameter N – this is the third template parameter, it is a non-type parameter ================================================ FILE: tests/data/examples/test_template_type_alias/input.rst ================================================ .. doxygenfile:: template_type_alias.h ================================================ FILE: tests/data/examples/test_template_type_alias/template_type_alias.h ================================================ /** * @brief a type alias with one template argument * * @tparam T this is the template parameter * */ template using IsFuzzy = std::is_fuzzy; /** * @brief a type alias with three template arguments * * @tparam T this is the first template parameter * @tparam U this is the second template parameter * @tparam N this is the third template parameter, it is a non-type parameter * */ template using IsFurry = std::is_furry; ================================================ FILE: tests/data/examples/test_union/compare.xml ================================================ union SeparateUnion A union of two values. Public Members int size The size of the thing. float depth How deep it is. namespace foo union MyUnion A union of two values. Public Members int someInt The int of it all. float someFloat The float side of things. ================================================ FILE: tests/data/examples/test_union/input.rst ================================================ .. doxygenunion:: SeparateUnion .. doxygennamespace:: foo ================================================ FILE: tests/data/examples/test_union/union.h ================================================ /// A union of two values union SeparateUnion { int size; ///< The size of the thing float depth; ///< How deep it is }; namespace foo { /// A union of two values union MyUnion { int someInt; ///< The int of it all float someFloat; ///< The float side of things }; } /// A class with a union class ClassWithUnion { /// A union with two values union UnionInClass { int intvalue; ///< An int value float floatvalue; ///< A float value }; /// Documented class class ExtraClass { int a_member; float another_member; }; }; ================================================ FILE: tests/data/examples/test_userdefined/compare.xml ================================================ class UserDefinedGroupTest A class. More details about the UserDefinedGroupTest class Custom Group Description of custom group void func1InCustomGroup Function 1 in custom group. Details. void func2InCustomGroup Function 2 in custom group. Details. Public Functions void func1InGroup1 Same documentation for both members. Details void ungroupedFunction Function without group. Details. ================================================ FILE: tests/data/examples/test_userdefined/input.rst ================================================ .. doxygenclass:: UserDefinedGroupTest :members: :protected-members: ================================================ FILE: tests/data/examples/test_userdefined/userdefined.h ================================================ // Example from Doxygen documentation /** A class. More details about the UserDefinedGroupTest class */ class UserDefinedGroupTest { public: //@{ /** Same documentation for both members. Details */ void func1InGroup1(); void func2InGroup1(); //@} /** Function without group. Details. */ void ungroupedFunction(); void func1InCustomGroup(); protected: void func2InCustomGroup(); }; void UserDefinedGroupTest::func1InGroup1() {} void UserDefinedGroupTest::func2InGroup1() {} /** @name Custom Group * Description of custom group */ //@{ /** Function 2 in custom group. Details. */ void UserDefinedGroupTest::func2InCustomGroup() {} /** Function 1 in custom group. Details. */ void UserDefinedGroupTest::func1InCustomGroup() {} //@} ================================================ FILE: tests/data/examples/test_xrefsect/compare.xml ================================================ A few examples of xrefsect items support. Functions int unimplementedvoid An example of using Doxygen’s todo command. Todo:Implement this function. void buggy_functionint param An example of using Doxygen’s bug and test commands. Bug:Does not work yet. Test:Add proper unit testing first. void old_functionvoid An example of using Doxygen’s deprecated command. Deprecated:Should not be used on new code. void sample_xrefitem_functionvoid An example of a custom Doxygen xrefitem declared as an ALIAS. xref Sample:This text shows up in the xref output. page Todo List Member unimplemented (void)Implement this function. page Bug List Member buggy_function (int param)Does not work yet. page Test List Member buggy_function (int param)Add proper unit testing first. page Deprecated List Member old_function (void)Should not be used on new code. page xref Sample Member sample_xrefitem_function (void) This text shows up in the xref output. ================================================ FILE: tests/data/examples/test_xrefsect/extra_dox_opts.txt ================================================ ALIASES = "xrefsample=\xrefitem xrefsample \"xref Sample\" \"xref Sample\" " ================================================ FILE: tests/data/examples/test_xrefsect/input.rst ================================================ .. doxygenfile:: xrefsect.h .. doxygenpage:: todo .. doxygenpage:: bug .. doxygenpage:: test .. doxygenpage:: deprecated .. doxygenpage:: xrefsample ================================================ FILE: tests/data/examples/test_xrefsect/xrefsect.h ================================================ /** * @file xrefsect.h * A few examples of xrefsect items support. */ /** * An example of using Doxygen's todo command. * * @todo Implement this function. */ int unimplemented(void); /** * An example of using Doxygen's bug and test commands. * * @bug Does not work yet. * * @test Add proper unit testing first. */ void buggy_function(int param); /** * An example of using Doxygen's deprecated command. * * @deprecated Should not be used on new code. */ void old_function(void); /** * An example of a custom Doxygen xrefitem declared as an ALIAS. * * @xrefsample This text shows up in the xref output. */ void sample_xrefitem_function(void); ================================================ FILE: tests/data/multi_project/A/stuff.h ================================================ /** * Doc for fun1 in project A */ void fun1(); /** * Doc for fun2 in project A */ void fun2(); /** * Unique function for project A */ void funA(); ================================================ FILE: tests/data/multi_project/B/stuff.h ================================================ /** * Doc for fun1 in project B */ void fun1(); /** * Doc for fun2 in project B */ void fun2(); /** * Unique function for project B */ void funB(); ================================================ FILE: tests/data/multi_project/C/stuff.h ================================================ /** * Doc for fun1 in project C */ void fun1(); /** * Doc for fun2 in project C */ void fun2(); /** * Unique function for project C */ void funC(); ================================================ FILE: tests/data/multi_project/compare.xml ================================================ void fun1 Doc for fun1 in project A. void fun1 Doc for fun1 in project B. void fun1 Doc for fun1 in project C. void fun2 Doc for fun2 in project A. void fun2 Doc for fun2 in project B. void fun2 Doc for fun2 in project C. void funA Unique function for project A. void funB Unique function for project B. void funC Unique function for project C. ================================================ FILE: tests/data/multi_project/input.rst ================================================ .. doxygenfunction:: fun1 :project: A .. doxygenfunction:: fun1 :project: B .. doxygenfunction:: fun1 :path: {project_c_path} .. doxygenfunction:: fun2 :project: A .. doxygenfunction:: fun2 :project: B .. doxygenfunction:: fun2 :path: {project_c_path} .. doxygenfunction:: funA :project: A .. doxygenfunction:: funB :project: B .. doxygenfunction:: funC :path: {project_c_path} ================================================ FILE: tests/runtests.sh ================================================ #!/bin/sh export PYTHONPATH="../${PYTHONPATH:+:$PYTHONPATH}" python3 -m pytest -v ================================================ FILE: tests/test_examples.py ================================================ from __future__ import annotations import dataclasses import enum import os import pathlib import shutil import subprocess from typing import TYPE_CHECKING from xml.parsers import expat import pytest import sphinx from breathe.process import AutoDoxygenProcessHandle if TYPE_CHECKING: from typing import Any sphinx_path: Any if sphinx.version_info < (7, 2, 0): from sphinx.testing.path import path as sphinx_path else: sphinx_path = pathlib.Path C_FILE_SUFFIXES = frozenset((".h", ".c", ".hpp", ".cpp")) IGNORED_ELEMENTS: frozenset[str] = frozenset(()) BUFFER_SIZE = 0x1000 TEST_DATA_DIR = pathlib.Path(__file__).parent / "data" # if this is changed, tests/data/examples/README.rst should be updated DEFAULT_CONF = { "project": "test", "breathe_default_project": "example", "breathe_show_include": False, "extensions": ["breathe", "sphinx.ext.graphviz"], } DOXYGEN_CACHE_KEY = "BREATHE_DOXYGEN_TEST_CACHE" class XMLEventType(enum.Enum): E_START = enum.auto() E_END = enum.auto() E_TEXT = enum.auto() @dataclasses.dataclass class XMLElement: name: str attr: dict[str, str] line_no: int column_no: int @dataclasses.dataclass class XMLElementEnd: line_no: int column_no: int @dataclasses.dataclass class XMLTextElement: value: str line_no: int column_no: int def xml_stream(infile): """XML pull parser. This is similar to xml.dom.pulldom.parse, except the locations of the elements are tracked.""" p = expat.ParserCreate() pending_events = [] pending_text = "" def dispatch_text(): nonlocal pending_text if pending_text: pending_events.append(( XMLEventType.E_TEXT, XMLTextElement(pending_text, p.CurrentLineNumber, p.CurrentColumnNumber), )) pending_text = "" def handle_start(name, attr): dispatch_text() pending_events.append(( XMLEventType.E_START, XMLElement(name, attr, p.CurrentLineNumber, p.CurrentColumnNumber), )) p.StartElementHandler = handle_start def handle_end(_): dispatch_text() pending_events.append(( XMLEventType.E_END, XMLElementEnd(p.CurrentLineNumber, p.CurrentColumnNumber), )) p.EndElementHandler = handle_end def handle_text(data): nonlocal pending_text pending_text += data p.CharacterDataHandler = handle_text while True: data = infile.read(BUFFER_SIZE) if not data: dispatch_text() p.Parse(data, True) yield from pending_events break p.Parse(data, False) if pending_events: yield from pending_events pending_events.clear() def get_individual_tests(): return (TEST_DATA_DIR / "examples").glob("test_*") def filtered_xml(infile): ignore = 0 for event, node in xml_stream(infile): if event == XMLEventType.E_START: if ignore or node.name in IGNORED_ELEMENTS: ignore += 1 else: yield event, node elif event == XMLEventType.E_END: if ignore: ignore -= 1 else: yield event, node else: if not ignore: text = node.value.strip() if text: node.value = text yield event, node def conf_overrides(extra): conf = DEFAULT_CONF.copy() conf.update(extra) return conf def str_to_set(x): return frozenset(x.split()) def attr_compare(name, actual, expected): if name == "classes": return str_to_set(actual) >= str_to_set(expected) return actual == expected @dataclasses.dataclass class VersionedFile: file: str version: tuple[int, ...] @dataclasses.dataclass class DoxygenExe(VersionedFile): template: str def str_to_version(v_str): return tuple(map(int, v_str.strip().split("."))) def versioned_model(p): return VersionedFile(str(p), str_to_version(str(p.name)[len("compare-") : -len(".xml")])) def compare_xml(generated, input_dir, version): alt_models = list(map(versioned_model, input_dir.glob("compare-*.xml"))) alt_models.sort(key=(lambda f: f.version), reverse=True) model = input_dir / "compare.xml" for alt_m in alt_models: if version >= alt_m.version: model = alt_m.file break event_str = { XMLEventType.E_START: "element start", XMLEventType.E_END: "element end", XMLEventType.E_TEXT: "text", } with open(generated, encoding="utf-8") as o_file, open(model, encoding="utf-8") as c_file: for o, c in zip(filtered_xml(o_file), filtered_xml(c_file)): o_type, o_node = o c_type, c_node = c assert o_type == c_type, ( f"at line {o_node.line_no}: found {event_str[o_type]} when expecting {event_str[c_type]}" # noqa: E501 ) if o_type == XMLEventType.E_START: assert o_node.name == c_node.name, ( f"wrong tag at line {o_node.line_no}: expected {c_node.name}, found {o_node.name}" # noqa: E501 ) # ignore extra attributes in o_node for key, value in c_node.attr.items(): if ( c_node.name == "desc_inline" and key == "domain" and sphinx.version_info[0] < 6 ): # prior to Sphinx 6, this attribute was not present continue assert key in o_node.attr, f"missing attribute at line {o_node.line_no}: {key}" o_value = o_node.attr[key] assert attr_compare(key, o_value, value), ( f'wrong value for attribute "{key}" at line {o_node.line_no}: expected "{value}", found "{o_value}"' # noqa: E501 ) elif o_type == XMLEventType.E_TEXT: assert o_node.value == c_node.value, ( f'wrong content at line {o_node.line_no}: expected "{c_node.value}", found "{o_node.value}"' # noqa: E501 ) @pytest.fixture(scope="module") def doxygen_cache(): dc = os.environ.get(DOXYGEN_CACHE_KEY) if not dc: return None return pathlib.Path(dc).absolute() @pytest.fixture(scope="module") def doxygen(doxygen_cache): if doxygen_cache is None: exc = shutil.which("doxygen") if not exc: raise ValueError("cannot find doxygen executable") v_str = subprocess.run( [exc, "--version"], check=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT, encoding="utf-8", ).stdout else: exc = "" v_str = (doxygen_cache / "version.txt").read_text(encoding="utf-8") return DoxygenExe( exc, str_to_version(v_str.split()[0]), (TEST_DATA_DIR / "examples" / "doxyfile_template").read_text(encoding="utf-8"), ) def run_doxygen_with_template(doxygen, tmp_path, cache, example_name, output_name): doxyfile = tmp_path / "Doxyfile" doxycontent = doxygen.template.format(output=tmp_path) extra_opts = pathlib.Path("extra_dox_opts.txt") if extra_opts.exists(): doxycontent += extra_opts.read_text(encoding="utf-8") doxyfile.write_text(doxycontent, encoding="utf-8") if cache is not None: # instead of passing a different path to breathe_projects.example, the # folder is copied to the same place it would be without caching so that # all paths in the generated output remain the same shutil.copytree(cache / example_name / output_name, tmp_path / output_name) else: subprocess.run([doxygen.file, doxyfile], check=True) if output_name != "xml": os.rename(tmp_path / "xml", tmp_path / output_name) def run_sphinx_and_compare(make_app, tmp_path, test_input, overrides, version): dest = tmp_path / "conf.py" ec = pathlib.Path("extra_conf.py") if ec.exists(): shutil.copyfile(ec, dest) else: dest.touch() make_app( buildername="xml", srcdir=sphinx_path(tmp_path), confoverrides=conf_overrides(overrides), ).build() compare_xml(tmp_path / "_build" / "xml" / "index.xml", test_input, version) @pytest.mark.parametrize("test_input", get_individual_tests(), ids=(lambda x: x.name[5:])) def test_example(make_app, tmp_path, test_input, monkeypatch, doxygen, doxygen_cache): monkeypatch.chdir(test_input) run_doxygen_with_template(doxygen, tmp_path, doxygen_cache, test_input.name, "xml") shutil.copyfile(test_input / "input.rst", tmp_path / "index.rst") run_sphinx_and_compare( make_app, tmp_path, test_input, {"breathe_projects": {"example": str(tmp_path / "xml")}}, doxygen.version, ) def test_auto(make_app, tmp_path, monkeypatch, doxygen, doxygen_cache): test_input = TEST_DATA_DIR / "auto" monkeypatch.chdir(test_input) if doxygen_cache is not None: xml_path = str(doxygen_cache / "auto" / "xml") monkeypatch.setattr(AutoDoxygenProcessHandle, "process", (lambda *args, **kwds: xml_path)) shutil.copyfile(test_input / "input.rst", tmp_path / "index.rst") run_sphinx_and_compare( make_app, tmp_path, test_input, { "breathe_projects_source": { "example": (str(test_input.absolute()), ["auto_class.h", "auto_function.h"]) } }, doxygen.version, ) def test_multiple_projects(make_app, tmp_path, monkeypatch, doxygen, doxygen_cache): test_input = TEST_DATA_DIR / "multi_project" for c in "ABC": monkeypatch.chdir(test_input / c) run_doxygen_with_template(doxygen, tmp_path, doxygen_cache, f"multi_project.{c}", f"xml{c}") (tmp_path / "index.rst").write_text( (test_input / "input.rst") .read_text(encoding="utf-8") .format(project_c_path=str(tmp_path / "xmlC")), encoding="utf-8", ) run_sphinx_and_compare( make_app, tmp_path, test_input, {"breathe_projects": {"A": str(tmp_path / "xmlA"), "B": str(tmp_path / "xmlB")}}, doxygen.version, ) ================================================ FILE: tests/test_filters.py ================================================ from __future__ import annotations import os.path from typing import NamedTuple import pytest from breathe import parser from breathe.renderer import TaggedNode, filter DEFAULT_OPTS = {"path": "", "project": "", "membergroups": "", "show": ""} @pytest.fixture(scope="module") def class_doc(): with open(os.path.join(os.path.dirname(__file__), "data", "classSample.xml"), "rb") as fid: return parser.parse_file(fid).value class SampleMembers(NamedTuple): public_field: filter.NodeStack public_method: filter.NodeStack protected_field: filter.NodeStack protected_method: filter.NodeStack private_field: filter.NodeStack private_method: filter.NodeStack @pytest.fixture def members(class_doc): common = [TaggedNode(None, class_doc.compounddef[0]), TaggedNode(None, class_doc)] memberdefs = {} for sect in class_doc.compounddef[0].sectiondef: member = sect.memberdef[0] memberdefs[member.name] = filter.NodeStack( [TaggedNode(None, member), TaggedNode(None, sect)] + common ) return SampleMembers( memberdefs["public_field"], memberdefs["public_method"], memberdefs["protected_field"], memberdefs["protected_method"], memberdefs["private_field"], memberdefs["private_method"], ) def create_class_filter(app, extra_ops): opts = DEFAULT_OPTS.copy() opts.update(extra_ops) return filter.create_class_filter(app, "Sample", opts) def test_members(app, members): app.config.breathe_default_members = [] filter = create_class_filter(app, {}) assert not filter(members.public_field) assert not filter(members.public_method) assert not filter(members.protected_field) assert not filter(members.protected_method) assert not filter(members.private_field) assert not filter(members.private_method) bools = (True, False) @pytest.mark.parametrize("public", bools) @pytest.mark.parametrize("private", bools) @pytest.mark.parametrize("protected", bools) @pytest.mark.parametrize("undocumented", bools) def test_public_class_members(app, members, public, private, protected, undocumented): app.config.breathe_default_members = [] opts = {} if public: opts["members"] = None if private: opts["private-members"] = None if protected: opts["protected-members"] = None if undocumented: opts["undoc-members"] = None filter = create_class_filter(app, opts) assert filter(members.public_field) == public assert filter(members.public_method) == (public and undocumented) assert filter(members.protected_field) == (protected and undocumented) assert filter(members.protected_method) == protected assert filter(members.private_field) == private assert filter(members.private_method) == (private and undocumented) def test_specific_class_members(app, members): app.config.breathe_default_members = [] filter = create_class_filter( app, {"members": "public_method,protected_method,private_field", "undoc-members": None} ) assert not filter(members.public_field) assert filter(members.public_method) assert not filter(members.protected_field) assert filter(members.protected_method) assert filter(members.private_field) assert not filter(members.private_method) # def test_nested_class_filtered(app): # app.config.breathe_default_members = [] # # doc = parser.parse_str( # """ # # sample.hpp # Sample # Sample::Inner # # # """ # ) # # compounddef = doc.value.compounddef[0] # ref_outer, ref_inner = compounddef.innerclass # # filter_ = filter.create_file_filter( # app, "sample.hpp", DEFAULT_OPTS, init_valid_names=("Sample", "Sample::Inner") # ) # assert filter_(filter.NodeStack([TaggedNode("innerclass", ref_outer), TaggedNode(None, compounddef)])) # noqa: E501 # assert not filter_( # filter.NodeStack([TaggedNode("innerclass", ref_inner), TaggedNode(None, compounddef)]) # ) ================================================ FILE: tests/test_parser.py ================================================ from __future__ import annotations import pytest from breathe import parser def test_bad_content(): xml = """ Sample sample.hpp int int public_field public_field Sample::public_field """ with pytest.raises(parser.ParseError) as exc: parser.parse_str(xml) assert exc.value.lineno == 6 def test_malformed(): xml = """ Sample sample.hpp int""" with pytest.raises(parser.ParseError): parser.parse_str(xml) def test_unknown_tag(): xml = """ Sample Sample sample.hpp int int public_field public_field Sample::public_field """ with pytest.warns(parser.ParseWarning) as record: parser.parse_str(xml) assert len(record) == 1 assert "Warning on line 5:" in str(record[0].message) def test_string_coalesce(): xml = """ Sample sample.hpp int int a a Sample::a ab int int b b Sample::b c """ doc = parser.parse_str(xml).value assert isinstance(doc, parser.Node_DoxygenType) members = doc.compounddef[0].sectiondef[0].memberdef desc1 = members[0].detaileddescription desc2 = members[1].detaileddescription assert desc1 is not None assert desc2 is not None tv1 = desc1[0] tv2 = desc2[0] assert isinstance(tv1, parser.TaggedValue) assert isinstance(tv2, parser.TaggedValue) p1 = tv1.value p2 = tv2.value assert isinstance(p1, parser.Node_docParaType) assert isinstance(p2, parser.Node_docParaType) assert len(p1) == 1 assert len(p2) == 1 assert p1[0] == "a\N{NO-BREAK SPACE}b" assert p2[0] == "\N{TRADE MARK SIGN}c\N{PLUS-MINUS SIGN}" ================================================ FILE: tests/test_renderer.py ================================================ from __future__ import annotations import os from typing import TYPE_CHECKING import docutils.parsers.rst import sphinx.addnodes import sphinx.environment import sphinx.locale from docutils import frontend, nodes, utils from breathe import parser, renderer from breathe.renderer.sphinxrenderer import SphinxRenderer if TYPE_CHECKING: from breathe.renderer import filter sphinx.locale.init([], "") COMMON_ARGS_memberdefType = { "id": "", "prot": parser.DoxProtectionKind.public, "static": False, "location": parser.Node_locationType(file="", line=0), } class MockMemo: def __init__(self): self.title_styles = "" self.section_level = "" class MockState: def __init__(self, app): from breathe.parser import DoxygenParser from breathe.project import ProjectInfoFactory env = sphinx.environment.BuildEnvironment(app) env.setup(app) env.temp_data["docname"] = "mock-doc" env.temp_data["breathe_project_info_factory"] = ProjectInfoFactory(app) env.temp_data["breathe_dox_parser"] = DoxygenParser(app) if hasattr(frontend, "get_default_settings"): settings = frontend.get_default_settings(docutils.parsers.rst.Parser) else: settings = frontend.OptionParser( components=(docutils.parsers.rst.Parser,) ).get_default_values() settings.env = env self.document = utils.new_document("", settings) # In sphinx 5.3.0 the method state.nested_parse is not called directly # so this memo object should exist here self.memo = MockMemo() def nested_parse(self, content, content_offset, contentnode, match_titles=1): pass class MockReporter: def __init__(self): pass def warning(self, description, line): pass def debug(self, message): pass class MockStateMachine: def __init__(self): self.reporter = MockReporter() def get_source_and_line(self, lineno: int): if lineno is None: lineno = 42 return "mock-doc", lineno class MockMaskFactory: def __init__(self): pass def mask(self, node): return node class MockContext: def __init__(self, app, node_stack, domain=None, options=[]): self.domain = domain self.node_stack = node_stack self.directive_args = [ None, # name None, # arguments options, # options "", # content None, # lineno None, # content_offset None, # block_text MockState(app), MockStateMachine(), ] self.child = None self.mask_factory = MockMaskFactory() def create_child_context(self, attribute, tag): return self class MockTargetHandler: def __init__(self): pass def __call__(self, document, refid): return [] class MockDocument: def __init__(self): self.reporter = MockReporter() class MockCompoundParser: """ A compound parser reads a doxygen XML file from disk; this mock implements a mapping of what would be the file name on disk to data using a dict. """ def __init__(self, compound_dict): self.compound_dict = compound_dict class MockFileData: def __init__(self, compounddef): self.compounddef = [compounddef] class MockCompound: def __init__(self, root): self.root = root def parse_compound(self, compoundname, project_info): compounddef = self.compound_dict[compoundname] return self.MockCompound(self.MockFileData(compounddef)) class NodeFinder(nodes.NodeVisitor): """Find node with specified class name.""" def __init__(self, name, document): nodes.NodeVisitor.__init__(self, document) self.name = name self.found_nodes = [] def unknown_visit(self, node): if node.__class__.__name__ == self.name: self.found_nodes.append(node) def find_nodes(nodes, name): """Find all docutils nodes with specified class name in *nodes*.""" finder = NodeFinder(name, MockDocument()) for node in nodes: node.walk(finder) return finder.found_nodes def find_node(nodes, name): """ Find a single docutils node with specified class name in *nodes*. Throw an exception if there isn't exactly one such node. """ found_nodes = find_nodes(nodes, name) if len(found_nodes) != 1: raise Exception(f"the number of nodes {name} is {len(found_nodes)}") return found_nodes[0] def test_find_nodes(): section = nodes.section() foo = nodes.Text("foo") desc = nodes.description() bar = nodes.Text("bar") section.children = [foo, desc, bar] assert find_nodes(section, "description") == [desc] assert find_nodes([section, desc], "description") == [desc, desc] assert find_nodes([], "description") == [] assert find_nodes(section, "unknown") == [] assert find_nodes(section, "Text") == [foo, bar] def check_exception(func, message): """Check if func() throws an exception with the specified message.""" exception = None try: func() except Exception as e: exception = e print(str(exception)) assert exception assert str(exception) == message def test_find_node(): section = nodes.section() foo = nodes.Text("foo") desc = nodes.description() bar = nodes.Text("bar") section.children = [foo, desc, bar] assert find_node(section, "description") == desc check_exception( lambda: find_node([section, desc], "description"), "the number of nodes description is 2" ) check_exception(lambda: find_node([], "description"), "the number of nodes description is 0") check_exception(lambda: find_node([section], "unknown"), "the number of nodes unknown is 0") check_exception(lambda: find_node([section], "Text"), "the number of nodes Text is 2") def render( app, member_def, domain=None, show_define_initializer=False, dox_parser=None, options=[] ): """Render Doxygen *member_def* with *renderer_class*.""" app.config.breathe_separate_member_pages = False app.config.breathe_use_project_refids = False app.config.breathe_show_define_initializer = show_define_initializer app.config.breathe_order_parameters_first = False app.config.breathe_debug_trace_directives = False app.config.breathe_debug_trace_doxygen_ids = False app.config.breathe_debug_trace_qualification = False r = SphinxRenderer( app, None, # project_info [], # node_stack None, # state None, # document MockTargetHandler(), dox_parser, (lambda nstack: True), ) r.context = MockContext(app, [renderer.TaggedNode(None, member_def)], domain, options) return r.render(member_def) def test_render_func(app): member_def = parser.Node_memberdefType( kind=parser.DoxMemberKind.function, definition="void foo", type=parser.Node_linkedTextType(["void"]), name="foo", argsstring="(int)", virt=parser.DoxVirtualKind.non_virtual, param=[parser.Node_paramType(type=parser.Node_linkedTextType(["int"]))], **COMMON_ARGS_memberdefType, ) signature = find_node(render(app, member_def), "desc_signature") assert signature.astext().startswith("void") n = find_node(signature, "desc_name")[0] assert isinstance(n, sphinx.addnodes.desc_sig_name) assert len(n) == 1 assert n[0] == "foo" params = find_node(signature, "desc_parameterlist") assert len(params) == 1 param = params[0] assert isinstance(param[0], sphinx.addnodes.desc_sig_keyword_type) assert param[0][0] == "int" def test_render_typedef(app): member_def = parser.Node_memberdefType( kind=parser.DoxMemberKind.typedef, definition="typedef int foo", type=parser.Node_linkedTextType(["int"]), name="foo", **COMMON_ARGS_memberdefType, ) signature = find_node(render(app, member_def), "desc_signature") assert signature.astext() == "typedef int foo" def test_render_c_typedef(app): member_def = parser.Node_memberdefType( kind=parser.DoxMemberKind.typedef, definition="typedef unsigned int bar", type=parser.Node_linkedTextType(["unsigned int"]), name="bar", **COMMON_ARGS_memberdefType, ) signature = find_node(render(app, member_def, domain="c"), "desc_signature") assert signature.astext() == "typedef unsigned int bar" def test_render_c_function_typedef(app): member_def = parser.Node_memberdefType( kind=parser.DoxMemberKind.typedef, definition="typedef void* (*voidFuncPtr)(float, int)", type=parser.Node_linkedTextType(["void* (*"]), name="voidFuncPtr", argsstring=")(float, int)", **COMMON_ARGS_memberdefType, ) signature = find_node(render(app, member_def, domain="c"), "desc_signature") assert signature.astext().startswith("typedef void *") # the use of desc_parameterlist in this case was not correct, # it should only be used for a top-level function def test_render_using_alias(app): member_def = parser.Node_memberdefType( kind=parser.DoxMemberKind.typedef, definition="using foo = int", type=parser.Node_linkedTextType(["int"]), name="foo", **COMMON_ARGS_memberdefType, ) signature = find_node(render(app, member_def), "desc_signature") assert signature.astext() == "using foo = int" def test_render_const_func(app): member_def = parser.Node_memberdefType( kind=parser.DoxMemberKind.function, definition="void f", type=parser.Node_linkedTextType(["void"]), name="f", argsstring="() const", virt=parser.DoxVirtualKind.non_virtual, const=True, **COMMON_ARGS_memberdefType, ) signature = find_node(render(app, member_def), "desc_signature") assert "_CPPv2NK1fEv" in signature["ids"] def test_render_lvalue_func(app): member_def = parser.Node_memberdefType( kind=parser.DoxMemberKind.function, definition="void f", type=parser.Node_linkedTextType(["void"]), name="f", argsstring="() &", virt=parser.DoxVirtualKind.non_virtual, refqual=parser.DoxRefQualifierKind.lvalue, **COMMON_ARGS_memberdefType, ) signature = find_node(render(app, member_def), "desc_signature") assert signature.astext().endswith("&") def test_render_rvalue_func(app): member_def = parser.Node_memberdefType( kind=parser.DoxMemberKind.function, definition="void f", type=parser.Node_linkedTextType(["void"]), name="f", argsstring="() &&", virt=parser.DoxVirtualKind.non_virtual, refqual=parser.DoxRefQualifierKind.rvalue, **COMMON_ARGS_memberdefType, ) signature = find_node(render(app, member_def), "desc_signature") assert signature.astext().endswith("&&") def test_render_const_lvalue_func(app): member_def = parser.Node_memberdefType( kind=parser.DoxMemberKind.function, definition="void f", type=parser.Node_linkedTextType(["void"]), name="f", argsstring="() const &", virt=parser.DoxVirtualKind.non_virtual, const=True, refqual=parser.DoxRefQualifierKind.lvalue, **COMMON_ARGS_memberdefType, ) signature = find_node(render(app, member_def), "desc_signature") assert signature.astext().endswith("const &") def test_render_const_rvalue_func(app): member_def = parser.Node_memberdefType( kind=parser.DoxMemberKind.function, definition="void f", type=parser.Node_linkedTextType(["void"]), name="f", argsstring="() const &&", virt=parser.DoxVirtualKind.non_virtual, const=True, refqual=parser.DoxRefQualifierKind.rvalue, **COMMON_ARGS_memberdefType, ) signature = find_node(render(app, member_def), "desc_signature") assert signature.astext().endswith("const &&") def test_render_variable_initializer(app): member_def = parser.Node_memberdefType( kind=parser.DoxMemberKind.variable, definition="const int EOF", type=parser.Node_linkedTextType(["const int"]), name="EOF", initializer=parser.Node_linkedTextType(["= -1"]), **COMMON_ARGS_memberdefType, ) signature = find_node(render(app, member_def), "desc_signature") assert signature.astext() == "const int EOF = -1" def test_render_define_initializer(app): member_def = parser.Node_memberdefType( kind=parser.DoxMemberKind.define, name="MAX_LENGTH", initializer=parser.Node_linkedTextType(["100"]), **COMMON_ARGS_memberdefType, ) signature_w_initializer = find_node( render(app, member_def, show_define_initializer=True), "desc_signature" ) assert signature_w_initializer.astext() == "MAX_LENGTH 100" member_def_no_show = parser.Node_memberdefType( kind=parser.DoxMemberKind.define, name="MAX_LENGTH_NO_INITIALIZER", initializer=parser.Node_linkedTextType(["100"]), **COMMON_ARGS_memberdefType, ) signature_wo_initializer = find_node( render(app, member_def_no_show, show_define_initializer=False), "desc_signature" ) assert signature_wo_initializer.astext() == "MAX_LENGTH_NO_INITIALIZER" def test_render_define_no_initializer(app): sphinx.addnodes.setup(app) member_def = parser.Node_memberdefType( kind=parser.DoxMemberKind.define, name="USE_MILK", **COMMON_ARGS_memberdefType ) signature = find_node(render(app, member_def), "desc_signature") assert signature.astext() == "USE_MILK" def test_render_innergroup(app): refid = "group__innergroup" mock_compound_parser = MockCompoundParser({ refid: parser.Node_compounddefType( kind=parser.DoxCompoundKind.group, compoundname="InnerGroup", briefdescription=parser.Node_descriptionType(["InnerGroup"]), id="", prot=parser.DoxProtectionKind.public, ) }) ref = parser.Node_refType(["InnerGroup"], refid=refid) compound_def = parser.Node_compounddefType( kind=parser.DoxCompoundKind.group, compoundname="OuterGroup", briefdescription=parser.Node_descriptionType(["OuterGroup"]), innergroup=[ref], id="", prot=parser.DoxProtectionKind.public, ) assert all( el.astext() != "InnerGroup" for el in render(app, compound_def, dox_parser=mock_compound_parser) ) assert any( el.astext() == "InnerGroup" for el in render(app, compound_def, dox_parser=mock_compound_parser, options=["inner"]) ) def get_directive(app): from docutils.statemachine import StringList from breathe.directives.function import DoxygenFunctionDirective app.config.breathe_separate_member_pages = False app.config.breathe_default_project = "test_project" app.config.breathe_domain_by_extension = {} app.config.breathe_domain_by_file_pattern = {} app.config.breathe_use_project_refids = False cls_args = ( "doxygenclass", ["at::Tensor"], {"members": "", "protected-members": None, "undoc-members": None}, StringList([], items=[]), 20, 24, ( ".. doxygenclass:: at::Tensor\n :members:\n" " :protected-members:\n :undoc-members:" ), MockState(app), MockStateMachine(), ) # fmt: skip return DoxygenFunctionDirective(*cls_args) def get_matches(datafile) -> tuple[list[str], list[filter.FinderMatch]]: with open(os.path.join(os.path.dirname(__file__), "data", datafile), "rb") as fid: doc = parser.parse_file(fid) assert isinstance(doc.value, parser.Node_DoxygenType) sectiondef = doc.value.compounddef[0].sectiondef[0] argsstrings = [child.argsstring for child in sectiondef.memberdef if child.argsstring] matches = [ [renderer.TaggedNode(None, m), renderer.TaggedNode(None, sectiondef)] for m in sectiondef.memberdef ] return argsstrings, matches def test_resolve_overrides(app): # Test that multiple function overrides works argsstrings, matches = get_matches("arange.xml") cls = get_directive(app) # Verify that the exact arguments returns one override for args in argsstrings: ast_param = cls._parse_args(args) _ = cls._resolve_function(matches, ast_param, None) def test_ellipsis(app): argsstrings, matches = get_matches("ellipsis.xml") cls = get_directive(app) # Verify that parsing an ellipsis works ast_param = cls._parse_args(argsstrings[0]) _ = cls._resolve_function(matches, ast_param, None) ================================================ FILE: tests/test_utils.py ================================================ from __future__ import annotations from unittest import TestCase from breathe import parser, path_handler from breathe.renderer.sphinxrenderer import get_definition_without_template_args, get_param_decl class TestUtils(TestCase): def test_param_decl(self): # From xml from: examples/specific/parameters.h xml = """ x int a float b int * c int(**) p [3] MyClass a MyClass * b int(&) r [3] """ doc = parser.parse_str(xml) assert isinstance(doc.value, parser.Node_DoxygenType) memberdef = doc.value.compounddef[0].sectiondef[0].memberdef[0] assert get_param_decl(memberdef.param[0]) == "int a" assert get_param_decl(memberdef.param[1]) == "float b" assert get_param_decl(memberdef.param[2]) == "int * c" assert get_param_decl(memberdef.param[3]) == "int(**p)[3]" assert get_param_decl(memberdef.param[4]) == "MyClass a" assert get_param_decl(memberdef.param[5]) == "MyClass * b" assert get_param_decl(memberdef.param[6]) == "int(&r)[3]" def test_definition_without_template_args(self): def get_definition(definition, name, bitfield=""): class MockDataObject: def __init__(self, definition, name, bitfield): self.definition = definition self.name = name self.bitfield = bitfield return get_definition_without_template_args(MockDataObject(definition, name, bitfield)) assert "void A::foo" == get_definition("void A::foo", "foo") # Template arguments in the return type should be preserved: assert "Result A::f" == get_definition("Result A::f", "f") # Nested template arguments: assert "Result A::f" == get_definition("Result A< B >::f", "f") # Bit fields assert "int f : 3" == get_definition("int f", "f", "3") def test_definition_without_template_args(): def get_definition(definition, name, bitfield=""): class MockDataObject: def __init__(self, definition, name, bitfield): self.definition = definition self.name = name self.bitfield = bitfield return get_definition_without_template_args(MockDataObject(definition, name, bitfield)) assert "void A::foo" == get_definition("void A::foo", "foo") # Template arguments in the return type should be preserved: assert "Result A::f" == get_definition("Result A::f", "f") # Nested template arguments: assert "Result A::f" == get_definition("Result A< B >::f", "f") # Bit fields assert "int f : 3" == get_definition("int f", "f", "3") def test_path_handler(): assert path_handler.includes_directory("directory/file.h") is True assert path_handler.includes_directory("directory\\file.h") is True assert path_handler.includes_directory("file.h") is False ================================================ FILE: tests/warnings/Makefile ================================================ # Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = build # User-friendly check for sphinx-build ifeq ($(shell which $(SPHINXBUILD) >/dev/null 2>&1; echo $$?), 1) $(error The '$(SPHINXBUILD)' command was not found. Make sure you have Sphinx installed, then set the SPHINXBUILD environment variable to point to the full path of the '$(SPHINXBUILD)' executable. Alternatively you can add the directory with the executable to your PATH. If you don't have Sphinx installed, grab it from https://www.sphinx-doc.org/) endif # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) source .PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest gettext help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" clean: rm -rf $(BUILDDIR)/* html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/TestBreatheWarnings.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/TestBreatheWarnings.qhc" devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/TestBreatheWarnings" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/TestBreatheWarnings" @echo "# devhelp" epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." ================================================ FILE: tests/warnings/make.bat ================================================ @ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% source set I18NSPHINXOPTS=%SPHINXOPTS% source if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. xml to make Docutils-native XML files echo. pseudoxml to make pseudoxml-XML files for display purposes echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) %SPHINXBUILD% 2> nul if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.https://www.sphinx-doc.org/ exit /b 1 ) if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\TestBreatheWarnings.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\TestBreatheWarnings.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdf" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdfja" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf-ja cd %BUILDDIR%/.. echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) if "%1" == "xml" ( %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml if errorlevel 1 exit /b 1 echo. echo.Build finished. The XML files are in %BUILDDIR%/xml. goto end ) if "%1" == "pseudoxml" ( %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml if errorlevel 1 exit /b 1 echo. echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. goto end ) :end ================================================ FILE: tests/warnings/source/class.rst ================================================ Class Warnings ============== Test 'cannot find project' warning. .. doxygenclass:: MyClass :project: nonexistent Test 'cannot find xml' warning: .. doxygenclass:: MyClass :project: invalidproject Test 'cannot find class' warning: .. doxygenclass:: NonExistentClass :project: class ================================================ FILE: tests/warnings/source/conf.py ================================================ # mypy: ignore-errors from __future__ import annotations import sys sys.path.append("../../") extensions = ["breathe"] # Breathe configuration parameters breathe_projects = { "class": "../../../examples/doxygen/class/xml/", "function": "../../../examples/specific/functionOverload/xml/", "group": "../../../examples/specific/group/xml/", "invalidproject": "invalid/path/", } master_doc = "index" project = "Test Breathe Warnings" html_theme = "default" ================================================ FILE: tests/warnings/source/define.rst ================================================ Define Warnings =============== Test 'cannot find project' warning. .. doxygendefine:: MY_DEFINE :project: nonexistent Test 'cannot find define' warning. .. doxygendefine:: MY_DEFINE :project: class ================================================ FILE: tests/warnings/source/function.rst ================================================ Function Warnings ================= Test 'cannot find project' warning. .. doxygenfunction:: MyFunction :project: nonexistent Test 'cannot find xml' warning: .. doxygenfunction:: MyFunction :project: invalidproject Test 'cannot find function' warning. .. doxygenfunction:: NonExistentFunction :project: function Test 'too many matches' warning. .. doxygenfunction:: f :project: function ================================================ FILE: tests/warnings/source/group.rst ================================================ Function Warnings ================= Test 'cannot find project' warning. .. doxygengroup:: MyFunction :project: nonexistent Test 'cannot find xml' warning: .. doxygengroup:: MyGroup :project: invalidproject Test 'cannot find function' warning. .. doxygengroup:: NonExistentGroup :project: group ================================================ FILE: tests/warnings/source/index.rst ================================================ Test Breathe Warnings ===================== This Sphinx instance is designed to call Breathe in every single incorrect way in order to test that the warnings work without crashing the build process. .. toctree:: :maxdepth: 2 class function group define ================================================ FILE: xml_parser_generator/make_parser.py ================================================ """Parse a JSON schema file and generate the C code for a Python module to parse XML""" from __future__ import annotations import collections import dataclasses import enum import functools import json import keyword import re from pathlib import Path from typing import TYPE_CHECKING, NamedTuple, TypeVar, cast import jinja2 if TYPE_CHECKING: from collections.abc import Iterable, Sequence from typing import Any, Callable, Literal, NoReturn T = TypeVar("T") # The minimum number of items a set should have before using a hash-based # lookup. If fewer, the strings are compared one-by-one instead. HASH_LOOKUP_THRESHOLD = 8 SPLIT_LINE_ITEM_THRESHOLD = 5 BUILTIN_ATTR_SCHEMA_TYPES = [ ("string", "str"), ("DoxBool", "bool"), ("integer", "int"), ("empty", "None"), ] RE_CHAR_TYPE = re.compile(r"\s*#\s*char\s*\(([^\s)]+)\s*\)\s*") def comma_join(items: Sequence[str], indent: int = 4): if len(items) < SPLIT_LINE_ITEM_THRESHOLD: return ", ".join(items) return (",\n" + " " * indent).join(items) class ContentType(enum.Enum): """A value specifying how children are organized when parsing an array-type element""" bare = enum.auto() """Child values are added directly to the array. There can only be one child type, which can be an element or text. """ tuple = enum.auto() """Child elements are grouped into named tuple-like objects. Each batch of child elements must appear in order in the XML document. Text content is not allowed. Currently, tuple child element names must be valid Python identifiers as there isn't a way to have different field names. """ union = enum.auto() """Each item is either a tagged union (an instance of TaggedValue) or a plain string""" @dataclasses.dataclass() class TypeRef: """An XML element""" name: str """the name of the element as it will appear in the XML file""" py_name: str """The Python field name that will hold the parsed value. This will be different from "name" if "name" is not a valid Python identifier. """ type: str | SchemaType """While the schema is being parsed, this will be a string containing the name of attribute's type. After parsing, this is set to the object representing the type. """ is_list: bool """Whether this element can appear more than once in its context""" min_items: Literal[0] | Literal[1] """If this is zero, the element is optional. This can only be zero or one. """ def py_type(self, as_param=False) -> str: """Get the Python type annotation describing the type of this element. If "as_param" is True, this represents a parameter type that can be converted to the actual type. For example with a given type "T": the generated parser uses FrozenList[T] to store arrays, but constructors accept Iterable[T] for array fields. """ assert isinstance(self.type, SchemaType) if self.is_list: container = "Iterable" if as_param else "FrozenList" return f"{container}[{self.type.py_name}]" if self.min_items == 0: return f"{self.type.py_name} | None" return self.type.py_name def needs_finish(self) -> bool: """Return True if the field value will need to be checked at the end of parsing the element. This is the case case for all fields except list fields with no minimum. For most fields, we need to know how many corresponding child elements exist, which can't be known until the parent element is fully parsed, but list fields without minimums accept any number of child elements. """ return not self.is_list or self.min_items > 0 @dataclasses.dataclass() class Attribute: """An XML attribute""" name: str """the name of the attribute as it will appear in the XML file""" py_name: str """The Python field name that will hold the parsed value. This will be different from "name" if "name" is not a valid Python identifier. """ type: str | AttributeType """While the schema is being parsed, this will be a string containing the name of attribute's type. After parsing, this is set to the object representing the type. """ optional: bool """Whether the attribute may be omitted. Fields corresponding to omitted attributes are set to None. """ def py_type(self, as_param=False) -> str: """Get the Python type annotation describing the type of this attribute. If "as_param" is True, this represents a parameter type that can be converted to the actual type. For example with a given type "T": the generated parser uses FrozenList[T] to store arrays, but constructors accept Iterable[T] for array fields. """ assert isinstance(self.type, SchemaType) if self.optional: return f"{self.type.py_name} | None" return self.type.py_name @dataclasses.dataclass() class SchemaType: name: str def __str__(self): """This is equal to self.name. This is important for the Jinja template, which frequently uses the names of types. """ return self.name def content_names(self) -> Iterable[str]: return [] @property def extra_args(self) -> str: """A string to add before the closing bracket of the C function call to the type's element start handler""" return "" def add_sorted(self, dest: list[SchemaType], visited: set[int]) -> None: if id(self) not in visited: visited.add(id(self)) dest.append(self) if TYPE_CHECKING: @property def py_name(self) -> str: raise NotImplementedError @dataclasses.dataclass() class AttributeType(SchemaType): """A type that can be used in attributes and elements. When used for an element, the element will not have any attributes or child elements. """ @dataclasses.dataclass() class BuiltinType(SchemaType): py_name: str """the name of the Python data type that will represent a value of this type""" @dataclasses.dataclass() class AddsToStringType(BuiltinType): pass @dataclasses.dataclass() class SpType(AddsToStringType): """This element represents an arbitrary character whose code point is given in the attribute "value". If "value" isn't present, the character is a space. """ @dataclasses.dataclass() class CodePointType(AddsToStringType): """This element represents a specific character.""" char: int """The unicode code-point of the character""" def __init__(self, char: int): self.name = "const_char" self.py_name = "str" self.char = char @property def extra_args(self) -> str: return f",{self.char:#x}" @dataclasses.dataclass() class BuiltinAttributeType(BuiltinType, AttributeType): pass class OtherAttrAction(enum.Enum): ignore = enum.auto() error = enum.auto() @dataclasses.dataclass() class ElementType(SchemaType): """An element type specified by the schema""" bases: list[str | SchemaType] """the types to derive from""" attributes: dict[str, Attribute] """XML attributes""" other_attr: OtherAttrAction """how to handle attributes not in "attributes" """ children: dict[str, TypeRef] """XML child elements""" used_directly: bool """Each element that is used directly, corresponds to a separate Python class. If this is False, this element is only used as a base element for other types and does not produce any Python classes.""" def fields(self) -> Iterable[TypeRef | Attribute]: yield from self.attributes.values() yield from self.children.values() @property def direct_field_count(self): return len(self.attributes) + len(self.children) def all_fields(self) -> Iterable[TypeRef | Attribute]: for b in self.bases: if isinstance(b, ElementType): yield from b.all_fields() yield from self.fields() @property def py_name(self) -> str: return f"Node_{self.name}" def add_sorted(self, dest: list[SchemaType], visited: set[int]) -> None: if id(self) not in visited: for b in self.bases: assert isinstance(b, SchemaType) b.add_sorted(dest, visited) visited.add(id(self)) dest.append(self) @dataclasses.dataclass() class TagOnlyElement(ElementType): """A simple element that cannot contain text (not counting whitespace) and does not preserve the order of its child elements""" @dataclasses.dataclass() class ListElement(ElementType): """An element type that gets parsed as an array type. The items of the array depend on "content", "content_type" and "allow_text". """ min_items: int content: dict[str, str | SchemaType] """Child elements that will be stored as array items. While the schema is being parsed, the values will be strings containing the names of the elements' types. After parsing, they are set to the objects representing the types. """ content_type: ContentType allow_text: bool def content_names(self) -> Iterable[str]: for b in self.bases: assert isinstance(b, SchemaType) yield from b.content_names() yield from self.content def all_content(self): for b in self.bases: if isinstance(b, ListElement): yield from b.content.values() yield from self.content.values() def py_union_ref(self) -> list[str]: types = self.py_union_list() if len(types) <= 1: return types return ["ListItem_" + self.name] def py_union_list(self, quote=False) -> list[str]: """Return a list of type annotations, the union of which, represent every possible value of this array's elements. This assumes self.content_type == ContentType.union. """ assert self.content_type == ContentType.union by_type = collections.defaultdict(list) needs_str = False for name, t in self.content.items(): assert isinstance(t, SchemaType) if not isinstance(t, AddsToStringType): by_type[t.py_name].append(name) else: needs_str = True types = [ "TaggedValue[Literal[{}], {}]".format( comma_join(sorted(f"'{n}'" for n in names), 26), f'"{t}"' if quote else t ) for t, names in by_type.items() ] str_included = False for b in self.bases: if isinstance(b, ListElement): types.extend(b.py_union_ref()) if b.allow_text: str_included = True if self.allow_text and not str_included: types.append("str") elif needs_str: raise ValueError( f'type "{self.name}" cannot have #spType or ' + '#char(...) items unless "allow_text" is true' ) return types @dataclasses.dataclass() class Schema: roots: dict[str, str | SchemaType] types: dict[str, SchemaType] class EnumEntry(NamedTuple): xml: str id: str @dataclasses.dataclass() class SchemaEnum(AttributeType): """A type representing an enumeration. This type is represented in python with enum.Enum. """ children: list[EnumEntry] hash: HashData | None = None def any_renamed(self) -> bool: return any(c.xml != c.id for c in self.children) @property def py_name(self) -> str: return self.name @dataclasses.dataclass() class SchemaCharEnum(AttributeType): """An enumeration type whose elements are single characters. Unlike SchemaEnum, the values are represented as strings. """ values: str @property def py_name(self) -> str: return self.name def unknown_type_error(ref: str, context: str, is_element: bool) -> NoReturn: thing = "element" if is_element else "attribute" raise ValueError(f'{thing} "{context}" has undefined type "{ref}"') def check_type_ref(schema: Schema, ref: str, context: str, is_element: bool = True) -> SchemaType: """Get the schema type that represent the type named by "ref" """ t = schema.types.get(ref) if t is None: m = RE_CHAR_TYPE.fullmatch(ref) if m is not None: char = int(m.group(1), 16) if char > 0x10FFFF: raise ValueError( f'"char" type at "{context}" must have a value between 0 and 0x10FFFF inclusive' ) return CodePointType(char) unknown_type_error(ref, context, is_element) return t def check_attr_type_ref(schema: Schema, ref: str, context: str) -> AttributeType: """Get the schema type that represent the type named by "ref" and raise an exception if it's not usable in an XML attribute""" r = check_type_ref(schema, ref, context, False) if isinstance(r, AttributeType): return r raise ValueError(f'attribute "{context}" has incompatible type "{ref}"') def check_py_name(name: str) -> None: """Raise ValueError if "name" is not suitable as a Python field name""" if (not name.isidentifier()) or keyword.iskeyword(name): raise ValueError(f'"{name}" is not a valid Python identifier') if name == "_children": raise ValueError('the name "_children" is reserved by the parser generator') def resolve_refs(schema: Schema) -> tuple[list[str], list[str]]: """Replace all type reference names with actual types and return the lists of all element names and attribute names""" elements: set[str] = set() attributes: set[str] = set() def check_element_type_defined(name: str, ref: str) -> SchemaType: t = check_type_ref(schema, ref, name) if isinstance(t, ElementType): t.used_directly = True return t for name, r in schema.roots.items(): elements.add(name) schema.roots[name] = check_element_type_defined(name, cast("str", r)) for typename, t in schema.types.items(): if not t.name: t.name = typename if isinstance(t, ElementType): # TODO: check for recursive bases for i, b in enumerate(t.bases): b_type = schema.types.get(cast("str", b)) if b_type is None: raise ValueError(f'type "{typename}" has undefined base "{b}"') if not isinstance(b_type, ElementType): raise ValueError(f'"{b}" cannot be used as a base') if isinstance(b_type, ListElement): if not isinstance(t, ListElement): raise ValueError("non-list elements cannot use list elements as bases") if b_type.content_type != t.content_type: raise ValueError( "list elements of one type cannot use list elements " "of another type as bases" ) t.bases[i] = b_type for name, child in t.children.items(): child.name = name if not child.py_name: child.py_name = name check_py_name(child.py_name) elements.add(name) child.type = check_element_type_defined( f"{typename}.{name}", cast("str", child.type) ) for name, attr in t.attributes.items(): attr.name = name if not attr.py_name: attr.py_name = name check_py_name(attr.py_name) attributes.add(name) t.attributes[name].type = check_attr_type_ref(schema, cast("str", attr.type), name) if isinstance(t, ListElement): for name, r in t.content.items(): elements.add(name) t.content[name] = check_element_type_defined( f"{typename}.{name}", cast("str", r) ) elements.update(schema.roots) return sorted(elements), sorted(attributes) class HashData(NamedTuple): salt1: str salt2: str g: list[int] # def generate_hash(items: list[str]) -> HashData: # try: # f1, f2, g = perfect_hash.generate_hash(items) # return HashData(f1.salt, f2.salt, g) # except ValueError: # print(items, file=sys.stderr) # raise def collect_field_names( all_fields: set[str], cur_fields: set[str], refs: Iterable[Attribute | TypeRef], type_name: str ) -> None: """Gather all field names into "all_fields" and make sure they are unique in "cur_fields" """ for ref in refs: all_fields.add(ref.py_name) if ref.py_name in cur_fields: raise ValueError(f'python name "{ref.py_name}" appears more than once in "{type_name}"') cur_fields.add(ref.py_name) def make_env(schema: Schema) -> jinja2.Environment: elements, attributes = resolve_refs(schema) tag_names: set[str] = set(schema.roots) py_field_name_set: set[str] = set() char_enum_chars: set[str] = set() list_element_field_counts: set[int] = set() tagonly_and_tuple_field_counts: set[int] = set() tuple_field_counts: set[int] = set() def field_count(t) -> int: if not isinstance(t, ElementType): return 0 return ( len(t.attributes) + len(t.children) + sum(cast("int", field_count(b)) for b in t.bases) ) for t in schema.types.values(): # if isinstance(t, SchemaEnum): # if len(t.children) >= HASH_LOOKUP_THRESHOLD: # t.hash = generate_hash([item.xml for item in t.children]) if isinstance(t, SchemaCharEnum): char_enum_chars.update(t.values) elif isinstance(t, ElementType): fields: set[str] = set() collect_field_names(py_field_name_set, fields, t.attributes.values(), t.name) collect_field_names(py_field_name_set, fields, t.children.values(), t.name) if isinstance(t, TagOnlyElement): if t.used_directly: tagonly_and_tuple_field_counts.add(field_count(t)) elif isinstance(t, ListElement): if t.used_directly: list_element_field_counts.add(field_count(t)) if t.content_type == ContentType.union: tag_names.update( name for name, t in t.content.items() if not isinstance(t, AddsToStringType) ) elif t.content_type == ContentType.tuple: tuple_field_counts.add(len(t.content)) tagonly_and_tuple_field_counts.add(len(t.content)) py_field_names = sorted(py_field_name_set) tmpl_env = jinja2.Environment( block_start_string="{%", block_end_string="%}", variable_start_string="{$", variable_end_string="$}", comment_start_string="/*#", comment_end_string="#*/", line_statement_prefix="//%", line_comment_prefix="//#", autoescape=False, ) def has_attributes(t): if not isinstance(t, ElementType): return False return t.attributes or any(has_attributes(b) for b in t.bases) def has_children(t): if not isinstance(t, ElementType): return False return t.children or any(has_children(b) for b in t.bases) def has_children_or_content(t): if not isinstance(t, ElementType): return False return ( t.children or (isinstance(t, ListElement) and t.content) or any(has_children_or_content(b) for b in t.bases) ) def has_children_or_tuple_content(t): if not isinstance(t, ElementType): return False return ( t.children or ( isinstance(t, ListElement) and t.content_type == ContentType.tuple and len(t.content) > 1 ) or any(has_children_or_tuple_content(b) for b in t.bases) ) def base_offsets(t): if not isinstance(t, ElementType): return tmpl_env.undefined() total = 0 for b in t.bases: assert isinstance(b, SchemaType) yield b, total total += field_count(b) yield None, total def list_type_or_base(t): if not isinstance(t, ElementType): return False return isinstance(t, ListElement) or any(list_type_or_base(b) for b in t.bases) def allow_text(t): if not isinstance(t, ListElement): return False return t.allow_text or any(allow_text(b) for b in t.bases) def content_type(ct): def inner(t): if not isinstance(t, ListElement): return False return t.content_type == ct return inner def children(t): if not isinstance(t, ElementType): return tmpl_env.undefined() return t.children.values() def get_attributes(t): if not isinstance(t, ElementType): return tmpl_env.undefined() return t.attributes.values() def content(t): if not isinstance(t, ListElement): return tmpl_env.undefined() return t.content.items() def used_directly(t): return isinstance(t, ElementType) and t.used_directly def optional(ref: TypeRef | Attribute) -> bool: if isinstance(ref, TypeRef): return ref.is_list or ref.min_items == 0 return ref.optional def array_field(ref) -> bool: if isinstance(ref, TypeRef): return ref.is_list return False def needs_finish_fields_call(t): if not isinstance(t, ElementType): return False return any(c.needs_finish() for c in t.children.values()) or any( map(needs_finish_fields_call, t.bases) ) def needs_finish_call(t): return needs_finish_fields_call(t) or ( isinstance(t, ListElement) and t.content_type == ContentType.tuple and len(t.content) > 1 ) def error(msg): raise TypeError(msg) class Once: def __init__(self, content): self.content = content self.used = False def __call__(self): if self.used: return "" self.used = True return self.content # types sorted topologically with regard to base elements sorted_types: list[SchemaType] = [] visited_types: set[int] = set() for t in schema.types.values(): t.add_sorted(sorted_types, visited_types) if isinstance(t, ElementType) and any(field_count(cast("ElementType", b)) for b in t.bases): # the code was written to support this but it has never been tested raise ValueError( 'elements having bases that have "attributes" or "children"' " are not currently supported" ) tmpl_env.tests.update({ "element": (lambda x: isinstance(x, ElementType)), "tagonly_e": (lambda x: isinstance(x, TagOnlyElement)), "list_e": list_type_or_base, "builtin_t": (lambda x: isinstance(x, BuiltinType)), "enumeration_t": (lambda x: isinstance(x, SchemaEnum)), "char_enum_t": (lambda x: isinstance(x, SchemaCharEnum)), "appends_str": (lambda x: isinstance(x, AddsToStringType)), "code_point_t": (lambda x: isinstance(x, CodePointType)), "sp_t": (lambda x: isinstance(x, CodePointType)), "used_directly": used_directly, "allow_text": allow_text, "has_attributes": has_attributes, "has_children": has_children, "has_children_or_content": has_children_or_content, "has_fields": lambda x: field_count(x) > 0, "has_children_or_tuple_content": has_children_or_tuple_content, "needs_finish_fields_call": needs_finish_fields_call, "needs_finish_call": needs_finish_call, "content_bare": content_type(ContentType.bare), "content_tuple": content_type(ContentType.tuple), "content_union": content_type(ContentType.union), "optional": optional, "array_field": array_field, }) tmpl_env.filters.update({ "field_count": field_count, "base_offsets": base_offsets, "children": children, "attributes": get_attributes, "content": content, "error": error, "Once": Once, }) tmpl_env.globals.update({ "types": sorted_types, "root_elements": list(schema.roots.items()), "element_names": elements, "attribute_names": attributes, "py_field_names": py_field_names, # "e_hash": generate_hash(elements), # "a_hash": generate_hash(attributes), # "py_f_hash": generate_hash(py_field_names), "union_tag_names": sorted(tag_names), "char_enum_chars": {c: i for i, c in enumerate(sorted(char_enum_chars))}, "list_element_field_counts": list(list_element_field_counts), "tagonly_and_tuple_field_counts": list(tagonly_and_tuple_field_counts), "tuple_field_counts": list(tuple_field_counts), "OtherAttrAction": OtherAttrAction, }) return tmpl_env class _NoDefault: pass _NO_DEFAULT = _NoDefault() def get_json_value( conv: Callable[[Any, str], T], context: str, d: dict[str, Any], key: str, default: T | _NoDefault = _NO_DEFAULT, ) -> T: r = d.get(key, _NO_DEFAULT) if r is _NO_DEFAULT: if default is _NO_DEFAULT: raise ValueError(f'missing value for "{context}.{key}"') return cast("T", default) return conv(r, context) def check_simple(t: type[T], name: str) -> Callable[[Any, str], T]: def inner(x, context: str) -> T: if isinstance(x, t): return x raise TypeError(f'value for "{context}" must be {name}') return inner get_json_bool = functools.partial(get_json_value, check_simple(bool, "a boolean")) get_json_int = functools.partial(get_json_value, check_simple(int, "an integer")) check_string = check_simple(str, "a string") get_json_str = functools.partial(get_json_value, check_string) check_obj = check_simple(cast("type[dict[str, Any]]", dict), "an object") get_json_obj = functools.partial(get_json_value, check_obj) check_list = check_simple(list, "an array") def get_json_mapping( item_conv: Callable[[Any, str], T], context: str, d: dict, key: str, default: dict[str, T] | _NoDefault = _NO_DEFAULT, ) -> dict[str, T]: def check(x, context): x = check_obj(x, context) return {key: item_conv(value, f"{context}.{key}") for key, value in x.items()} return get_json_value(check, context, d, key, default) def get_json_list( item_conv: Callable[[Any, str], T], context: str, d: dict, key: str, default: list[T] | _NoDefault = _NO_DEFAULT, ) -> list[T]: def check(x, context) -> list[T]: x = check_list(x, context) return [item_conv(value, f"{context}[{i}]") for i, value in enumerate(x)] return get_json_value(check, context, d, key, default) def check_zero_or_one(x, context: str) -> Literal[0] | Literal[1]: if x == 0: return 0 if x == 1: return 1 raise TypeError(f'value for "{context}" must be 0 or 1') get_json_zero_or_one = functools.partial(get_json_value, check_zero_or_one) def check_other_attr_action(x, context: str) -> OtherAttrAction: if x == "ignore": return OtherAttrAction.ignore if x == "error": return OtherAttrAction.error raise TypeError(f'value for "{context}" must be "error" or "ignore"') get_json_other_attr_action = functools.partial(get_json_value, check_other_attr_action) def check_typeref(x, context: str) -> TypeRef: x = check_obj(x, context) return TypeRef( "", get_json_str(context, x, "py_name", ""), get_json_str(context, x, "type"), get_json_bool(context, x, "is_list", False), get_json_zero_or_one(context, x, "min_items", 1), ) get_json_typeref = functools.partial(get_json_value, check_typeref) def check_attribute(x, context: str) -> Attribute: x = check_obj(x, context) return Attribute( "", get_json_str(context, x, "py_name", ""), get_json_str(context, x, "type"), get_json_bool(context, x, "optional", False), ) get_json_attribute = functools.partial(get_json_value, check_attribute) def check_enum_entry(x, context: str) -> EnumEntry: if isinstance(x, str): return EnumEntry(x, x) if isinstance(x, dict): xml = get_json_str(context, x, "xml") id = get_json_str(context, x, "id", xml) if not id.isidentifier(): raise ValueError(f'value of "{context}" is not a valid Python identifier') return EnumEntry(xml, id) raise TypeError(f'"{context}" must be a string or object') def make_tag_only_element(x: dict[str, Any], context: str) -> TagOnlyElement: return TagOnlyElement( "", get_json_list(check_string, context, x, "bases", []), get_json_mapping(check_attribute, context, x, "attributes", {}), get_json_other_attr_action(context, x, "other_attr", OtherAttrAction.error), get_json_mapping(check_typeref, context, x, "children", {}), False, ) def make_list_element(x: dict[str, Any], context: str, content_t: ContentType) -> ListElement: return ListElement( "", get_json_list(check_string, context, x, "bases", []), get_json_mapping(check_attribute, context, x, "attributes", {}), get_json_other_attr_action(context, x, "other_attr", OtherAttrAction.error), get_json_mapping(check_typeref, context, x, "children", {}), False, get_json_int(context, x, "min_items", 0), get_json_mapping(check_string, context, x, "content", {}), content_t, get_json_bool(context, x, "allow_text", False), ) def make_enumeration(x: dict[str, Any], context: str) -> SchemaEnum: return SchemaEnum("", get_json_list(check_enum_entry, context, x, "values")) def make_char_enumeration(x: dict[str, Any], context: str) -> SchemaCharEnum: return SchemaCharEnum("", get_json_str(context, x, "values")) def check_type(x, context: str) -> SchemaType: x = check_obj(x, context) kind = get_json_str(context, x, "kind") if kind == "tag_only_element": return make_tag_only_element(x, context) if kind == "list_element": return make_list_element(x, context, ContentType.bare) if kind == "union_list_element": return make_list_element(x, context, ContentType.union) if kind == "tuple_list_element": return make_list_element(x, context, ContentType.tuple) if kind == "enumeration": return make_enumeration(x, context) if kind == "char_enumeration": return make_char_enumeration(x, context) raise ValueError( f'"{context}.kind" must be "tag_only_element", "list_element", ' '"mixed_element" or "enumeration"' ) get_json_type = functools.partial(get_json_value, check_type) def check_schema(x) -> Schema: if not isinstance(x, dict): raise TypeError("json value must be an object") r = Schema( get_json_mapping(check_string, "", x, "roots"), get_json_mapping(check_type, "", x, "types", {}), ) r.types["#spType"] = SpType("spType", "str") for t, py in BUILTIN_ATTR_SCHEMA_TYPES: r.types["#" + t] = BuiltinAttributeType(t, py) return r def generate_from_json(json_path, template_files) -> None: with open(json_path, "rb") as ifile: schema = check_schema(json.load(ifile)) env = make_env(schema) for i_file, o_file in template_files: template_str = Path(i_file).read_text(encoding="utf-8") with open(o_file, "w", encoding="utf-8") as ofile: env.from_string(template_str).stream().dump(ofile) ================================================ FILE: xml_parser_generator/module_template.py.in ================================================ """ Python module to parse Doxygen's XML output. This module defines the following types: - TaggedValue TaggedValue is a name/value pair for tagged union values. - Node Node is an empty class used as a base type for all classes that start with "Node_". - Node_X These classes are generated according the input schema. - ListItem_X Types that have "kind" equal to "tuple_list_element" in the schema also have a companion class for their elements. It will have the same name as the main class except it starts with ListItem_ instead of Node_. These are named tuples. - ParseError The exception raised when there is a problem with the XML input that cannot be ignored. - ParseWarning The warning class for possible problems in the XML input. Currently this is only issued for unexpected elements and attributes. Each non-simple type has some or all of the following entities: - _node_class_attr__X Attribute handlers for element X. This is a mapping of attribute names to functions that handle the attributes. - def _node_class_attr_end__X(state: _ParseState, obj) This is called after all attributes are handled. It is used to check for unset fields and to fill them with default values or raise an exception. This is a separate function so that derived elements can use it. - _node_class_child__X Element handlers for element X. This is a mapping of element names to functions that handle the elements. - def _node_class_finish_fields__X(state: _ParseState, obj) This is called after all child elements are handled. It is used to check for unset fields and to fill them with default values or raise an exception. This is a separate function so that derived elements can use it. If the type is used directly, it will have its own class and the following function: - def _node_class_start__X(state: _ParseState, setter: Callable, attr: Iterable[tuple[str, str]]): This has three responsibilities: - To create the object corresponding to element X. - To handle the XML attributes. - To add the new object and the appropriate XML event handlers to the top of the parser stack. This function doesn't return a value immediately, instead "setter" is called with the value when it's ready. """ from __future__ import annotations import enum import functools import warnings from typing import ( TYPE_CHECKING, Literal, NamedTuple, ) from xml.parsers import expat if TYPE_CHECKING: import sys from collections.abc import Iterable, Sequence from typing import ( Any, Callable, ClassVar, Generic, NoReturn, TypeVar, Union, ) if sys.version_info >= (3, 11): from typing import TypeAlias else: from typing_extensions import TypeAlias T = TypeVar("T") T_covar = TypeVar("T_covar", covariant=True) U_covar = TypeVar("U_covar", covariant=True) class ParseError(RuntimeError): @property def message(self, /) -> str: return self.args[0] @property def lineno(self, /) -> int: return self.args[1] def __str__(self, /) -> str: if self.lineno is None: return "Error: " + self.message return f"Error on line {self.lineno}: {self.message}" class ParseWarning(UserWarning): pass class Node: __slots__ = () _fields: ClassVar[tuple[str, ...]] # This needs to run on Python 3.8, where built-in types don't implement # __class_getitem__, and Python 3.9 and 3.10, which don't allow # multiple-inheritance with NamedTuple. if TYPE_CHECKING: class ListNode(list[T], Node, Generic[T]): ... class TaggedValue(NamedTuple, Generic[T_covar, U_covar]): name: T_covar value: U_covar else: class TaggedValue(NamedTuple): name: str value: Any __class_getitem__ = classmethod(lambda cls, x: cls) class ListNode(list, Node): __slots__ = () __class_getitem__ = classmethod(lambda cls, x: cls) //% macro emit_fields(type) {%- for b in type.bases %}{$ emit_fields(b) $}{% endfor -%} //% for ref in type|attributes {$ ref.py_name $}: {$ ref.py_type() $} //% endfor //% for ref in type|children {$ ref.py_name $}: {$ ref.py_type() $} //% endfor //% endmacro //% macro emit_content_fields(type) {%- for b in type.bases %}{$ emit_content_fields(b) $}{% endfor -%} //% for cname,ctype in type|content {$ cname $}: {$ ctype.py_name $} //% endfor //% endmacro //% for type in types //% if type is element //% if type is content_union //% set members = type.py_union_list(true)|sort //% if members|length > 1 if TYPE_CHECKING: ListItem_{$ type $}: TypeAlias = Union[ //% for m in members {$ ', ' if not loop.first $}{$ m $} //% endfor ] //% endif //% endif //% if type is used_directly //% if type is content_tuple //% set list_item_type = 'ListItem_'~type class ListItem_{$ type $}(NamedTuple): //% for cname,ctype in type|content {$ cname $}: {$ ctype.py_name $} //% endfor //% elif type is content_union //% if members|length > 1 //% set list_item_type = 'ListItem_'~type //% else //% set list_item_type = members|first //% endif //% elif type is content_bare //% set list_item_type = (type|content|first)[1].py_name //% elif type is list_e {$ "invalid content type"|error $} //% endif class Node_{$ type $}({$ 'ListNode["'~list_item_type~'"]' if type is list_e else 'Node' $}): __slots__ = ( //% for f in type.all_fields() "{$ f.py_name $}", //% endfor ) _fields = __slots__ //% if type is list_e or type is has_fields def __init__( self, //% if type is list_e __children, //% endif //% for f in type.all_fields() if f is not optional {$ f.py_name $}: {$ f.py_type(true) $}, //% endfor //% for f in type.all_fields() if f is optional {$ f.py_name $}: {$ f.py_type(true) $} = {$ '()' if f is array_field else 'None' $}, //% endfor ): # pragma: no cover //% if type is list_e super().__init__(__children) //% endif //% for f in type.all_fields() //% if f is array_field self.{$ f.py_name $} = {$ f.py_name $} if _GLOBAL_type({$ f.py_name $}) is _GLOBAL_list else _GLOBAL_list({$ f.py_name $}) //% else self.{$ f.py_name $} = {$ f.py_name $} //% endif //% endfor //% endif //% endif //% elif type is enumeration_t class {$ type $}(enum.Enum): //% for entry in type.children {$ entry.id $} = "{$ entry.xml $}" //% endfor //% elif type is char_enum_t {$ type $} = Literal[{% for c in type.values %}{$ "'"~c~"'" $}{$ ',' if not loop.last $}{% endfor %}] //% endif //% endfor def parse_str(data: str, /): return _parse(data, expat.XMLParserType.Parse) def parse_file(file, /): return _parse(file, expat.XMLParserType.ParseFile) if TYPE_CHECKING: _ChildStartCallback = Callable[["_ParseState", Any, Iterable[tuple[str, str]]], None] _FinishCallback = Callable[["_ParseState"], None] _TextCallback = Callable[["_ParseState", str], None] _Setter = Callable[[Any], None] _T_covar = TypeVar("_T_covar", covariant=True) _U_covar = TypeVar("_U_covar", covariant=True) _GLOBAL_type = type _GLOBAL_list = list class _ParseCallbacks: __slots__ = "value", "setter", "cs_call", "f_call", "t_call" value: Any """The value corresponding the currently visited XML element.""" setter: _Setter | None """A callback given by the parent element to consume the value. This may be None if no action is needed. """ cs_call: dict[str, _ChildStartCallback] | None """A mapping of element names to callbacks for a children of the current element. This may be None if no child elements are allowed. """ f_call: _FinishCallback | None """A callback for when the current element is closed. This may be None if no action is needed. """ t_call: _TextCallback | None """A callback for text contained directly inside the current element. This may be None if text is not allowed. If None, whitespace is ignored. """ def __init__(self, value=None, setter=None, cs_call=None, f_call=None, t_call=None): self.value = value self.setter = setter self.cs_call = cs_call self.f_call = f_call self.t_call = t_call class _ParseState: def __init__(self, parser: expat.XMLParserType, /): self.parser = parser self.parse_callbacks: list[_ParseCallbacks] = [] # While this is greater than zero all XML content is ignored. # # This starts at zero. When an unexpected element start is encountered, # a warning is issued (via PyErr_WarnFormat) and this is set to 1. Any # subsequent element-starts increment this and element-ends decrement # this until this is zero again, and normal parsing resumes. self.ignore_level: int = 0 def start_element(self, name: str, attrs: dict[str, str], /) -> None: if self.ignore_level: self.ignore_level += 1 return cb = self.parse_callbacks[-1] if cb.cs_call is not None: handler = cb.cs_call.get(name) if handler is not None: handler(self, cb.value, attrs.items()) return self.set_parse_warning(f'unexpected element "{name}"') self.ignore_level = 1 def end_element(self, unused, /) -> None: if self.ignore_level: self.ignore_level -= 1 return cb = self.parse_callbacks[-1] if cb.f_call is not None: cb.f_call(self) if cb.setter is not None: cb.setter(cb.value) self.parse_callbacks.pop() def character_data(self, s: str, /) -> None: if self.ignore_level: return cb = self.parse_callbacks[-1] if cb.t_call is not None: cb.t_call(self, s) elif s and not s.isspace(): self.set_parse_warning("unexpected character data") def raise_parse_error(self, msg, /) -> NoReturn: raise ParseError(msg, self.parser.CurrentLineNumber) def set_parse_warning(self, msg, /) -> None: warnings.warn(ParseWarning(f'Warning on line {self.parser.CurrentLineNumber}: {msg}')) def _node_list_common_text(state: _ParseState, data: str, /): value = state.parse_callbacks[-1].value if value and type(value[-1]) is str: value[-1] += data else: value.append(data) def _push_tuple_item( state: _ParseState, tuple_i: int, tag_names: Sequence[str], cls, obj, / ): if tuple_i == 0: if len(obj): tuple_size = len(tag_names) if len(obj[-1]) < tuple_size: state.raise_parse_error( f'"{tag_names[0]}" element can only come after "{tag_names[tuple_size-1]}" element or be the first in its group', ) obj[-1] = cls._make(obj[-1]) # tuples are immutable so a list is used while collecting the values new_tuple: list[Any] = [] obj.append(new_tuple) return new_tuple.append if not obj or len(obj[-1]) < tuple_i: state.raise_parse_error( f'"{tag_names[tuple_i]}" element can only come after "{tag_names[tuple_i-1]}" element' ) return obj[-1].append def _check_complete_tuple(state: _ParseState, tag_names: Sequence[str], cls, obj, /): if obj: last = obj[-1] if len(last) != len(tag_names): state.raise_parse_error( f'"{tag_names[len(last)]}" element must come after "{tag_names[len(last)-1]}" element' ) obj[-1] = cls._make(last) def _warn_unexpected_attribute(state: _ParseState, name: str, /): state.set_parse_warning(f'unexpected attribute "{name}"') def _raise_missing_attribute_error(state: _ParseState, name: str, /): state.raise_parse_error(f'missing "{name}" attribute') def _raise_duplicate_element_error(state: _ParseState, name: str, /): state.raise_parse_error(f'"{name}" cannot appear more than once in this context') def _raise_missing_element_error(state: _ParseState, parent: Any, name: str, /): state.raise_parse_error(f'"{parent}" missing "{name}" child') def _raise_empty_list_element_error(state: _ParseState, name: str, /): state.raise_parse_error(f'at least one "{name}" child is required') def _raise_invalid_int_error(state: _ParseState, value: str, /): state.raise_parse_error(f'"{value}" is not a valid integer') def _raise_invalid_enum_error(state: _ParseState, value: str, /): state.raise_parse_error(f'"{value}" is not one of the allowed enumeration values') def _raise_invalid_char_enum_error(state: _ParseState, c: str, allowed: str, /): state.raise_parse_error(f'"{c}" is not one of the allowed character values; must be one of "{allowed}"') def _parse_DoxBool_attribute(state: _ParseState, name: str, value: str, /) -> bool: if value == "yes": return True if value == "no": return False state.raise_parse_error(f'"{name}" must be "yes" or "no"') def _node_string_text(state: _ParseState, data: str) -> None: state.parse_callbacks[-1].value += data def _node_start_string(state: _ParseState, setter: _Setter, attr: Iterable[tuple[str, str]], /): for name, _ in attr: _warn_unexpected_attribute(state, name) state.parse_callbacks.append(_ParseCallbacks('', setter, None, None, _node_string_text)) def _node_start_empty(state: _ParseState, setter: _Setter, attr: Iterable[tuple[str, str]], /): for name, _ in attr: _warn_unexpected_attribute(state, name) setter(None) state.parse_callbacks.append(_ParseCallbacks()) def _node_start_spType(state: _ParseState, attr: Iterable[tuple[str, str]], /): c = ' ' for name, value in attr: if name != "value": _warn_unexpected_attribute(state, name) try: c_i = int(value, 10) except ValueError: state.raise_parse_error('"value" must be a valid integer') if 0 > c_i > 127: state.raise_parse_error('"value" must be between 0 and 127') c = chr(c_i) state.parse_callbacks.append(_ParseCallbacks()) return c def _node_start_const_char(state: _ParseState, attr: Iterable[tuple[str, str]], /): for name, _ in attr: _warn_unexpected_attribute(state, name) state.parse_callbacks.append(_ParseCallbacks()) def _union_codepoint_element(c): def inner(state: _ParseState, obj, attr: Iterable[tuple[str, str]], /) -> None: if obj and type(obj[-1]) is str: obj[-1] += c else: obj.append(c) _node_start_const_char(state, attr) return inner _cur_list: dict[str, Callable] def _add_to_list(name): def inner(f): global _cur_list _cur_list[name] = f return inner //% for type in types //% if type is element //% if type is has_attributes _node_class_attr__{$ type $} = _cur_list = {} //% for b in type.bases|select('has_attributes') _node_class_attr__{$ type $}.update(node_class_attr__{$ b $}) //% endfor //% for attr in type|attributes @_add_to_list("{$ attr.name $}") def _a__{$ type $}__{$ attr.name $}(state: _ParseState, obj, value: str, /): //% if attr.type is builtin_t //% if attr.type.name == "string" obj.{$ attr.py_name $} = value //% elif attr.type.name == "integer" try: obj.{$ attr.py_name $} = int(value, 10) except ValueError: _raise_invalid_int_error(state, value) //% else obj.{$ attr.py_name $} = _parse_{$ attr.type $}_attribute(state, "{$ attr.name $}", value) //% endif //% elif attr.type is enumeration_t try: obj.{$ attr.py_name $} = {$ attr.type $}(value.strip()) except ValueError: _raise_invalid_enum_error(state, value) //% else obj.{$ attr.py_name $} = _parse__{$ attr.type $}(state, value) //% endif //% endfor def _node_class_attr_end__{$ type $}(state: _ParseState, obj, /): //% for b in type.bases if b is has_attributes _node_class_attr_end__{$ b $}(state, obj) //% endfor //% for ref in type|attributes if not hasattr(obj, "{$ ref.py_name $}"): //% if ref.optional obj.{$ ref.py_name $} = None //% else _raise_missing_attribute_error(state,"{$ ref.name $}") //% endif //% endfor //% endif //% if type is has_children_or_content _node_class_child__{$ type $} = _cur_list = {} //% for b in type.bases|select('has_children_or_content') _node_class_child__{$ type $}.update(_node_class_child__{$ b $}) //% endfor //% for cref in type|children @_add_to_list("{$ cref.name $}") def _e__{$ type $}__{$ cref.name $}(state: _ParseState, obj, attr: Iterable[tuple[str, str]], /): //% if cref.is_list _node_{$ 'start_' if cref.type is builtin_t else 'class_start__' $}{$ cref.type $}( state, obj.{$ cref.py_name $}.append, attr{$ cref.type.extra_args $}) //% else if hasattr(obj, "{$ cref.py_name $}"): _raise_duplicate_element_error(state, "{$ cref.name $}") _node_{$ 'start_' if cref.type is builtin_t else 'class_start__' $}{$ cref.type $}( state, functools.partial(setattr, obj, "{$ cref.py_name $}"), attr{$ cref.type.extra_args $}) //% endif //% endfor //% for cname,ctype in type|content //% if type is content_union and ctype is code_point_t _add_to_list("{$ cname $}")(_union_codepoint_element(chr({$ ctype.char $}))) //% else @_add_to_list("{$ cname $}") def _e__{$ type $}__{$ cname $}(state: _ParseState, obj, attr: Iterable[tuple[str, str]], /): //% if type is content_tuple _node_{$ 'start_' if ctype is builtin_t else 'class_start__' $}{$ ctype $}( state, _push_tuple_item( state, {$ loop.index0 $}, _tuple_item_tag_names__{$ type $}, ListItem_{$ type $}, obj), attr{$ ctype.extra_args $}) //% elif type is content_union //% if ctype is appends_str c = _node_{$ 'start_' if ctype is builtin_t else 'class_start__' $}{$ ctype $}(state, attr) if obj and type(obj[-1]) is str: obj[-1] += c else: obj.append(c) //% else _node_{$ 'start_' if ctype is builtin_t else 'class_start__' $}{$ ctype $}( state, (lambda x: obj.append(TaggedValue("{$ cname $}", x))), attr{$ ctype.extra_args $}) //% endif //% else _node_{$ 'start_' if ctype is builtin_t else 'class_start__' $}{$ ctype $}( state, obj.append, attr{$ ctype.extra_args $}) //% endif //% endif //% endfor //% endif //% if type is used_directly //% if type is content_tuple _tuple_item_tag_names__{$ type $} = ListItem_{$ type $}._fields //% elif type is content_union //% elif type is content_bare //% set list_item_type = (type|content|first)[1].py_name //% elif type is list_e {$ "invalid content type"|error $} //% endif def _node_class_start__{$ type $}(state: _ParseState, setter: Callable, attr: Iterable[tuple[str, str]], /): n = Node_{$ type $}.__new__(Node_{$ type $}) //% for ref in type|children if ref.is_list n.{$ ref.py_name $} = [] //% endfor //% if type is has_attributes or type.other_attr == OtherAttrAction.error for name, value in attr: //% if type is has_attributes handler = _node_class_attr__{$ type $}.get(name) if handler is not None: handler(state, n, value) //% if type.other_attr == OtherAttrAction.error else: _warn_unexpected_attribute(state, name) //% endif //% else _warn_unexpected_attribute(state, name) //% endif //% endif //% if type is has_attributes _node_class_attr_end__{$ type $}(state, n) //% endif state.parse_callbacks.append(_ParseCallbacks( n, setter, //% if type is has_children_or_content _node_class_child__{$ type $}, //% else None, //% endif //% if type is needs_finish_call _node_class_finish__{$ type $}, //% else None, //% endif //% if type is allow_text _node_list_common_text, //% else None, //% endif )) //% if type is needs_finish_fields_call def _node_class_finish_fields__{$ type $}(state: _ParseState, obj, /) -> None: //% for b in type.bases|select('needs_finish_fields_call') _node_class_finish_fields__{$ b $}(state, obj) //% endfor //% for ref in type|children //% if ref.min_items //% if ref.is_list if len(obj.{$ ref.py_name $}) < 1: _raise_empty_list_element_error(state,"{$ ref.name $}") //% else if not hasattr(obj, "{$ ref.py_name $}"): _raise_missing_element_error(state, obj, "{$ ref.name $}") //% endif //% elif not ref.is_list if not hasattr(obj, "{$ ref.py_name $}"): obj.{$ ref.py_name $} = None //% endif //% endfor //% endif //% if type is needs_finish_call def _node_class_finish__{$ type $}(state: _ParseState, /): n = state.parse_callbacks[-1].value //% if type is needs_finish_fields_call _node_class_finish_fields__{$ type $}(state, n) //% endif //% if type is content_tuple and type.content|length > 1 _check_complete_tuple(state, _tuple_item_tag_names__{$ type $}, ListItem_{$ type $}, n) //% endif //% endif //% endif //% elif type is char_enum_t def _parse__{$ type $}(state: _ParseState, data, /): data = data.strip() if len(data) != 1: state.raise_parse_error("value must be a single character") if data not in "{$ type.values $}": _raise_invalid_char_enum_error(state, data,"{$ type.values $}") return data //% endif //% endfor _top_level_handlers = _cur_list = {} //% for name,type in root_elements @_add_to_list("{$ name $}") def _(state: _ParseState, obj, attr: Iterable[tuple[str, str]], /): cb = state.parse_callbacks[-1] if obj is not None: state.raise_parse_error("cannot have more than one root element") def setter(x): cb.value = TaggedValue("{$ name $}", x) return _node_{$ 'start_' if ctype is builtin_t else 'class_start__' $}{$ type $}(state, setter, attr) //% endfor def _parse(obj, meth, /): p = expat.ParserCreate() state = _ParseState(p) state.parse_callbacks.append(_ParseCallbacks( None, None, _top_level_handlers)) p.StartElementHandler = state.start_element p.EndElementHandler = state.end_element p.CharacterDataHandler = state.character_data try: meth(p, obj) except expat.ExpatError as e: raise ParseError(expat.errors.messages[e.code], e.lineno) finally: # break reference cycle for faster garbage collection p.StartElementHandler = None p.EndElementHandler = None p.CharacterDataHandler = None value = state.parse_callbacks[0].value if value is None: raise ParseError("document without a recognized root element", None) return value ================================================ FILE: xml_parser_generator/schema.json ================================================ { "roots": { "doxygen": "DoxygenType", "doxygenindex": "DoxygenTypeIndex" }, "types": { "DoxygenTypeIndex": { "kind": "tag_only_element", "attributes": { "version": {"type": "#string"} }, "other_attr": "ignore", "children": { "compound": {"type": "CompoundType", "is_list": true, "min_items": 0} } }, "CompoundType": { "kind": "tag_only_element", "attributes": { "refid": {"type": "#string"}, "kind": {"type": "CompoundKind"} }, "children": { "name": {"type": "#string"}, "member": {"type": "MemberType", "is_list": true, "min_items": 0} } }, "DoxygenType": { "kind": "tag_only_element", "attributes": { "version": {"type": "#string"} }, "other_attr": "ignore", "children": { "compounddef": {"type": "compounddefType", "is_list": true, "min_items": 0} } }, "compounddefType": { "kind": "tag_only_element", "attributes": { "abstract": {"type": "#DoxBool", "optional": true}, "final": {"type": "#DoxBool", "optional": true}, "id": {"type": "#string"}, "inline": {"type": "#DoxBool", "optional": true}, "kind": {"type": "DoxCompoundKind"}, "language": {"type": "DoxLanguage", "optional": true}, "prot": {"type": "DoxProtectionKind", "optional": true}, "sealed": {"type": "#DoxBool", "optional": true} }, "children": { "basecompoundref": {"type": "compoundRefType", "is_list": true, "min_items": 0}, "briefdescription": {"type": "descriptionType", "min_items": 0}, "collaborationgraph": {"type": "graphType", "min_items": 0}, "compoundname": {"type": "#string"}, "derivedcompoundref": {"type": "compoundRefType", "is_list": true, "min_items": 0}, "detaileddescription": {"type": "descriptionType", "min_items": 0}, "exports": {"type": "exportsType", "min_items": 0}, "incdepgraph": {"type": "graphType", "min_items": 0}, "includedby": {"type": "incType", "is_list": true, "min_items": 0}, "includes": {"type": "incType", "is_list": true, "min_items": 0}, "inheritancegraph": {"type": "graphType", "min_items": 0}, "initializer": {"type": "linkedTextType", "min_items": 0}, "innerclass": {"type": "refType", "is_list": true, "min_items": 0}, "innerconcept": {"type": "refType", "is_list": true, "min_items": 0}, "innerdir": {"type": "refType", "is_list": true, "min_items": 0}, "innerfile": {"type": "refType", "is_list": true, "min_items": 0}, "innergroup": {"type": "refType", "is_list": true, "min_items": 0}, "innermodule": {"type": "refType", "is_list": true, "min_items": 0}, "innernamespace": {"type": "refType", "is_list": true, "min_items": 0}, "innerpage": {"type": "refType", "is_list": true, "min_items": 0}, "invincdepgraph": {"type": "graphType", "min_items": 0}, "listofallmembers": {"type": "listofallmembersType", "min_items": 0}, "location": {"type": "locationType", "min_items": 0}, "programlisting": {"type": "listingType", "min_items": 0}, "qualifier": {"type": "#string", "is_list": true, "min_items": 0}, "requiresclause": {"type": "linkedTextType", "min_items": 0}, "sectiondef": {"type": "sectiondefType", "is_list": true, "min_items": 0}, "tableofcontents": {"type": "tableofcontentsType", "min_items": 0}, "templateparamlist": {"type": "templateparamlistType", "min_items": 0}, "title": {"type": "#string", "min_items": 0} } }, "graphType": { "kind": "tag_only_element", "children": { "node": {"type": "nodeType", "is_list": true, "min_items": 1} } }, "templateparamlistType": { "kind": "tag_only_element", "children": { "param": {"type": "paramType", "is_list": true, "min_items": 0} } }, "sectiondefType": { "kind": "tag_only_element", "attributes": { "kind": {"type": "DoxSectionKind"} }, "children": { "description": {"type": "descriptionType", "min_items": 0}, "header": {"type": "#string", "min_items": 0}, "member": {"type": "MemberType", "is_list": true, "min_items": 0}, "memberdef": {"type": "memberdefType", "is_list": true, "min_items": 0} } }, "tableofcontentsType": { "kind": "tag_only_element", "children": { "tocsect": {"type": "tableofcontentsKindType", "is_list": true, "min_items": 1}, "tableofcontents": {"type": "tableofcontentsType", "is_list": true, "min_items": 0} } }, "linkedTextType": { "kind": "union_list_element", "allow_text": true, "content": { "ref": "refTextType" } }, "descriptionType": { "kind": "union_list_element", "allow_text": true, "children": { "title": {"type": "#string", "min_items": 0} }, "content": { "internal": "docInternalType", "para": "docParaType", "sect1": "docSect1Type", "sect2": "docSect2Type", "sect3": "docSect3Type" } }, "exportsType": { "kind": "tag_only_element", "children": { "export": {"type": "exportType", "is_list": true, "min_items": 1} } }, "listingType": { "kind": "tag_only_element", "attributes": { "filename": {"type": "#string", "optional": true} }, "children": { "codeline": {"type": "codelineType", "is_list": true, "min_items": 0} } }, "locationType": { "kind": "tag_only_element", "attributes": { "bodyend": {"type": "#integer", "optional": true}, "bodyfile": {"type": "#string", "optional": true}, "bodystart": {"type": "#integer", "optional": true}, "column": {"type": "#integer", "optional": true}, "declcolumn": {"type": "#integer", "optional": true}, "declfile": {"type": "#string", "optional": true}, "declline": {"type": "#integer", "optional": true}, "file": {"type": "#string"}, "line": {"type": "#integer", "optional": true} } }, "listofallmembersType": { "kind": "tag_only_element", "children": { "member": {"type": "memberRefType", "is_list": true, "min_items": 0} } }, "memberRefType": { "kind": "tag_only_element", "attributes": { "ambiguityscope": {"type": "#string", "optional": true}, "prot": {"type": "DoxProtectionKind"}, "refid": {"type": "#string"}, "virt": {"type": "DoxVirtualKind"} }, "children": { "name": {"type": "#string"}, "scope": {"type": "#string"} } }, "memberdefType": { "kind": "tag_only_element", "attributes": { "accessor": {"type": "DoxAccessor", "optional": true}, "add": {"type": "#DoxBool", "optional": true}, "attribute": {"type": "#DoxBool", "optional": true}, "bound": {"type": "#DoxBool", "optional": true}, "const": {"type": "#DoxBool", "optional": true}, "constexpr": {"type": "#DoxBool", "optional": true}, "consteval": {"type": "#DoxBool", "optional": true}, "constinit": {"type": "#DoxBool", "optional": true}, "constrained": {"type": "#DoxBool", "optional": true}, "explicit": {"type": "#DoxBool", "optional": true}, "extern": {"type": "#DoxBool", "optional": true}, "final": {"type": "#DoxBool", "optional": true}, "gettable": {"type": "#DoxBool", "optional": true}, "id": {"type": "#string"}, "initonly": {"type": "#DoxBool", "optional": true}, "inline": {"type": "#DoxBool", "optional": true}, "kind": {"type": "DoxMemberKind"}, "maybeambiguous": {"type": "#DoxBool", "optional": true}, "maybedefault": {"type": "#DoxBool", "optional": true}, "maybevoid": {"type": "#DoxBool", "optional": true}, "mutable": {"type": "#DoxBool", "optional": true}, "new": {"type": "#DoxBool", "optional": true}, "nodiscard": {"type": "#DoxBool", "optional": true}, "noexcept": {"type": "#DoxBool", "optional": true}, "noexceptexpression": {"type": "#string", "optional": true}, "optional": {"type": "#DoxBool", "optional": true}, "privategettable": {"type": "#DoxBool", "optional": true}, "privatesettable": {"type": "#DoxBool", "optional": true}, "property": {"type": "#DoxBool", "optional": true}, "prot": {"type": "DoxProtectionKind"}, "protectedgettable": {"type": "#DoxBool", "optional": true}, "protectedsettable": {"type": "#DoxBool", "optional": true}, "raise": {"type": "#DoxBool", "optional": true, "py_name": "raise_"}, "readable": {"type": "#DoxBool", "optional": true}, "readonly": {"type": "#DoxBool", "optional": true}, "refqual": {"type": "DoxRefQualifierKind", "optional": true}, "removable": {"type": "#DoxBool", "optional": true}, "remove": {"type": "#DoxBool", "optional": true}, "required": {"type": "#DoxBool", "optional": true}, "sealed": {"type": "#DoxBool", "optional": true}, "settable": {"type": "#DoxBool", "optional": true}, "static": {"type": "#DoxBool"}, "strong": {"type": "#DoxBool", "optional": true}, "transient": {"type": "#DoxBool", "optional": true}, "virt": {"type": "DoxVirtualKind", "optional": true}, "volatile": {"type": "#DoxBool", "optional": true}, "writable": {"type": "#DoxBool", "optional": true} }, "children": { "argsstring": {"type": "#string", "min_items": 0}, "bitfield": {"type": "#string", "min_items": 0}, "briefdescription": {"type": "descriptionType", "min_items": 0}, "definition": {"type": "#string", "min_items": 0}, "detaileddescription": {"type": "descriptionType", "min_items": 0}, "enumvalue": {"type": "enumvalueType", "is_list": true, "min_items": 0}, "exceptions": {"type": "linkedTextType", "min_items": 0}, "inbodydescription": {"type": "descriptionType", "min_items": 0}, "initializer": {"type": "linkedTextType", "min_items": 0}, "location": {"type": "locationType"}, "name": {"type": "#string"}, "param": {"type": "paramType", "is_list": true, "min_items": 0}, "qualifiedname": {"type": "#string", "min_items": 0}, "qualifier": {"type": "#string", "is_list": true, "min_items": 0}, "read": {"type": "#string", "min_items": 0}, "referencedby": {"type": "referenceType", "is_list": true, "min_items": 0}, "references": {"type": "referenceType", "is_list": true, "min_items": 0}, "reimplementedby": {"type": "reimplementType", "is_list": true, "min_items": 0}, "reimplements": {"type": "reimplementType", "is_list": true, "min_items": 0}, "requiresclause": {"type": "linkedTextType", "min_items": 0}, "templateparamlist": {"type": "templateparamlistType", "min_items": 0}, "type": {"type": "linkedTextType", "min_items": 0}, "write": {"type": "#string", "min_items": 0} } }, "MemberType": { "kind": "tag_only_element", "attributes": { "kind": {"type": "MemberKind"}, "refid": {"type": "#string"} }, "children": { "name": {"type": "#string"} } }, "paramType": { "kind": "tag_only_element", "children": { "array": {"type": "#string", "min_items": 0}, "attributes": {"type": "#string", "min_items": 0}, "briefdescription": {"type": "descriptionType", "min_items": 0}, "declname": {"type": "#string", "min_items": 0}, "defname": {"type": "#string", "min_items": 0}, "defval": {"type": "linkedTextType", "min_items": 0}, "type": {"type": "linkedTextType", "min_items": 0}, "typeconstraint": {"type": "linkedTextType", "min_items": 0} } }, "enumvalueType": { "kind": "tag_only_element", "attributes": { "id": {"type": "#string"}, "prot": {"type": "DoxProtectionKind"} }, "children": { "briefdescription": {"type": "descriptionType", "min_items": 0}, "detaileddescription": {"type": "descriptionType", "min_items": 0}, "initializer": {"type": "linkedTextType", "min_items": 0}, "name": {"type": "#string"} } }, "referenceType": { "kind": "union_list_element", "allow_text": true, "attributes": { "compoundref": {"type": "#string", "optional": true}, "endline": {"type": "#integer", "optional": true}, "refid": {"type": "#string"}, "startline": {"type": "#integer", "optional": true} } }, "docInternalType": { "kind": "union_list_element", "allow_text": true, "content": { "para": "docParaType", "sect1": "docSect1Type" } }, "docSect1Type": { "kind": "union_list_element", "allow_text": true, "attributes": { "id": {"type": "#string"} }, "children": { "title": {"type": "docTitleType", "min_items": 0} }, "content": { "para": "docParaType", "sect2": "docSect2Type", "sect3": "docSect3Type", "internal": "docInternalS1Type" } }, "nodeType": { "kind": "tag_only_element", "attributes": { "id": {"type": "#string"} }, "children": { "childnode": {"type": "childnodeType", "is_list": true, "min_items": 0}, "label": {"type": "#string"}, "link": {"type": "linkType", "min_items": 0} } }, "linkType": { "kind": "tag_only_element", "attributes": { "external": {"type": "#string", "optional": true}, "refid": {"type": "#string"} } }, "childnodeType": { "kind": "tag_only_element", "attributes": { "refid": {"type": "#string"}, "relation": {"type": "DoxGraphRelation"} }, "children": { "edgelabel": {"type": "#string", "is_list": true, "min_items": 0} } }, "codelineType": { "kind": "tag_only_element", "attributes": { "external": {"type": "#DoxBool", "optional": true}, "lineno": {"type": "#integer", "optional": true}, "refid": {"type": "#string", "optional": true}, "refkind": {"type": "DoxRefKind", "optional": true} }, "children": { "highlight": {"type": "highlightType", "is_list": true, "min_items": 0} } }, "highlightType": { "kind": "union_list_element", "allow_text": true, "attributes": { "class": {"type": "DoxHighlightClass", "py_name": "class_"} }, "content": { "sp": "#spType", "ref": "refTextType" } }, "docInternalS1Type": { "kind": "union_list_element", "allow_text": true, "content": { "para": "docParaType", "sect2": "docSect2Type" } }, "docSect2Type": { "kind": "union_list_element", "allow_text": true, "attributes": { "id": {"type": "#string"} }, "children": { "title": {"type": "docTitleType", "min_items": 0} }, "content": { "para": "docParaType", "sect3": "docSect3Type", "internal": "docInternalS2Type" } }, "docSect3Type": { "kind": "union_list_element", "allow_text": true, "attributes": { "id": {"type": "#string"} }, "children": { "title": {"type": "docTitleType", "min_items": 0} }, "content": { "para": "docParaType", "sect4": "docSect4Type", "internal": "docInternalS3Type" } }, "docInternalS2Type": { "kind": "union_list_element", "allow_text": true, "content": { "para": "docParaType", "sect3": "docSect3Type" } }, "docSect4Type": { "kind": "union_list_element", "allow_text": true, "attributes": { "id": {"type": "#string"} }, "children": { "title": {"type": "docTitleType", "min_items": 0} }, "content": { "para": "docParaType", "sect5": "docSect5Type", "internal": "docInternalS4Type" } }, "docSect5Type": { "kind": "union_list_element", "allow_text": true, "attributes": { "id": {"type": "#string"} }, "children": { "title": {"type": "docTitleType", "min_items": 0} }, "content": { "para": "docParaType", "sect6": "docSect6Type", "internal": "docInternalS5Type" } }, "docSect6Type": { "kind": "union_list_element", "allow_text": true, "attributes": { "id": {"type": "#string"} }, "children": { "title": {"type": "docTitleType", "min_items": 0} }, "content": { "para": "docParaType", "internal": "docInternalS6Type" } }, "docInternalS3Type": { "kind": "union_list_element", "allow_text": true, "content": { "para": "docParaType", "sect3": "docSect4Type" } }, "docInternalS4Type": { "kind": "union_list_element", "allow_text": true, "content": { "para": "docParaType", "sect5": "docSect4Type" } }, "docInternalS5Type": { "kind": "union_list_element", "allow_text": true, "content": { "para": "docParaType", "sect6": "docSect4Type" } }, "docInternalS6Type": { "kind": "union_list_element", "allow_text": true, "content": { "para": "docParaType" } }, "docListItemType": { "kind": "list_element", "attributes": { "override": {"type": "DoxCheck", "optional": true}, "value": {"type": "#integer", "optional": true} }, "content": { "para": "docParaType" } }, "docCaptionType": { "kind": "union_list_element", "allow_text": true, "attributes": { "id": {"type": "#string"} } }, "docRowType": { "kind": "tag_only_element", "children": { "entry": {"type": "docEntryType", "is_list": true, "min_items": 0} } }, "docEntryType": { "kind": "tag_only_element", "any_attr": true, "attributes": { "align": {"type": "DoxAlign", "optional": true}, "class": {"type": "#string", "py_name": "class_", "optional": true}, "colspan": {"type": "#integer", "optional": true}, "rowspan": {"type": "#integer", "optional": true}, "thead": {"type": "#DoxBool"}, "valign": {"type": "DoxVerticalAlign", "optional": true}, "width": {"type": "#string", "optional": true} }, "children": { "para": {"type": "docParaType", "is_list": true, "min_items": 0} } }, "docTocItemType": { "kind": "union_list_element", "allow_text": true, "attributes": { "id": {"type": "#string"} } }, "docParamListItem": { "kind": "tag_only_element", "children": { "parameterdescription": {"type": "descriptionType"}, "parameternamelist": {"type": "docParamNameList", "is_list": true, "min_items": 0} } }, "docParamNameList": { "kind": "tag_only_element", "children": { "parametername": {"type": "docParamName", "is_list": true, "min_items": 0}, "parametertype": {"type": "docParamType", "is_list": true, "min_items": 0} } }, "docParamType": { "kind": "union_list_element", "allow_text": true, "content": { "ref": "refTextType" } }, "docParamName": { "kind": "union_list_element", "allow_text": true, "attributes": { "direction": {"type": "DoxParamDir", "optional": true} }, "content": { "ref": "refTextType" } }, "tableofcontentsKindType": { "kind": "tag_only_element", "children": { "name": {"type": "#string"}, "reference": {"type": "#string"}, "tableofcontents": {"type": "tableofcontentsType", "is_list": true, "min_items": 0} } }, "incType": { "kind": "union_list_element", "allow_text": true, "attributes": { "refid": {"type": "#string", "optional": true}, "local": {"type": "#DoxBool"} } }, "compoundRefType": { "kind": "union_list_element", "allow_text": true, "attributes": { "refid": {"type": "#string", "optional": true}, "prot": {"type": "DoxProtectionKind"}, "virt": {"type": "DoxVirtualKind"} } }, "refType": { "kind": "union_list_element", "allow_text": true, "attributes": { "refid": {"type": "#string"}, "prot": {"type": "DoxProtectionKind", "optional": true}, "inline": {"type": "#DoxBool", "optional": true} } }, "exportType": { "kind": "union_list_element", "allow_text": true, "attributes": { "refid": {"type": "#string", "optional": true} } }, "refTextType": { "kind": "union_list_element", "allow_text": true, "attributes": { "refid": {"type": "#string"}, "kindref": {"type": "DoxRefKind"}, "external": {"type": "#string", "optional": true}, "tooltip": {"type": "#string", "optional": true} } }, "reimplementType": { "kind": "union_list_element", "allow_text": true, "attributes": { "refid": {"type": "#string"} } }, "DoxRefKind": { "kind": "enumeration", "values": ["compound", "member"] }, "MemberKind": { "kind": "enumeration", "values": [ "define", "property", "event", "variable", "typedef", "enum", "enumvalue", "function", "signal", "prototype", "friend", "dcop", "slot" ] }, "DoxMemberKind": { "kind": "enumeration", "values": [ "define", "property", "event", "variable", "typedef", "enum", "function", "signal", "prototype", "friend", "dcop", "slot", "interface", "service" ] }, "docTitleCmdGroup": { "kind": "union_list_element", "allow_text": true, "content": { "ulink": "docURLLink", "bold": "docMarkupType", "s": "docMarkupType", "strike": "docMarkupType", "underline": "docMarkupType", "emphasis": "docMarkupType", "computeroutput": "docMarkupType", "subscript": "docMarkupType", "superscript": "docMarkupType", "center": "docMarkupType", "small": "docMarkupType", "cite": "docMarkupType", "del": "docMarkupType", "ins": "docMarkupType", "htmlonly": "docHtmlOnlyType", "manonly": "#string", "xmlonly": "#string", "rtfonly": "#string", "latexonly": "#string", "docbookonly": "#string", "image": "docImageType", "dot": "docDotMscType", "msc": "docDotMscType", "plantuml": "docPlantumlType", "anchor": "docAnchorType", "formula": "docFormulaType", "ref": "docRefTextType", "emoji": "docEmojiType", "linebreak": "#char(A)", "nonbreakablespace": "#char(A0)", "iexcl": "#char(A1)", "cent": "#char(A2)", "pound": "#char(A3)", "curren": "#char(A4)", "yen": "#char(A5)", "brvbar": "#char(A6)", "sect": "#char(A7)", "umlaut": "#char(A8)", "copy": "#char(A9)", "ordf": "#char(AA)", "laquo": "#char(AB)", "not": "#char(AC)", "shy": "#char(AD)", "registered": "#char(AE)", "macr": "#char(AF)", "deg": "#char(B0)", "plusmn": "#char(B1)", "sup2": "#char(B2)", "sup3": "#char(B3)", "acute": "#char(B4)", "micro": "#char(B5)", "para": "#char(B6)", "middot": "#char(B7)", "cedil": "#char(B8)", "sup1": "#char(B9)", "ordm": "#char(BA)", "raquo": "#char(BB)", "frac14": "#char(BC)", "frac12": "#char(BD)", "frac34": "#char(BE)", "iquest": "#char(BF)", "Agrave": "#char(C0)", "Aacute": "#char(C1)", "Acirc": "#char(C2)", "Atilde": "#char(C3)", "Aumlaut": "#char(C4)", "Aring": "#char(C5)", "AElig": "#char(C6)", "Ccedil": "#char(C7)", "Egrave": "#char(C8)", "Eacute": "#char(C9)", "Ecirc": "#char(CA)", "Eumlaut": "#char(CB)", "Igrave": "#char(CC)", "Iacute": "#char(CD)", "Icirc": "#char(CE)", "Iumlaut": "#char(CF)", "ETH": "#char(D0)", "Ntilde": "#char(D1)", "Ograve": "#char(D2)", "Oacute": "#char(D3)", "Ocirc": "#char(D4)", "Otilde": "#char(D5)", "Oumlaut": "#char(D6)", "times": "#char(D7)", "Oslash": "#char(D8)", "Ugrave": "#char(D9)", "Uacute": "#char(DA)", "Ucirc": "#char(DB)", "Uumlaut": "#char(DC)", "Yacute": "#char(DD)", "THORN": "#char(DE)", "szlig": "#char(DF)", "agrave": "#char(E0)", "aacute": "#char(E1)", "acirc": "#char(E2)", "atilde": "#char(E3)", "aumlaut": "#char(E4)", "aring": "#char(E5)", "aelig": "#char(E6)", "ccedil": "#char(E7)", "egrave": "#char(E8)", "eacute": "#char(E9)", "ecirc": "#char(EA)", "eumlaut": "#char(EB)", "igrave": "#char(EC)", "iacute": "#char(ED)", "icirc": "#char(EE)", "iumlaut": "#char(EF)", "eth": "#char(F0)", "ntilde": "#char(F1)", "ograve": "#char(F2)", "oacute": "#char(F3)", "ocirc": "#char(F4)", "otilde": "#char(F5)", "oumlaut": "#char(F6)", "divide": "#char(F7)", "oslash": "#char(F8)", "ugrave": "#char(F9)", "uacute": "#char(FA)", "ucirc": "#char(FB)", "uumlaut": "#char(FC)", "yacute": "#char(FD)", "thorn": "#char(FE)", "yumlaut": "#char(FF)", "fnof": "#char(192)", "Alpha": "#char(391)", "Beta": "#char(392)", "Gamma": "#char(393)", "Delta": "#char(394)", "Epsilon": "#char(395)", "Zeta": "#char(396)", "Eta": "#char(397)", "Theta": "#char(398)", "Iota": "#char(399)", "Kappa": "#char(39A)", "Lambda": "#char(39B)", "Mu": "#char(39C)", "Nu": "#char(39D)", "Xi": "#char(39E)", "Omicron": "#char(39F)", "Pi": "#char(3A0)", "Rho": "#char(3A1)", "Sigma": "#char(3A3)", "Tau": "#char(3A4)", "Upsilon": "#char(3A5)", "Phi": "#char(3A6)", "Chi": "#char(3A7)", "Psi": "#char(3A8)", "Omega": "#char(3A9)", "alpha": "#char(3B1)", "beta": "#char(3B2)", "gamma": "#char(3B3)", "delta": "#char(3B4)", "epsilon": "#char(3B5)", "zeta": "#char(3B6)", "eta": "#char(3B7)", "theta": "#char(3B8)", "iota": "#char(3B9)", "kappa": "#char(3BA)", "lambda": "#char(3BB)", "mu": "#char(3BC)", "nu": "#char(3BD)", "xi": "#char(3BE)", "omicron": "#char(3BF)", "pi": "#char(3C0)", "rho": "#char(3C1)", "sigmaf": "#char(3C2)", "sigma": "#char(3C3)", "tau": "#char(3C4)", "upsilon": "#char(3C5)", "phi": "#char(3C6)", "chi": "#char(3C7)", "psi": "#char(3C8)", "omega": "#char(3C9)", "thetasym": "#char(3D1)", "upsih": "#char(3D2)", "piv": "#char(3D6)", "bull": "#char(2022)", "hellip": "#char(2026)", "prime": "#char(2032)", "Prime": "#char(2033)", "oline": "#char(203E)", "frasl": "#char(2044)", "weierp": "#char(2118)", "imaginary": "#char(2111)", "real": "#char(211C)", "trademark": "#char(2122)", "alefsym": "#char(2135)", "larr": "#char(2190)", "uarr": "#char(2191)", "rarr": "#char(2192)", "darr": "#char(2193)", "harr": "#char(2194)", "crarr": "#char(21B5)", "lArr": "#char(21D0)", "uArr": "#char(21D1)", "rArr": "#char(21D2)", "dArr": "#char(21D3)", "hArr": "#char(21D4)", "forall": "#char(2200)", "part": "#char(2202)", "exist": "#char(2203)", "empty": "#char(2205)", "nabla": "#char(2207)", "isin": "#char(2208)", "notin": "#char(2209)", "ni": "#char(220B)", "prod": "#char(220F)", "sum": "#char(2211)", "minus": "#char(2212)", "lowast": "#char(2217)", "radic": "#char(221A)", "prop": "#char(221D)", "infin": "#char(221E)", "ang": "#char(2220)", "and": "#char(2227)", "or": "#char(2228)", "cap": "#char(2229)", "cup": "#char(222A)", "int": "#char(222B)", "there4": "#char(2234)", "sim": "#char(223C)", "cong": "#char(2245)", "asymp": "#char(2248)", "ne": "#char(2260)", "equiv": "#char(2261)", "le": "#char(2264)", "ge": "#char(2265)", "sub": "#char(2282)", "sup": "#char(2283)", "nsub": "#char(2284)", "sube": "#char(2286)", "supe": "#char(2287)", "oplus": "#char(2295)", "otimes": "#char(2297)", "perp": "#char(22A5)", "sdot": "#char(22C5)", "lceil": "#char(2308)", "rceil": "#char(2309)", "lfloor": "#char(230A)", "rfloor": "#char(230B)", "lang": "#char(27E8)", "rang": "#char(27E9)", "loz": "#char(25CA)", "spades": "#char(2660)", "clubs": "#char(2663)", "hearts": "#char(2665)", "diams": "#char(2666)", "OElig": "#char(152)", "oelig": "#char(153)", "Scaron": "#char(160)", "scaron": "#char(161)", "Yumlaut": "#char(178)", "circ": "#char(2C6)", "tilde": "#char(2DC)", "ensp": "#char(2002)", "emsp": "#char(2003)", "thinsp": "#char(2009)", "zwnj": "#char(200C)", "zwj": "#char(200D)", "lrm": "#char(200E)", "rlm": "#char(200F)", "ndash": "#char(2013)", "mdash": "#char(2014)", "lsquo": "#char(2018)", "rsquo": "#char(2019)", "sbquo": "#char(201A)", "ldquo": "#char(201C)", "rdquo": "#char(201D)", "bdquo": "#char(201E)", "dagger": "#char(2020)", "Dagger": "#char(2021)", "permil": "#char(2030)", "lsaquo": "#char(2039)", "rsaquo": "#char(203A)", "euro": "#char(20AC)", "tm": "#char(2122)" } }, "docCmdGroup": { "kind": "union_list_element", "allow_text": true, "bases": ["docTitleCmdGroup"], "content": { "hruler": "#empty", "preformatted": "docMarkupType", "programlisting": "listingType", "verbatim": "#string", "javadocliteral": "#string", "javadoccode": "#string", "indexentry": "docIndexEntryType", "orderedlist": "docListType", "itemizedlist": "docListType", "simplesect": "docSimpleSectType", "title": "docTitleType", "variablelist": "docVariableListType", "table": "docTableType", "heading": "docHeadingType", "dotfile": "docImageFileType", "mscfile": "docImageFileType", "diafile": "docImageFileType", "plantumlfile": "docImageFileType", "toclist": "docTocListType", "language": "docLanguageType", "parameterlist": "docParamListType", "xrefsect": "docXRefSectType", "copydoc": "docCopyType", "details": "docDetailsType", "blockquote": "docBlockQuoteType", "parblock": "docParBlockType" } }, "docParaType": { "kind": "union_list_element", "bases": ["docCmdGroup"] }, "docMarkupType": { "kind": "union_list_element", "bases": ["docCmdGroup"] }, "docTitleType": { "kind": "union_list_element", "bases": ["docTitleCmdGroup"] }, "docSummaryType": { "kind": "union_list_element", "bases": ["docTitleCmdGroup"] }, "docURLLink": { "kind": "union_list_element", "bases": ["docTitleCmdGroup"], "attributes": { "url": {"type": "#string"} } }, "docHtmlOnlyType": { "kind": "union_list_element", "allow_text": true, "attributes": { "block": {"type": "#string", "optional": true} } }, "docImageType": { "kind": "union_list_element", "bases": ["docTitleCmdGroup"], "attributes": { "type": {"type": "DoxImageKind", "optional": true}, "name": {"type": "#string", "optional": true}, "width": {"type": "#string", "optional": true}, "height": {"type": "#string", "optional": true}, "alt": {"type": "#string", "optional": true}, "inline": {"type": "#DoxBool", "optional": true}, "caption": {"type": "#string", "optional": true} } }, "docDotMscType": { "kind": "union_list_element", "bases": ["docTitleCmdGroup"], "attributes": { "name": {"type": "#string", "optional": true}, "width": {"type": "#string", "optional": true}, "height": {"type": "#string", "optional": true}, "caption": {"type": "#string", "optional": true} } }, "docPlantumlType": { "kind": "union_list_element", "bases": ["docTitleCmdGroup"], "attributes": { "name": {"type": "#string", "optional": true}, "width": {"type": "#string", "optional": true}, "height": {"type": "#string", "optional": true}, "caption": {"type": "#string", "optional": true}, "engine": {"type": "DoxPlantumlEngine", "optional": true} } }, "docRefTextType": { "kind": "union_list_element", "bases": ["docTitleCmdGroup"], "attributes": { "refid": {"type": "#string"}, "kindref": {"type": "#string"}, "external": {"type": "#string", "optional": true} } }, "docHeadingType": { "kind": "union_list_element", "bases": ["docTitleCmdGroup"], "attributes": { "level": {"type": "#integer"} } }, "docImageFileType": { "kind": "union_list_element", "bases": ["docTitleCmdGroup"], "attributes": { "name": {"type": "#string", "optional": true}, "width": {"type": "#string", "optional": true}, "height": {"type": "#string", "optional": true} } }, "docAnchorType": { "kind": "union_list_element", "allow_text": true, "attributes": { "id": {"type": "#string"} } }, "docFormulaType": { "kind": "union_list_element", "allow_text": true, "attributes": { "id": {"type": "#string"} } }, "docEmojiType": { "kind": "tag_only_element", "attributes": { "name": {"type": "#string"}, "unicode": {"type": "#string"} } }, "docIndexEntryType": { "kind": "tag_only_element", "children": { "primaryie": {"type": "#string"}, "secondaryie": {"type": "#string"} } }, "docListType": { "kind": "list_element", "min_items": 1, "attributes": { "type": {"type": "DoxOlType", "optional": true}, "start": {"type": "#integer", "optional": true} }, "content": { "listitem": "docListItemType" } }, "docSimpleSectType": { "kind": "tag_only_element", "attributes": { "kind": {"type": "DoxSimpleSectKind"} }, "children": { "title": {"type": "docTitleType", "min_items": 0}, "para": {"type": "docParaType", "is_list": true} } }, "docVariableListType": { "kind": "tuple_list_element", "min_items": 1, "content": { "varlistentry": "docVarListEntryType", "listitem": "docListItemType" } }, "docTableType" : { "kind": "tag_only_element", "attributes": { "rows": {"type": "#integer"}, "cols": {"type": "#integer"}, "width": {"type": "#string", "optional": true} }, "children": { "caption": {"type": "docCaptionType", "min_items": 0}, "row": {"type": "docRowType", "is_list": true, "min_items": 0} } }, "docTocListType": { "kind": "list_element", "content": { "tocitem": "docTocItemType" } }, "docLanguageType": { "kind": "list_element", "attributes": { "langid": {"type": "#string"} }, "content": { "para": "docParaType" } }, "docParamListType": { "kind": "list_element", "attributes": { "kind": {"type": "DoxParamListKind"} }, "content": { "parameteritem": "docParamListItem" } }, "docXRefSectType": { "kind": "tag_only_element", "attributes": { "id": {"type": "#string"} }, "children": { "xreftitle": {"type": "#string", "is_list": true, "min_items": 0}, "xrefdescription": {"type": "descriptionType"} } }, "docCopyType": { "kind": "tag_only_element", "attributes": { "link": {"type": "#string"} }, "children": { "para": {"type": "docParaType", "is_list": true, "min_items": 0}, "sec1": {"type": "docSect1Type", "is_list": true, "min_items": 0}, "internal": {"type": "docInternalType", "min_items": 0} } }, "docDetailsType": { "kind": "tag_only_element", "children": { "summary": {"type": "docSummaryType", "min_items": 0}, "para": {"type": "docParaType", "is_list": true, "min_items": 0} } }, "docBlockQuoteType": { "kind": "list_element", "content": { "para": "docParaType" } }, "docParBlockType": { "kind": "list_element", "content": { "para": "docParaType" } }, "docVarListEntryType": { "kind": "tag_only_element", "children": { "term": {"type": "docTitleType"} } }, "DoxCompoundKind": { "kind": "enumeration", "values": [ {"xml": "class", "id": "class_"}, "struct", "union", "interface", "protocol", "category", "exception", "service", "singleton", "module", "type", "file", "namespace", "group", "page", "example", "dir", "concept" ] }, "CompoundKind": { "kind": "enumeration", "values": [ {"xml": "class", "id": "class_"}, "struct", "union", "interface", "protocol", "category", "exception", "module", "type", "file", "namespace", "group", "page", "example", "dir", "concept" ] }, "DoxLanguage": { "kind": "enumeration", "values": [ "Unknown", "IDL", "Java", {"xml": "C#", "id": "CSharp"}, "D", "PHP", {"xml": "Objective-C", "id": "Objective_C"}, {"xml": "C++", "id": "CPlusPlus"}, "JavaScript", "Python", "Fortran", "VHDL", "XML", "SQL", "Markdown", "Slice", "Lex" ] }, "DoxProtectionKind": { "kind": "enumeration", "values": [ "public", "protected", "private", "package" ] }, "DoxRefQualifierKind": { "kind": "enumeration", "values": [ "lvalue", "rvalue" ] }, "DoxVirtualKind": { "kind": "enumeration", "values": [ {"xml": "non-virtual", "id": "non_virtual"}, "virtual", {"xml": "pure-virtual", "id": "pure_virtual"} ] }, "DoxSectionKind": { "kind": "enumeration", "values": [ {"xml": "user-defined", "id": "user_defined"}, {"xml": "public-type", "id": "public_type"}, {"xml": "public-func", "id": "public_func"}, {"xml": "public-attrib", "id": "public_attrib"}, {"xml": "public-slot", "id": "public_slot"}, "signal", {"xml": "dcop-func", "id": "dcop_func"}, "property", "event", {"xml": "public-static-func", "id": "public_static_func"}, {"xml": "public-static-attrib", "id": "public_static_attrib"}, {"xml": "protected-type", "id": "protected_type"}, {"xml": "protected-func", "id": "protected_func"}, {"xml": "protected-attrib", "id": "protected_attrib"}, {"xml": "protected-slot", "id": "protected_slot"}, {"xml": "protected-static-func", "id": "protected_static_func"}, {"xml": "protected-static-attrib", "id": "protected_static_attrib"}, {"xml": "package-type", "id": "package_type"}, {"xml": "package-func", "id": "package_func"}, {"xml": "package-attrib", "id": "package_attrib"}, {"xml": "package-static-func", "id": "package_static_func"}, {"xml": "package-static-attrib", "id": "package_static_attrib"}, {"xml": "private-type", "id": "private_type"}, {"xml": "private-func", "id": "private_func"}, {"xml": "private-attrib", "id": "private_attrib"}, {"xml": "private-slot", "id": "private_slot"}, {"xml": "private-static-func", "id": "private_static_func"}, {"xml": "private-static-attrib", "id": "private_static_attrib"}, "friend", "related", "define", "prototype", "typedef", "enum", "func", "var" ] }, "DoxHighlightClass": { "kind": "enumeration", "values": [ "comment", "normal", "preprocessor", "keyword", "keywordtype", "keywordflow", "stringliteral", "xmlcdata", "charliteral", "vhdlkeyword", "vhdllogic", "vhdlchar", "vhdldigit" ] }, "DoxSimpleSectKind": { "kind": "enumeration", "values": [ "see", {"xml": "return", "id": "return_"}, "author", "authors", "version", "since", "date", "note", "warning", "pre", "post", "copyright", "invariant", "remark", "attention", "important", "par", "rcs" ] }, "DoxImageKind": { "kind": "enumeration", "values": [ "html", "latex", "docbook", "rtf", "xml" ] }, "DoxPlantumlEngine": { "kind": "enumeration", "values": [ "uml", "bpm", "wire", "dot", "ditaa", "salt", "math", "latex", "gantt", "mindmap", "wbs", "yaml", "creole", "json", "flow", "board", "git", "hcl", "regex", "ebnf", "files" ] }, "DoxParamListKind": { "kind": "enumeration", "values": [ "param", "retval", "exception", "templateparam" ] }, "DoxParamDir": { "kind": "enumeration", "values": [ {"xml": "in", "id": "in_"}, "out", "inout" ] }, "DoxAccessor": { "kind": "enumeration", "values": [ "retain", "copy", "assign", "weak", "strong", "unretained" ] }, "DoxAlign": { "kind": "enumeration", "values": [ "left", "right", "center" ] }, "DoxVerticalAlign": { "kind": "enumeration", "values": [ "bottom", "top", "middle" ] }, "DoxGraphRelation": { "kind": "enumeration", "values": [ "include", "usage", {"xml": "template-instance", "id": "template_instance"}, {"xml": "public-inheritance", "id": "public_inheritance"}, {"xml": "protected-inheritance", "id": "protected_inheritance"}, {"xml": "private-inheritance", "id": "private_inheritance"}, {"xml": "type-constraint", "id": "type_constraint"} ] }, "DoxCheck": { "kind": "enumeration", "values": [ "checked", "unchecked" ] }, "DoxOlType": { "kind": "char_enumeration", "values": "1aAiI" } } } ================================================ FILE: xml_parser_generator/setuptools_builder.py ================================================ from __future__ import annotations import os.path from setuptools.command.build_py import build_py try: from setuptools.modified import newer_group except ImportError: from distutils.dep_util import newer_group from distutils import log from distutils.dir_util import mkpath import make_parser class CustomBuildPy(build_py): SCHEMA_FILE = os.path.join("xml_parser_generator", "schema.json") PY_MODULE_TEMPLATE = os.path.join("xml_parser_generator", "module_template.py.in") MAKER_SOURCE = os.path.join("xml_parser_generator", "make_parser.py") PARSER_DEST = os.path.join("breathe", "_parser.py") PY_M_DEPENDENCIES = [SCHEMA_FILE, PY_MODULE_TEMPLATE, MAKER_SOURCE] def make_parser(self): dest = self.PARSER_DEST if not self.editable_mode: dest = os.path.join(self.build_lib, dest) mkpath(os.path.dirname(dest), dry_run=self.dry_run) if self.force or newer_group(self.PY_M_DEPENDENCIES, dest): log.info(f'generating "{dest}" source from template') if not self.dry_run: make_parser.generate_from_json(self.SCHEMA_FILE, [(self.PY_MODULE_TEMPLATE, dest)]) else: log.debug(f'"{dest}" is up-to-date') def run(self): super().run() self.make_parser() if __name__ == "__main__": make_parser.generate_from_json( CustomBuildPy.SCHEMA_FILE, [(CustomBuildPy.PY_MODULE_TEMPLATE, CustomBuildPy.PARSER_DEST)] ) ================================================ FILE: xml_parser_generator/stubs_template.pyi.in ================================================ import enum from typing import ClassVar, Generic, Literal, overload, Protocol, Self, SupportsIndex, TypeVar from collections.abc import Iterable T = TypeVar('T',covariant=True) U = TypeVar('U',covariant=True) class SupportsRead(Protocol): def read(self, length: int, /) -> bytes | bytearray: ... class FrozenListItr(Generic[T]): def __iter__(self) -> Self: ... def __next__(self) -> T: ... class FrozenList(Generic[T]): def __init__(self, items: Iterable[T]): ... def __len__(self) -> int: ... def __getitem__(self, i: SupportsIndex) -> T: ... def __iter__(self) -> FrozenListItr[T]: ... class TaggedValue(Generic[T, U]): @property def name(self) -> T: ... @property def value(self) -> U: ... def __init__(self, name: T, value: U): ... def __len__(self) -> Literal[2]: ... @overload def __getitem__(self, i: Literal[0]) -> T: ... @overload def __getitem__(self, i: Literal[1]) -> U: ... @overload def __getitem__(self, i: SupportsIndex) -> T | U: ... class Node: _fields: ClassVar[tuple[str, ...]] class ParseError(RuntimeError): @property def message(self) -> str: ... @property def lineno(self) -> int: ... class ParseWarning(UserWarning): pass TopLevel = ( //% for name,type in root_elements {$ '| ' if not loop.first $}TaggedValue[Literal['{$ name $}'],{$ type.py_name $}] //% endfor ) def parse_str(data: str, /) -> TopLevel: ... def parse_file(file: SupportsRead, /) -> TopLevel: ... //% macro emit_fields(type) {%- for b in type.bases %}{$ emit_fields(b) $}{% endfor -%} //% for ref in type|attributes {$ ref.py_name $}: {$ ref.py_type() $} //% endfor //% for ref in type|children {$ ref.py_name $}: {$ ref.py_type() $} //% endfor //% endmacro //% macro emit_content_fields(type) {%- for b in type.bases %}{$ emit_content_fields(b) $}{% endfor -%} //% for cname,ctype in type|content {$ cname $}: {$ ctype.py_name $} //% endfor //% endmacro //% for type in types //% if type is content_tuple //% set list_item_type = 'ListItem_'~type class ListItem_{$ type $}: {$ emit_content_fields(type) $} def __init__(self{% for cname,ctype in type|content %}, {$ cname $}: {$ ctype.py_name $}{% endfor %}): ... def __len__(self) -> Literal[{$ type|content|length $}]: ... //% for cname,ctype in type|content @overload def __getitem__(self,i: Literal[{$ loop.index0 $}]) -> {$ ctype.py_name $}: ... //% endfor @overload def __getitem__(self,i: SupportsIndex) -> {$ type|content|map('last')|map(attribute='py_name')|join(' | ') $}: ... //% elif type is content_union //% set members = type.py_union_list()|sort //% if members|length > 1 //% set list_item_type = 'ListItem_'~type ListItem_{$ type $} = ( //% for m in members {$ '| ' if not loop.first $}{$ m $} //% endfor ) //% else //% set list_item_type = members|first //% endif //% elif type is content_bare //% set list_item_type = (type|content|first)[1].py_name //% elif type is list_e {$ "invalid content type"|error $} //% endif //% if type is used_directly class Node_{$ type $}(Node{$ ', FrozenList['~list_item_type~']' if type is list_e $}): {$ emit_fields(type) $} def __init__(self{$ ', __items: Iterable['~list_item_type~'], /' if type is list_e $} {%- if type|field_count -%}, * {%- for f in type.all_fields() if f is not optional %}, {$ f.py_name $}: {$ f.py_type(true) $}{% endfor -%} {%- for f in type.all_fields() if f is optional %}, {$ f.py_name $}: {$ f.py_type(true) $} = ...{% endfor -%} {%- endif %}): ... //% elif type is enumeration_t class {$ type $}(enum.Enum): //% for entry in type.children {$ entry.id $} = '{$ entry.xml $}' //% endfor //% elif type is char_enum_t {$ type $} = Literal[{% for c in type.values %}{$ "'"~c~"'" $}{$ ',' if not loop.last $}{% endfor %}] //% endif //% endfor