From 4f208ac991700d10cc96a0d7a581e843347f63e3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Tue, 1 Aug 2023 00:16:29 +0200 Subject: [PATCH 01/23] Fix resolve for absolute reference names --- .github/workflows/ci.yml | 3 +- sphinxcontrib/phpdomain.py | 81 ++++++++++++++++++------- test/unit/ns.html | 120 +++++++++++++++++++++++++++++++++++++ test/unit/ns.md | 39 ++++++++++++ 4 files changed, 222 insertions(+), 21 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7595a41a..f53e7fa2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -57,8 +57,8 @@ jobs: cd test/unit find . -name '*.html' -exec rm {} \; make html SPHINXOPTS='-W' + (cd _build/html && rm genindex.html index.html search.html php-modindex.html) (cd _build/html && find . -name '*.html' -exec sh -c 'xmllint {} --xpath '"'"'//div[@role="main"]'"'"' | xmllint --format - > ../../{}' \;) - rm genindex.html index.html search.html php-modindex.html - name: Diff Unit Outputs run: | @@ -71,6 +71,7 @@ jobs: uses: stefanzweifel/git-auto-commit-action@v4 with: branch: ${{ github.head_ref || github.ref_name }}.changes + # create_branch: true push_options: '--force' commit_message: Unit Changes commit_user_name: Bot diff --git a/sphinxcontrib/phpdomain.py b/sphinxcontrib/phpdomain.py index af78ba9f..068f98b9 100644 --- a/sphinxcontrib/phpdomain.py +++ b/sphinxcontrib/phpdomain.py @@ -9,6 +9,7 @@ :license: BSD, see LICENSE for details. """ import re +import inspect from docutils import nodes from docutils.parsers.rst import directives, Directive @@ -18,17 +19,46 @@ from sphinx.locale import _ from sphinx.domains import Domain, ObjType, Index from sphinx.directives import ObjectDescription +from sphinx.util import logging from sphinx.util.nodes import make_refnode from sphinx.util.docfields import Field, GroupedField, TypedField from sphinx import __version__ as sphinx_version +# log informative messages +def log_info( + fromdocnode, + message: str +): + logger = logging.getLogger(__name__) + logger.info(f"[phpdomain] {message}", location=fromdocnode) + +# log messages that should fail CI +def log_warning( + fromdocnode, + message: str +): + logger = logging.getLogger(__name__) + logger.warning(f"[phpdomain] {message}", location=fromdocnode) + +# log assertions that should fail CI +def log_assert( + fromdocnode, + value: bool +): + if not value: + caller = inspect.getframeinfo(inspect.stack()[1][0]) + logger = logging.getLogger(__name__) + logger.warning(f"[phpdomain-assert] line {caller.lineno}", location=fromdocnode) + php_sig_re = re.compile( r'''^ (public\ |protected\ |private\ )? # visibility (final\ |abstract\ |static\ )? # modifiers - ([\w.]*\:\:)? # class name(s) - (\$?\w+) \s* # thing name - (?: \((.*)\) # optional: arguments - (?:\s* -> \s* (.*))?)? # return annotation + ([\w\\]+\:\:)? # class name(s) + (\$?[\w\\]+) \s* # thing name + (?: + \((.*)\) # optional: arguments + (?:\s* -> \s* (.*))? # return annotation + )? (?:\s* : \s* (.*))? # backed enum type / case value $ # and nothing more ''', re.VERBOSE) @@ -170,13 +200,13 @@ def handle_signature(self, sig, signode): # determine module and class name (if applicable), as well as full name modname = self.options.get( 'namespace', self.env.temp_data.get('php:namespace')) - - classname = self.env.temp_data.get('php:class') separator = separators[self.objtype] - # Method declared as Class::methodName - if not classname and '::' in name_prefix: + if '::' in name_prefix: classname = name_prefix.rstrip('::') + else: + classname = self.env.temp_data.get('php:class') + if self.objtype == 'global' or self.objtype == 'function': add_module = False modname = None @@ -189,10 +219,10 @@ def handle_signature(self, sig, signode): if name_prefix and self.objtype != 'staticmethod': if name_prefix.startswith(classname): name_prefix = name_prefix[len(classname):].rstrip('::') - classname = classname.rstrip('::') + classname = classname.rstrip('::') # TODO seems like wrongly coded, there should be no '::' in the classname fullname = name_prefix + classname + separator + name elif name_prefix: - classname = classname.rstrip('::') + classname = classname.rstrip('::') # TODO seems like wrongly coded, there should be no '::' in the classname fullname = name_prefix + name # Currently in a class, but not creating another class, @@ -544,12 +574,15 @@ def process_link(self, env, refnode, has_explicit_title, title, target): title = title[2:] target = target.lstrip('~') # only has a meaning for the title - # If the first char is ~ don't display the leading namespace & class. + # If the first char is '~' don't display the leading namespace & class. if title.startswith('~'): m = re.search(r"(?:.+[:]{2}|(?:.*?\\{1,2})+)?(.*)\Z", title) if m: title = m.group(1) + if title.startswith(NS): + title = title[1:] + refnode['php:namespace'] = env.temp_data.get('php:namespace') refnode['php:class'] = env.temp_data.get('php:class') @@ -735,7 +768,7 @@ def resolve_xref(self, env, fromdocname, builder, modname = node.get('php:namespace') clsname = node.get('php:class') searchorder = node.hasattr('refspecific') and 1 or 0 - name, obj = self.find_obj(env, modname, clsname, + name, obj = self.find_obj(env, node, modname, clsname, target, typ, searchorder) if not obj: return None @@ -743,23 +776,28 @@ def resolve_xref(self, env, fromdocname, builder, return make_refnode(builder, fromdocname, obj[0], name, contnode, name) - def find_obj(self, env, modname, classname, name, type, searchorder=0): + def find_obj(self, env, fromdocnode, modname, classname, name, type, searchorder=0): """ - Find a PHP object for "name", perhaps using the given namespace and/or - classname. + Find a PHP object for "name", using the given namespace and classname. """ # skip parens if name[-2:] == '()': name = name[:-2] - if not name: - return None, None - objects = self.data['objects'] + if name.startswith(NS): + absname = name[1:] + else: + absname = (modname + NS if modname else "") \ + + (classname + NS if classname and '::' not in name else "") \ + + name + newname = None if searchorder == 1: - if modname and classname and \ + if absname in objects: + newname = absname + elif modname and classname and \ modname + NS + classname + '::' + name in objects: newname = modname + NS + classname + '::' + name elif modname and modname + NS + name in objects: @@ -773,7 +811,9 @@ def find_obj(self, env, modname, classname, name, type, searchorder=0): elif name in objects: newname = name else: - if name in objects: + if absname in objects: + newname = absname + elif name in objects: newname = name elif classname and classname + '::' + name in objects: newname = classname + '::' + name @@ -792,6 +832,7 @@ def find_obj(self, env, modname, classname, name, type, searchorder=0): 'object::' + name in objects: newname = 'object::' + name if newname is None: + log_info(fromdocnode, f"Target not found '{absname}'") return None, None return newname, objects[newname] diff --git a/test/unit/ns.html b/test/unit/ns.html index bf0c670a..cbfc2d8f 100644 --- a/test/unit/ns.html +++ b/test/unit/ns.html @@ -47,5 +47,125 @@

Cross linking + +

NS can be changed

+
+
+ + class + + + Foo\Bar\ + + + A + + +
+
+
+
+
+ + Foo\Bar\A:: + + + simplify + + ( + ) + +
+
+
+ +
+
+ + class + + + Bar\ + + + A + + +
+
+
+
+
+ + Bar\A:: + + + simplify + + ( + ) + +
+
+
+
+

Cross linking

+ +
+ +
+

NS must not be guessed

+
+

These cross references must not have a link as the target methods are not defined.

+
+
    +
  • +

    + + A2::simplify + +

    +
  • +
+ +
    +
  • +

    + + A::simplify + +

    +
  • +
+
diff --git a/test/unit/ns.md b/test/unit/ns.md index 1ebfe4dd..fc594651 100644 --- a/test/unit/ns.md +++ b/test/unit/ns.md @@ -12,3 +12,42 @@ ## Cross linking - {php:meth}`A::simplify` + +# NS can be changed + +:::{php:namespace} Foo\Bar +::: + +:::{php:class} A +::: + +:::{php:method} simplify() +::: + +:::{php:namespace} Bar +::: + +:::{php:class} A +::: + +:::{php:method} simplify() +::: + +## Cross linking + +- {php:meth}`A::simplify` +- {php:meth}`\Foo\Bar\A::simplify` +- {php:meth}`\Bar\A::simplify` + +# NS must not be guessed + +:::note +These cross references must not have a link as the target methods are not defined. +::: + +- {php:meth}`\A2::simplify` + +:::{php:namespace} Bar2 +::: + +- {php:meth}`A::simplify` From 2dbd2f09436e1401c13c472fa98d867e8e554029 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Tue, 1 Aug 2023 11:04:38 +0200 Subject: [PATCH 02/23] move test/unit to test --- .github/workflows/ci.yml | 14 +++++++------- .gitignore | 2 +- test/{unit => }/Makefile | 0 test/{unit => }/conf.py | 0 test/{unit => }/index.md | 0 test/{unit => }/make.bat | 0 test/{unit => }/method.html | 0 test/{unit => }/method.md | 0 test/{unit => }/ns.html | 0 test/{unit => }/ns.md | 0 test/{unit => }/requirements.txt | 0 11 files changed, 8 insertions(+), 8 deletions(-) rename test/{unit => }/Makefile (100%) rename test/{unit => }/conf.py (100%) rename test/{unit => }/index.md (100%) rename test/{unit => }/make.bat (100%) rename test/{unit => }/method.html (100%) rename test/{unit => }/method.md (100%) rename test/{unit => }/ns.html (100%) rename test/{unit => }/ns.md (100%) rename test/{unit => }/requirements.txt (100%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f53e7fa2..fe5a4013 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -33,7 +33,7 @@ jobs: run: | python -m pip install --upgrade pip pip install -r requirements.txt - pip install -r test/unit/requirements.txt + pip install -r test/requirements.txt pip install . - name: Build Test @@ -52,28 +52,28 @@ jobs: run: | cd test/must-build && make html SPHINXOPTS='-W -D toc_object_entries_show_parents=all' - - name: Run Unit Testing + - name: Build Unit Tests run: | - cd test/unit + cd test find . -name '*.html' -exec rm {} \; make html SPHINXOPTS='-W' (cd _build/html && rm genindex.html index.html search.html php-modindex.html) (cd _build/html && find . -name '*.html' -exec sh -c 'xmllint {} --xpath '"'"'//div[@role="main"]'"'"' | xmllint --format - > ../../{}' \;) - - name: Diff Unit Outputs + - name: Diff Unit Tests Output run: | - cd test/unit + cd test rm -r _build git add . -N && git diff --exit-code - - name: Push Unit Changes + - name: Push Unit Tests Output if: failure() uses: stefanzweifel/git-auto-commit-action@v4 with: branch: ${{ github.head_ref || github.ref_name }}.changes # create_branch: true push_options: '--force' - commit_message: Unit Changes + commit_message: Unit Tests Changes commit_user_name: Bot commit_user_email: bot@example.com commit_author: Bot diff --git a/.gitignore b/.gitignore index 508e0cc1..f9909d52 100644 --- a/.gitignore +++ b/.gitignore @@ -2,7 +2,7 @@ dist/ build/ doc/_build test/must-build/_build -test/unit/_build +test/_build *.pyc *.egg-info .DS_Store diff --git a/test/unit/Makefile b/test/Makefile similarity index 100% rename from test/unit/Makefile rename to test/Makefile diff --git a/test/unit/conf.py b/test/conf.py similarity index 100% rename from test/unit/conf.py rename to test/conf.py diff --git a/test/unit/index.md b/test/index.md similarity index 100% rename from test/unit/index.md rename to test/index.md diff --git a/test/unit/make.bat b/test/make.bat similarity index 100% rename from test/unit/make.bat rename to test/make.bat diff --git a/test/unit/method.html b/test/method.html similarity index 100% rename from test/unit/method.html rename to test/method.html diff --git a/test/unit/method.md b/test/method.md similarity index 100% rename from test/unit/method.md rename to test/method.md diff --git a/test/unit/ns.html b/test/ns.html similarity index 100% rename from test/unit/ns.html rename to test/ns.html diff --git a/test/unit/ns.md b/test/ns.md similarity index 100% rename from test/unit/ns.md rename to test/ns.md diff --git a/test/unit/requirements.txt b/test/requirements.txt similarity index 100% rename from test/unit/requirements.txt rename to test/requirements.txt From b654b720f48441aeaefd27ab540f1f18334e0071 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Tue, 1 Aug 2023 11:30:50 +0200 Subject: [PATCH 03/23] move test/must-build to test --- .github/workflows/ci.yml | 31 +- test/must-build/Makefile | 130 - test/must-build/conf.py | 210 -- test/must-build/make.bat | 155 -- test/rst_doc.html | 2186 +++++++++++++++++ test/{must-build/test_doc.rst => rst_doc.md} | 2 + test/rst_doc2.html | 100 + .../{must-build/test_doc2.rst => rst_doc2.md} | 3 +- test/rst_index.html | 110 + test/{must-build/index.rst => rst_index.md} | 9 +- test/rst_nesting_regression.html | 62 + ...gression.rst => rst_nesting_regression.md} | 2 + 12 files changed, 2484 insertions(+), 516 deletions(-) delete mode 100644 test/must-build/Makefile delete mode 100644 test/must-build/conf.py delete mode 100644 test/must-build/make.bat create mode 100644 test/rst_doc.html rename test/{must-build/test_doc.rst => rst_doc.md} (99%) create mode 100644 test/rst_doc2.html rename test/{must-build/test_doc2.rst => rst_doc2.md} (98%) create mode 100644 test/rst_index.html rename test/{must-build/index.rst => rst_index.md} (80%) create mode 100644 test/rst_nesting_regression.html rename test/{must-build/test_nesting_regression.rst => rst_nesting_regression.md} (96%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index fe5a4013..476091b0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -36,22 +36,6 @@ jobs: pip install -r test/requirements.txt pip install . - - name: Build Test - run: | - cd test/must-build && make html SPHINXOPTS='-W' - - - name: Build Test with toc show_parents=hide - run: | - cd test/must-build && make html SPHINXOPTS='-W -D toc_object_entries_show_parents=hide' - - - name: Build Test with toc show_parents=domain - run: | - cd test/must-build && make html SPHINXOPTS='-W -D toc_object_entries_show_parents=domain' - - - name: Build Test with toc show_parents=all - run: | - cd test/must-build && make html SPHINXOPTS='-W -D toc_object_entries_show_parents=all' - - name: Build Unit Tests run: | cd test @@ -77,3 +61,18 @@ jobs: commit_user_name: Bot commit_user_email: bot@example.com commit_author: Bot + + - name: Build Unit Tests with toc show_parents=hide + run: | + cd test + make html SPHINXOPTS='-W -D toc_object_entries_show_parents=hide' + + - name: Build Unit Tests with toc show_parents=domain + run: | + cd test + make html SPHINXOPTS='-W -D toc_object_entries_show_parents=domain' + + - name: Build Unit Tests with toc show_parents=all + run: | + cd test + make html SPHINXOPTS='-W -D toc_object_entries_show_parents=all' diff --git a/test/must-build/Makefile b/test/must-build/Makefile deleted file mode 100644 index f61d9887..00000000 --- a/test/must-build/Makefile +++ /dev/null @@ -1,130 +0,0 @@ -# Makefile for Sphinx documentation -# - -# You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -PAPER = -BUILDDIR = _build - -# Internal variables. -PAPEROPT_a4 = -D latex_paper_size=a4 -PAPEROPT_letter = -D latex_paper_size=letter -ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . - -.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest - -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 " text to make text files" - @echo " man to make manual pages" - @echo " changes to make an overview of all changed/added/deprecated items" - @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/sphinxcontrib-rubydomain-acceptancetest.qhcp" - @echo "To view the help file:" - @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/sphinxcontrib-rubydomain-acceptancetest.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/sphinxcontrib-rubydomain-acceptancetest" - @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/sphinxcontrib-rubydomain-acceptancetest" - @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 all-pdf' or \`make all-ps' in that directory to" \ - "run these through (pdf)latex." - -latexpdf: latex - $(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." - -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." - -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." diff --git a/test/must-build/conf.py b/test/must-build/conf.py deleted file mode 100644 index e3bde517..00000000 --- a/test/must-build/conf.py +++ /dev/null @@ -1,210 +0,0 @@ -# -*- coding: utf-8 -*- -# -# sphinxcontrib-rubydomain-acceptancetest documentation build configuration file, created by -# sphinx-quickstart on Sun Apr 25 13:27:18 2010. -# -# This file is execfile()d with the current directory set to its containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - -import sys, os - -# If extensions (or modules to document with autodoc) are in another directory, -# add these directories to sys.path here. If the directory is relative to the -# documentation root, use os.path.abspath to make it absolute, like shown here. -sys.path.append(os.path.abspath('..')) - -# -- General configuration ----------------------------------------------------- - -# If your documentation needs a minimal Sphinx version, state it here. -needs_sphinx = '1.0' - -# Add any Sphinx extension module names here, as strings. They can be extensions -# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinxcontrib.phpdomain'] - -# Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] - -# The suffix of source filenames. -source_suffix = '.rst' - -# The encoding of source files. -#source_encoding = 'utf-8-sig' - -# The master toctree document. -master_doc = 'index' - -# General information about the project. -project = u'sphinxcontrib-phpdomain-acceptancetest' -copyright = u'2011, Mark Story' - -# The version info for the project you're documenting, acts as replacement for -# |version| and |release|, also used in various other places throughout the -# built documents. -# -# The short X.Y version. -version = '0.1' -# The full version, including alpha/beta/rc tags. -release = '0.1' - -# The language for content autogenerated by Sphinx. Refer to documentation -# for a list of supported languages. -#language = None - -# There are two options for replacing |today|: either, you set today to some -# non-false value, then it is used: -#today = '' -# Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' - -# List of patterns, relative to source directory, that match files and -# directories to ignore when looking for source files. -exclude_patterns = ['_build'] - -# The reST default role (used for this markup: `text`) to use for all documents. -#default_role = None - -# If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True - -# If true, the current module name will be prepended to all description -# unit titles (such as .. function::). -#add_module_names = True - -# If true, sectionauthor and moduleauthor directives will be shown in the -# output. They are ignored by default. -#show_authors = False - -# The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' - -# A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] - - -# -- Options for HTML output --------------------------------------------------- - -# The theme to use for HTML and HTML Help pages. Major themes that come with -# Sphinx are currently 'default' and 'sphinxdoc'. -html_theme = 'default' - -# Theme options are theme-specific and customize the look and feel of a theme -# further. For a list of options available for each theme, see the -# documentation. -#html_theme_options = {} - -# Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] - -# The name for this set of Sphinx documents. If None, it defaults to -# " v documentation". -#html_title = None - -# A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None - -# The name of an image file (relative to this directory) to place at the top -# of the sidebar. -#html_logo = None - -# The name of an image file (within the static path) to use as favicon of the -# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 -# pixels large. -#html_favicon = None - -# Add any paths that contain custom static files (such as style sheets) here, -# relative to this directory. They are copied after the builtin static files, -# so a file named "default.css" will overwrite the builtin "default.css". -#html_static_path = ['_static'] - -# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, -# using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' - -# If true, SmartyPants will be used to convert quotes and dashes to -# typographically correct entities. -#html_use_smartypants = True - -# Custom sidebar templates, maps document names to template names. -#html_sidebars = {} - -# Additional templates that should be rendered to pages, maps page names to -# template names. -#html_additional_pages = {} - -# If false, no module index is generated. -#html_domain_indices = True - -# If false, no index is generated. -#html_use_index = True - -# If true, the index is split into individual pages for each letter. -#html_split_index = False - -# If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True - -# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True - -# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True - -# If true, an OpenSearch description file will be output, and all pages will -# contain a tag referring to it. The value of this option must be the -# base URL from which the finished HTML is served. -#html_use_opensearch = '' - -# If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = '' - -# Output file base name for HTML help builder. -htmlhelp_basename = 'sphinxcontrib-phpdomain-acceptancetestdoc' - - -# -- Options for LaTeX output -------------------------------------------------- - -# The paper size ('letter' or 'a4'). -#latex_paper_size = 'letter' - -# The font size ('10pt', '11pt' or '12pt'). -#latex_font_size = '10pt' - -# Grouping the document tree into LaTeX files. List of tuples -# (source start file, target name, title, author, documentclass [howto/manual]). -latex_documents = [ - ('index', 'sphinxcontrib-phpdomain-acceptancetest.tex', u'sphinxcontrib-phpdomain-acceptancetest Documentation', - u'Mark Story', 'manual'), -] - -# The name of an image file (relative to this directory) to place at the top of -# the title page. -#latex_logo = None - -# For "manual" documents, if this is true, then toplevel headings are parts, -# not chapters. -#latex_use_parts = False - -# Additional stuff for the LaTeX preamble. -#latex_preamble = '' - -# Documents to append as an appendix to all manuals. -#latex_appendices = [] - -# If false, no module index is generated. -#latex_domain_indices = True - - -# -- Options for manual page output -------------------------------------------- - -# One entry per manual page. List of tuples -# (source start file, name, description, authors, manual section). -man_pages = [ - ('index', 'sphinxcontrib-phpdomain-acceptancetest', u'sphinxcontrib-phpdomain-acceptancetest Documentation', - [u'Mark Story'], 1) -] diff --git a/test/must-build/make.bat b/test/must-build/make.bat deleted file mode 100644 index 4310ae15..00000000 --- a/test/must-build/make.bat +++ /dev/null @@ -1,155 +0,0 @@ -@ECHO OFF - -REM Command file for Sphinx documentation - -if "%SPHINXBUILD%" == "" ( - set SPHINXBUILD=sphinx-build -) -set BUILDDIR=_build -set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . -if NOT "%PAPER%" == "" ( - set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% -) - -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. changes to make an overview over all changed/added/deprecated items - 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 -) - -if "%1" == "html" ( - %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/html. - goto end -) - -if "%1" == "dirhtml" ( - %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. - goto end -) - -if "%1" == "singlehtml" ( - %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml - echo. - echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. - goto end -) - -if "%1" == "pickle" ( - %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle - echo. - echo.Build finished; now you can process the pickle files. - goto end -) - -if "%1" == "json" ( - %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json - echo. - echo.Build finished; now you can process the JSON files. - goto end -) - -if "%1" == "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. - goto end -) - -if "%1" == "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\sphinxcontrib-rubydomain-acceptancetest.qhcp - echo.To view the help file: - echo.^> assistant -collectionFile %BUILDDIR%\qthelp\sphinxcontrib-rubydomain-acceptancetest.ghc - goto end -) - -if "%1" == "devhelp" ( - %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp - echo. - echo.Build finished. - goto end -) - -if "%1" == "epub" ( - %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub - echo. - echo.Build finished. The epub file is in %BUILDDIR%/epub. - goto end -) - -if "%1" == "latex" ( - %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex - echo. - echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. - goto end -) - -if "%1" == "text" ( - %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text - echo. - echo.Build finished. The text files are in %BUILDDIR%/text. - goto end -) - -if "%1" == "man" ( - %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man - echo. - echo.Build finished. The manual pages are in %BUILDDIR%/man. - goto end -) - -if "%1" == "changes" ( - %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes - echo. - echo.The overview file is in %BUILDDIR%/changes. - goto end -) - -if "%1" == "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. - goto end -) - -if "%1" == "doctest" ( - %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest - echo. - echo.Testing of doctests in the sources finished, look at the ^ -results in %BUILDDIR%/doctest/output.txt. - goto end -) - -:end diff --git a/test/rst_doc.html b/test/rst_doc.html new file mode 100644 index 00000000..38078b93 --- /dev/null +++ b/test/rst_doc.html @@ -0,0 +1,2186 @@ + +
+
+

Acceptance tests for PHPdomain

+
+

Globals

+
+
+ + $global_var + + +
+
+

A global variable

+
+
+
+
+ + constant + + + SOME_CONSTANT + + +
+
+

A global constant

+
+
+
+
+ + constant + + + VALUE + + +
+
+

A global constant

+
+
+
+
in_array(needle, haystack)
+
+

Checks for needle in haystack.

+
+
Parameters:
+
+
    +
  • +

    needle – The element to search for.

    +
  • +
  • +

    haystack (array) – The array to search.

    +
  • +
+
+
Returns:
+
+

Element exists in array.

+
+
Return type:
+
+

+ + + boolean + + +

+
+
+
+
+
+
+

Classes

+
+
+ + class + + + DateTime + + +
+
+

Datetime class

+
+
setDate($year, $month, $day)
+
+

Set the date in the datetime object

+
+
Parameters:
+
+
    +
  • +

    $year (int) – The year.

    +
  • +
  • +

    $month (int) – The month.

    +
  • +
  • +

    $day (int) – The day.

    +
  • +
+
+
+
+
+
+
setTime($hour, $minute[, $second])
+
+

Set the time

+
+
Parameters:
+
+
    +
  • +

    $hour (int) – The hour

    +
  • +
  • +

    $minute (int) – The minute

    +
  • +
  • +

    $second (int) – The second

    +
  • +
+
+
+
+
+
+
+ + public + + + static + + + getLastErrors + + ( + ) + +
+
+

Returns the warnings and errors

+
+
Returns:
+
+

array Returns array containing info about warnings and errors.

+
+
+
+
+
+
+ + constant + + + ATOM + + +
+
+

Y-m-dTH:i:sP

+
+
+
+
+ + property + + + testattr + + +
+
+

Value of some attribute

+
+
+
+
+
+
+ + class + + + OtherClass + + +
+
+

Another class

+
+
+
+
OtherClass::update($arg='', $arg2=[], $arg3=[])
+
+

Update something.

+
+
+
+
+ + property + + + OtherClass::$ + + + nonIndentedAttribute + + +
+
+

This attribute wasn’t indented

+
+
+
+
+ + constant + + + OtherClass:: + + + NO_INDENT + + +
+
+

This class constant wasn’t indented

+
+
+
+
+ + static + + + OtherClass:: + + + staticMethod + + +
+
+

A static method.

+
+
+
+
+

Exceptions

+
+
+ + exception + + + InvalidArgumentException + + +
+
+

Throw when you get an argument that is bad.

+
+
+
+
+

Interfaces

+
+
+ + interface + + + DateTimeInterface + + +
+
+

Datetime interface

+
+
setDate($year, $month, $day)
+
+

Set the date in the datetime object

+
+
Parameters:
+
+
    +
  • +

    $year (int) – The year.

    +
  • +
  • +

    $month (int) – The month.

    +
  • +
  • +

    $day (int) – The day.

    +
  • +
+
+
+
+
+
+
setTime($hour, $minute[, $second])
+
+

Set the time

+
+
Parameters:
+
+
    +
  • +

    $hour (int) – The hour

    +
  • +
  • +

    $minute (int) – The minute

    +
  • +
  • +

    $second (int) – The second

    +
  • +
+
+
+
+
+
+
+ + constant + + + ATOM + + +
+
+

Y-m-dTH:i:sP

+
+
+
+
+ + property + + + testattr + + +
+
+

Value of some attribute

+
+
+
+
+
+
+ + interface + + + OtherInterface + + +
+
+

Another interface

+
+
+
+
+

Traits

+
+
+ + trait + + + LogTrait + + +
+
+

A logging trait

+
+
log($level, $string)
+
+

A method description.

+
+
+
+
+
+
+

More globals after classes

+
+
+ + $other_global_var + + +
+
+

A global variable

+
+
+
+
strpos($needle, $haystack)
+
+

Position of needle in haystack

+
+
+
+

Test Case - Global symbols with no namespaces

+

+ + + $global_var + + +

+

+ + + $other_global_var + + +

+

+ + + SOME_CONSTANT + + +

+

+ + + in_array + + +

+

+ + + strpos + + +

+

+ + + DateTime + + +

+

+ + + DateTime::setTime() + + +

+

+ + + DateTime::getLastErrors() + + +

+

+ + + DateTime::setDate() + + +

+

+ + + DateTime::ATOM + + +

+

+ + + DateTime::$testattr + + +

+

+ + + OtherClass::update + + +

+

+ + + OtherClass::$nonIndentedAttribute + + +

+

+ + + OtherClass::NO_INDENT + + +

+

+ + + OtherClass::staticMethod + + +

+

+ + + InvalidArgumentException + + +

+

+ + + DateTimeInterface + + +

+

+ + + DateTimeInterface::setTime() + + +

+

+ + + setDate() + + +

+

+ + + DateTimeInterface::ATOM + + +

+

+ + + DateTimeInterface::$testattr + + +

+

+ + + OtherInterface + + +

+

+ + + LogTrait + + +

+

+ + + LogTrait::log() + + +

+
+ +
+
+ +

Namespaced elements

+
+
namespaced_function($one[, $two])
+
+

A function in a namespace

+
+
Parameters:
+
+
    +
  • +

    $one (string) – First parameter.

    +
  • +
  • +

    $two (string) – Second parameter.

    +
  • +
+
+
+
+
+
+
+ + constant + + + LibraryName\ + + + NS_CONST + + +
+
+

A constant in a namespace

+
+
+
+
+ + exception + + + LibraryName\ + + + NamespaceException + + +
+
+

This exception is in a namespace.

+
+
+
+
+ + class + + + LibraryName\ + + + LibraryClass + + +
+
+

A class in a namespace

+
+
+ + instanceMethod + + ( + + $foo + + ) + +
+
+

An instance method

+
+
+
+
+ + constant + + + TEST_CONST + + +
+
+

Test constant

+
+
+
+
+ + property + + + property + + +
+
+

A property!

+
+
+
+
+
+
+ + static + + + LibraryName\LibraryClass:: + + + staticMethod + + +
+
+

A static method in a namespace

+
+
+
+
+ + class + + + LibraryName\ + + + NamespaceClass + + +
+
+

A class in the namespace, no indenting on children

+
+
+
+
LibraryName\NamespaceClass::firstMethod($one, $two)
+
+

A normal instance method.

+
+
+
+
+ + property + + + LibraryName\NamespaceClass::$ + + + property + + +
+
+

A property

+
+
+
+
+ + constant + + + LibraryName\NamespaceClass:: + + + NAMESPACE_CONST + + +
+
+

Const on class in namespace

+
+
+
+
+ + static + + + LibraryName\NamespaceClass:: + + + namespaceStatic + + ( + + $foo + + ) + +
+
+

A static method here.

+
+
+
+
+ + final + + + class + + + LibraryName\ + + + LibraryClassFinal + + +
+
+

A final class

+
+
+
+
publicLibraryName\LibraryClassFinal::firstMethod($one, $two)
+
+

A public instance method.

+
+
+
+
protectedLibraryName\LibraryClassFinal::secondMethod($one, $two)
+
+

A protected instance method.

+
+
+
+
privateLibraryName\LibraryClassFinal::thirdMethod($one, $two)
+
+

A private instance method.

+
+
+
+
staticLibraryName\LibraryClassFinal::fourthMethod($one, $two)
+
+

A static method.

+
+
+
+
protectedfinalLibraryName\LibraryClassFinal::fifthMethod($one, $two)
+
+

A protected final method.

+
+
+
+
+ + abstract + + + class + + + LibraryName\ + + + LibraryClassAbstract + + +
+
+

An abstract class

+
+
+
+
+ + interface + + + LibraryName\ + + + LibraryInterface + + +
+
+

A interface in a namespace

+
+
+ + instanceMethod + + ( + + $foo + + ) + +
+
+
+

An instance method

+
+
+
+
+ + trait + + + LibraryName\ + + + TemplateTrait + + +
+
+

A trait in a namespace

+
+
+ + render + + ( + + $template + + ) + +
+
+
+

Render a template.

+
+
+
+

Test Case - not including namespace

+

Within a namespace context you don’t need to include the namespace in links.

+

+ + + LibraryName + + +

+

+ + + namespaced_function() + + +

+

+ + + NS_CONST + + +

+

+ + + LibraryClass + + +

+

+ + + LibraryClass::instanceMethod + + +

+

+ + + LibraryClass::staticMethod() + + +

+

+ + + LibraryClass::$property + + +

+

+ + + LibraryClass::TEST_CONST + + +

+

+ + + LibraryName\OtherClass + + +

+

+ + + LibraryName\ThirdClass + + +

+

+ + + NamespaceClass + + +

+

+ + + NamespaceClass::firstMethod + + +

+

+ + + NamespaceClass::$property + + +

+

+ + + NamespaceClass::NAMESPACE_CONST + + +

+

+ + + LibraryClassFinal + + +

+

+ + + LibraryClassFinal::firstMethod + + +

+

+ + + LibraryClassFinal::secondMethod + + +

+

+ + + LibraryClassFinal::thirdMethod + + +

+

+ + + LibraryClassFinal::fourthMethod + + +

+

+ + + LibraryClassFinal::fifthMethod + + +

+

+ + + LibraryInterface + + +

+

+ + + LibraryInterface::instanceMethod + + +

+

+ + + NamespaceException + + +

+

+ + + TemplateTrait + + +

+

+ + + LibraryName\TemplateTrait + + +

+

+ + + LibraryName\TemplateTrait::render() + + +

+
+ +
+

Test Case - global access

+

+ + + DateTime + + +

+

+ + + DateTime::setTime() + + +

+

+ + + $global_var + + +

+

+ + + SOME_CONSTANT + + +

+

+ + + LibraryName\LibraryClass::$property + + +

+

$property Should not be prefixed with classname.

+

+ + + LibraryName\LibraryClass::TEST_CONST + + +

+

+ + + LibraryName\NS_CONST + + +

+

+ + + DateTimeInterface + + +

+

+ + + DateTimeInterface::setTime() + + +

+
+
+
+

Any Cross Ref

+

+ + + LibraryName\NS_CONST + + +

+

+ + + DateTimeInterface::setTime() + + +

+
+
+ +

Nested namespaces

+
+
+ + exception + + + LibraryName\SubPackage\ + + + NestedNamespaceException + + +
+
+

In a package

+
+
+
+
+ + class + + + LibraryName\SubPackage\ + + + SubpackageClass + + +
+
+

A class in a subpackage

+
+
+
+
+ + interface + + + LibraryName\SubPackage\ + + + SubpackageInterface + + +
+
+

A class in a subpackage

+
+
+ +
+
+ +

Return Types

+
+
+ + class + + + OtherLibrary\ + + + ReturningClass + + +
+
+

A class to do some returning.

+
+
+ + returnClassFromSameNamespace + + ( + ) + +
+
+
+
Returns:
+
+

An object instance of a class from the same namespace.

+
+
Return type:
+
+

+ + + + OtherLibrary\ReturnedClass + + + +

+
+
+
+
+
+
+ + returnClassFromOtherNamespace + + ( + ) + +
+
+
+
Returns:
+
+

An object instance of a class from another namespace.

+
+
Return type:
+
+

+ + + + LibraryName\SubPackage\SubpackageInterface + + + +

+
+
+
+
+
+
+ + returnClassConstant + + ( + ) + +
+
+
+
Returns:
+
+

The value of a specific class constant.

+
+
Return type:
+
+

+ + + + LibraryName\NamespaceClass::NAMESPACE_CONST + + + +

+
+
+
+
+
+
+ + returnGlobalConstant + + ( + ) + +
+
+
+
Returns:
+
+

The value of a specific global constant.

+
+
Return type:
+
+

+ + + + SOME_CONSTANT + + + +

+
+
+
+
+
+
+ + returnExceptionInstance + + ( + ) + +
+
+
+
Returns:
+
+

An instance of an exception.

+
+
Return type:
+
+

+ + + + InvalidArgumentException + + + +

+
+
+
+
+
+
+ + returnScalarType + + ( + ) + +
+
+
+
Returns:
+
+

A scalar string type.

+
+
Return type:
+
+

+ + + string + + +

+
+
+
+
+
+
+ + returnUnionType + + ( + ) + +
+
+
+
Returns:
+
+

Any of a whole bunch of things specified with a PHP 8 union type.

+
+
Return type:
+
+

+ + + int|string|OtherLibrary\ReturnedClass|LibraryName\SubPackage\SubpackageInterface|null + + +

+
+
+
+
+
+
+
+
+ + class + + + OtherLibrary\ + + + ReturnedClass + + +
+
+

A class to return.

+
+
+
+
+

Enums

+
+ +

Basic Enumerations

+
+
+ + enum + + + Example\Basic\ + + + Suit + + +
+
+

In playing cards, a suit is one of the categories into which the cards of a +deck are divided.

+
+
+ + case + + + Hearts + + +
+
+
+
+
+ + case + + + Diamonds + + +
+
+
+
+
+ + case + + + Clubs + + +
+
+
+
+
+ + case + + + Spades + + +
+
+
+
+
+
+
+ +

Backed Enumerations

+
+
+ + enum + + + Example\Backed\ + + + Suit + + + + + string + + + +
+
+

In playing cards, a suit is one of the categories into which the cards of a +deck are divided.

+
+
+ + case + + + Hearts + + + + + 'H' + + + +
+
+
+
+
+ + case + + + Diamonds + + + + + 'D' + + + +
+
+
+
+
+ + case + + + Clubs + + + + + 'C' + + + +
+
+
+
+
+ + case + + + Spades + + + + + 'S' + + + +
+
+
+
+
+
+
+ +

Advanced Enumerations

+
+
+ + enum + + + Example\Advanced\ + + + Suit + + + + + string + + + +
+
+

In playing cards, a suit is one of the categories into which the cards of a +deck are divided.

+
+
+ + case + + + Hearts + + + + + 'H' + + + +
+
+
+
+
+ + case + + + Diamonds + + + + + 'D' + + + +
+
+
+
+
+ + case + + + Clubs + + + + + 'C' + + + +
+
+
+
+
+ + case + + + Spades + + + + + 'S' + + + +
+
+
+
+
+ + color + + ( + ) + + + + string + + + +
+
+

Returns “red” for hearts and diamonds, “black” for clubs and spades.

+
+
+
+
+ + static + + + values + + + + + string[] + + + +
+
+

Returns an array of the values of all the cases on this enum.

+
+
+
+
+ + constant + + + Roses + + + + + Hearts + + + +
+
+

An alias for Example\Advanced\Suit::Hearts.

+
+
+
+
+ + constant + + + Bells + + + + + Diamonds + + + +
+
+

An alias for Example\Advanced\Suit::Diamonds.

+
+
+
+
+ + constant + + + Acorns + + + + + Clubs + + + +
+
+

An alias for Example\Advanced\Suit::Clubs.

+
+
+
+
+ + constant + + + Shields + + + + + Spades + + + +
+
+

An alias for Example\Advanced\Suit::Spades.

+
+
+
+
+
+ +
+
+
+
diff --git a/test/must-build/test_doc.rst b/test/rst_doc.md similarity index 99% rename from test/must-build/test_doc.rst rename to test/rst_doc.md index 862936a5..a5fd2c79 100644 --- a/test/must-build/test_doc.rst +++ b/test/rst_doc.md @@ -1,3 +1,4 @@ +```{eval-rst} Acceptance tests for PHPdomain ############################## @@ -629,3 +630,4 @@ Links to Advanced Enumeration Example :php:const:`Example\\Advanced\\Suit::Acorns` :php:const:`Example\\Advanced\\Suit::Shields` +``` diff --git a/test/rst_doc2.html b/test/rst_doc2.html new file mode 100644 index 00000000..81294b4d --- /dev/null +++ b/test/rst_doc2.html @@ -0,0 +1,100 @@ + +
+
+

Top Level Namespace

+

namespace Imagine\Draw

+ +
+
+ + class + + + Imagine\Draw\ + + + DrawerInterface + + +
+
+
+

Instance of this interface is returned by ImagineImageImageInterface::draw.

+
+
Imagine\Draw\DrawerInterface::arc(PointInterface$center, BoxInterface$size, $start, $end, Color$color)
+
+

Draws an arc on a starting at a given x, y coordinates under a given start and end angles

+
+
Parameters:
+
+
    +
  • +

    $center (ImagineImagePointInterface) – Center of the arc.

    +
  • +
  • +

    $size (ImagineImageBoxInterface) – Size of the bounding box.

    +
  • +
  • +

    $start (integer) – Start angle.

    +
  • +
  • +

    $end (integer) – End angle.

    +
  • +
  • +

    $color (ImagineImageColor) – Line color.

    +
  • +
+
+
Throws:
+
+

ImagineExceptionRuntimeException

+
+
Returns:
+
+

ImagineDrawDrawerInterface

+
+
+
+
+
+

Re-used namespace

+

No indexing errors or links should point to this namespace.

+
+
+ + class + + + LibraryName\ + + + ThirdClass + + +
+
+

Another class in a currentmodule block

+
+
+

No indexing errors or links should point to this namespace.

+
+
+ + class + + + LibraryName\ + + + OtherClass + + +
+
+

Another class in a reused namespace

+
+
+
+
+
+
diff --git a/test/must-build/test_doc2.rst b/test/rst_doc2.md similarity index 98% rename from test/must-build/test_doc2.rst rename to test/rst_doc2.md index 2da3f00c..a7774bd8 100644 --- a/test/must-build/test_doc2.rst +++ b/test/rst_doc2.md @@ -1,3 +1,4 @@ +```{eval-rst} Top Level Namespace ################### @@ -41,4 +42,4 @@ No indexing errors or links should point to this namespace. .. php:class:: OtherClass Another class in a reused namespace - +``` diff --git a/test/rst_index.html b/test/rst_index.html new file mode 100644 index 00000000..fc236e7e --- /dev/null +++ b/test/rst_index.html @@ -0,0 +1,110 @@ + +
+
+

Welcome to sphinxcontrib-phpdomain-acceptancetest’s documentation!

+

Contents:

+ +
+
+

Indices and tables

+ +
+
+
diff --git a/test/must-build/index.rst b/test/rst_index.md similarity index 80% rename from test/must-build/index.rst rename to test/rst_index.md index cc97f676..798ea246 100644 --- a/test/must-build/index.rst +++ b/test/rst_index.md @@ -1,3 +1,4 @@ +```{eval-rst} Welcome to sphinxcontrib-phpdomain-acceptancetest's documentation! =================================================================== @@ -6,9 +7,9 @@ Contents: .. toctree:: :maxdepth: 2 - test_doc - test_doc2 - test_nesting_regression + rst_doc + rst_doc2 + rst_nesting_regression Indices and tables ================== @@ -16,4 +17,4 @@ Indices and tables * :ref:`genindex` * :ref:`modindex` * :ref:`search` - +``` diff --git a/test/rst_nesting_regression.html b/test/rst_nesting_regression.html new file mode 100644 index 00000000..55cc150d --- /dev/null +++ b/test/rst_nesting_regression.html @@ -0,0 +1,62 @@ + +
+
+

Nested method Regression

+

Test nested methods to ensure page generation doesn’t hard fail.

+
+
+ + populate_variables + + ( + ) + +
+
+
+
+

Set us up the vars

+
+
+
    +
  • +

    ‘post_id’: an integer post ID

    +
  • +
  • +

    ‘exclude_date’: boolean whether or not to include the date in the byline

    +
  • +
+
+
+
+
param array $args:
+
+

Associative array containing following keys:

+
+
+
+
+
+
+ + generate_byline + + ( + ) + +
+
+

this creates the byline text and adds it to $this->output

+
+
See:
+
+

$output $reates this

+
+
+
+
+
+
+
+
+
diff --git a/test/must-build/test_nesting_regression.rst b/test/rst_nesting_regression.md similarity index 96% rename from test/must-build/test_nesting_regression.rst rename to test/rst_nesting_regression.md index e4eb4d63..54d9cabe 100644 --- a/test/must-build/test_nesting_regression.rst +++ b/test/rst_nesting_regression.md @@ -1,3 +1,4 @@ +```{eval-rst} Nested method Regression ======================== @@ -17,3 +18,4 @@ Test nested methods to ensure page generation doesn't hard fail. this creates the byline text and adds it to $this->output :see: $output $reates this +``` From 177580194598cc0dc44e1e9f47ca603e3ad02dc1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Tue, 1 Aug 2023 12:48:15 +0200 Subject: [PATCH 04/23] push changes to forks and only once per workflow --- .github/workflows/ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 476091b0..f4a5eab6 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,11 +51,11 @@ jobs: git add . -N && git diff --exit-code - name: Push Unit Tests Output - if: failure() + if: failure() && github.repository_owner != 'markstory' && matrix.python == '3.11' uses: stefanzweifel/git-auto-commit-action@v4 with: branch: ${{ github.head_ref || github.ref_name }}.changes - # create_branch: true + create_branch: true push_options: '--force' commit_message: Unit Tests Changes commit_user_name: Bot From 4225cd45e0bae2b43d3848ddbab59d9cbea49772 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Tue, 1 Aug 2023 13:01:29 +0200 Subject: [PATCH 05/23] assert log using CI --- .github/workflows/ci.yml | 5 +++-- sphinxcontrib/phpdomain.py | 6 +++--- test/log.txt | 27 +++++++++++++++++++++++++++ 3 files changed, 33 insertions(+), 5 deletions(-) create mode 100644 test/log.txt diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index f4a5eab6..9bc1e437 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -12,7 +12,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python: ['3.7', '3.8', '3.9', '3.10', '3.11'] + python: ['3.8', '3.9', '3.10', '3.11'] fail-fast: false permissions: @@ -40,9 +40,10 @@ jobs: run: | cd test find . -name '*.html' -exec rm {} \; - make html SPHINXOPTS='-W' + make html SPHINXOPTS='-W' | tee log.txt (cd _build/html && rm genindex.html index.html search.html php-modindex.html) (cd _build/html && find . -name '*.html' -exec sh -c 'xmllint {} --xpath '"'"'//div[@role="main"]'"'"' | xmllint --format - > ../../{}' \;) + sed -i -r 's~.*/(test/.* \[phpdomain)~\1~;t;d' log.txt - name: Diff Unit Tests Output run: | diff --git a/sphinxcontrib/phpdomain.py b/sphinxcontrib/phpdomain.py index 068f98b9..d49c204a 100644 --- a/sphinxcontrib/phpdomain.py +++ b/sphinxcontrib/phpdomain.py @@ -30,7 +30,7 @@ def log_info( message: str ): logger = logging.getLogger(__name__) - logger.info(f"[phpdomain] {message}", location=fromdocnode) + logger.info(f"[phpdomain.info] {message}", location=fromdocnode) # log messages that should fail CI def log_warning( @@ -38,7 +38,7 @@ def log_warning( message: str ): logger = logging.getLogger(__name__) - logger.warning(f"[phpdomain] {message}", location=fromdocnode) + logger.warning(f"[phpdomain.warning] {message}", location=fromdocnode) # log assertions that should fail CI def log_assert( @@ -48,7 +48,7 @@ def log_assert( if not value: caller = inspect.getframeinfo(inspect.stack()[1][0]) logger = logging.getLogger(__name__) - logger.warning(f"[phpdomain-assert] line {caller.lineno}", location=fromdocnode) + logger.warning(f"[phpdomain.assert] line {caller.lineno}", location=fromdocnode) php_sig_re = re.compile( r'''^ (public\ |protected\ |private\ )? # visibility diff --git a/test/log.txt b/test/log.txt new file mode 100644 index 00000000..30f2478d --- /dev/null +++ b/test/log.txt @@ -0,0 +1,27 @@ +test/method.md:11: [phpdomain.info] Target not found 'Foo\array' +test/ns.md:48: [phpdomain.info] Target not found 'A2::simplify' +test/ns.md:53: [phpdomain.info] Target not found 'Bar2\A::simplify' +test/rst_doc.md:24: [phpdomain.info] Target not found 'array' +test/rst_doc.md:27: [phpdomain.info] Target not found 'boolean' +test/rst_doc.md:40: [phpdomain.info] Target not found 'DateTime\int' +test/rst_doc.md:40: [phpdomain.info] Target not found 'DateTime\int' +test/rst_doc.md:40: [phpdomain.info] Target not found 'DateTime\int' +test/rst_doc.md:48: [phpdomain.info] Target not found 'DateTime\int' +test/rst_doc.md:48: [phpdomain.info] Target not found 'DateTime\int' +test/rst_doc.md:48: [phpdomain.info] Target not found 'DateTime\int' +test/rst_doc.md:104: [phpdomain.info] Target not found 'DateTimeInterface\int' +test/rst_doc.md:104: [phpdomain.info] Target not found 'DateTimeInterface\int' +test/rst_doc.md:104: [phpdomain.info] Target not found 'DateTimeInterface\int' +test/rst_doc.md:112: [phpdomain.info] Target not found 'DateTimeInterface\int' +test/rst_doc.md:112: [phpdomain.info] Target not found 'DateTimeInterface\int' +test/rst_doc.md:112: [phpdomain.info] Target not found 'DateTimeInterface\int' +test/rst_doc.md:221: [phpdomain.info] Target not found 'LibraryName\string' +test/rst_doc.md:221: [phpdomain.info] Target not found 'LibraryName\string' +test/rst_doc.md:497: [phpdomain.info] Target not found 'OtherLibrary\ReturningClass\string' +test/rst_doc.md:502: [phpdomain.info] Target not found 'OtherLibrary\ReturningClass\int|string|OtherLibrary\ReturnedClass|LibraryName\SubPackage\SubpackageInterface|null' +test/rst_doc2.md:11: [phpdomain.info] Target not found 'Imagine\Draw\ImagineImageImageInterface::draw' +test/rst_doc2.md:17: [phpdomain.info] Target not found 'Imagine\Draw\DrawerInterface\ImagineImagePointInterface' +test/rst_doc2.md:17: [phpdomain.info] Target not found 'Imagine\Draw\DrawerInterface\ImagineImageBoxInterface' +test/rst_doc2.md:17: [phpdomain.info] Target not found 'Imagine\Draw\DrawerInterface\integer' +test/rst_doc2.md:17: [phpdomain.info] Target not found 'Imagine\Draw\DrawerInterface\integer' +test/rst_doc2.md:17: [phpdomain.info] Target not found 'Imagine\Draw\DrawerInterface\ImagineImageColor' From 52d2a708ff4ab0f4940c55f26a02c2fe24343fd7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Tue, 1 Aug 2023 13:42:43 +0200 Subject: [PATCH 06/23] fix method/property render /w explicit class name --- sphinxcontrib/phpdomain.py | 10 +--------- test/rst_doc.html | 3 +++ test/rst_nesting_regression.html | 6 ++++++ 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/sphinxcontrib/phpdomain.py b/sphinxcontrib/phpdomain.py index d49c204a..2f2e24ae 100644 --- a/sphinxcontrib/phpdomain.py +++ b/sphinxcontrib/phpdomain.py @@ -214,15 +214,7 @@ def handle_signature(self, sig, signode): fullname = name else: add_module = True - # name_prefix and a non-static method, means the classname was - # repeated. Trim off the :: - if name_prefix and self.objtype != 'staticmethod': - if name_prefix.startswith(classname): - name_prefix = name_prefix[len(classname):].rstrip('::') - classname = classname.rstrip('::') # TODO seems like wrongly coded, there should be no '::' in the classname - fullname = name_prefix + classname + separator + name - elif name_prefix: - classname = classname.rstrip('::') # TODO seems like wrongly coded, there should be no '::' in the classname + if name_prefix: fullname = name_prefix + name # Currently in a class, but not creating another class, diff --git a/test/rst_doc.html b/test/rst_doc.html index 38078b93..69bff6c3 100644 --- a/test/rst_doc.html +++ b/test/rst_doc.html @@ -687,6 +687,9 @@

Namespaced elementsA class in a namespace

+ + LibraryClass:: + instanceMethod diff --git a/test/rst_nesting_regression.html b/test/rst_nesting_regression.html index 55cc150d..8661e110 100644 --- a/test/rst_nesting_regression.html +++ b/test/rst_nesting_regression.html @@ -5,6 +5,9 @@

Nested method Regression
+ + Largo_Byline:: + populate_variables @@ -38,6 +41,9 @@

Nested method Regression
+ + Largo_Byline:: + generate_byline From ad98351b3a8c2bb3a93d5a488e85676bfc65c461 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Tue, 1 Aug 2023 14:24:45 +0200 Subject: [PATCH 07/23] remove dead code and prevent NS refetch --- sphinxcontrib/phpdomain.py | 23 ++++++----------------- 1 file changed, 6 insertions(+), 17 deletions(-) diff --git a/sphinxcontrib/phpdomain.py b/sphinxcontrib/phpdomain.py index 2f2e24ae..521c54cb 100644 --- a/sphinxcontrib/phpdomain.py +++ b/sphinxcontrib/phpdomain.py @@ -208,12 +208,10 @@ def handle_signature(self, sig, signode): classname = self.env.temp_data.get('php:class') if self.objtype == 'global' or self.objtype == 'function': - add_module = False modname = None classname = None fullname = name else: - add_module = True if name_prefix: fullname = name_prefix + name @@ -247,17 +245,9 @@ def handle_signature(self, sig, signode): name_prefix = modname + NS + name_prefix signode += addnodes.desc_addname(name_prefix, name_prefix) - elif add_module and self.env.config.add_module_names: - if self.objtype == 'global': - nodetext = '' - signode += addnodes.desc_addname(nodetext, nodetext) - else: - modname = self.options.get( - 'namespace', self.env.temp_data.get('php:namespace')) - - if modname and not self.env.temp_data.get('php:in_class', False): - nodetext = modname + NS - signode += addnodes.desc_addname(nodetext, nodetext) + elif modname and not self.env.temp_data.get('php:in_class', False) and self.env.config.add_module_names: + nodetext = modname + NS + signode += addnodes.desc_addname(nodetext, nodetext) signode += addnodes.desc_name(name, name) if not arglist: @@ -461,7 +451,6 @@ def needs_arglist(self): def get_index_text(self, modname, name_cls): name, cls = name_cls - add_modules = self.env.config.add_module_names if self.objtype.endswith('method') or self.objtype == 'attr' or self.objtype == 'case': try: @@ -473,21 +462,21 @@ def get_index_text(self, modname, name_cls): if self.objtype.endswith('method'): if modname and clsname is None: return _('%s() (in namespace %s)') % (name, modname) - elif modname and add_modules: + elif modname and self.env.config.add_module_names: return _('%s() (%s\\%s method)') % (propname, modname, clsname) else: return _('%s() (%s method)') % (propname, clsname) elif self.objtype == 'attr': if modname and clsname is None: return _('%s (in namespace %s)') % (name, modname) - elif modname and add_modules: + elif modname and self.env.config.add_module_names: return _('%s (%s\\%s property)') % (propname, modname, clsname) else: return _('%s (%s property)') % (propname, clsname) elif self.objtype == 'case': if modname and clsname is None: return _('%s enum case') % (name) - elif modname and add_modules: + elif modname and self.env.config.add_module_names: return _('%s (%s\\%s enum case)') % (propname, modname, clsname) else: return _('%s (%s enum case)') % (propname, clsname) From c54ff7c533fc2acccf1b6b30893154c86ef337fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Tue, 1 Aug 2023 14:15:21 +0200 Subject: [PATCH 08/23] improve comments --- doc/conf.py | 13 ------------- setup.py | 1 - sphinxcontrib/__init__.py | 1 - sphinxcontrib/phpdomain.py | 28 +++++++++++++++++----------- 4 files changed, 17 insertions(+), 26 deletions(-) diff --git a/doc/conf.py b/doc/conf.py index 39131ca1..7c30d66f 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -1,16 +1,3 @@ -# -*- coding: utf-8 -*- -# -# sphinxcontrib-rubydomain-acceptancetest documentation build configuration file, created by -# sphinx-quickstart on Sun Apr 25 13:27:18 2010. -# -# This file is execfile()d with the current directory set to its containing dir. -# -# Note that not all possible configuration values are present in this -# autogenerated file. -# -# All configuration values have a default; values that are commented out -# serve to show the default. - import sys, os # If extensions (or modules to document with autodoc) are in another directory, diff --git a/setup.py b/setup.py index 17040e24..5fa8643c 100644 --- a/setup.py +++ b/setup.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- import os from setuptools import setup, find_packages diff --git a/sphinxcontrib/__init__.py b/sphinxcontrib/__init__.py index 35d34fc5..ae0f862b 100644 --- a/sphinxcontrib/__init__.py +++ b/sphinxcontrib/__init__.py @@ -1,4 +1,3 @@ -# -*- coding: utf-8 -*- """ sphinxcontrib ~~~~~~~~~~~~~ diff --git a/sphinxcontrib/phpdomain.py b/sphinxcontrib/phpdomain.py index 521c54cb..c03a7d3c 100644 --- a/sphinxcontrib/phpdomain.py +++ b/sphinxcontrib/phpdomain.py @@ -1,11 +1,7 @@ -# -*- coding: utf-8 -*- """ - sphinx.domains.php - ~~~~~~~~~~~~~~~~~~~ + Sphinx PHP domain. - The PHP domain. Based off of the rubydomain by SHIBUKAWA Yoshiki - - :copyright: Copyright 2017 by Mark Story + :copyright: Copyright 2023 by Mark Story :license: BSD, see LICENSE for details. """ import re @@ -24,27 +20,34 @@ from sphinx.util.docfields import Field, GroupedField, TypedField from sphinx import __version__ as sphinx_version -# log informative messages def log_info( fromdocnode, message: str ): + """ + Log informative message. Should have no affect on exit code. + """ logger = logging.getLogger(__name__) logger.info(f"[phpdomain.info] {message}", location=fromdocnode) -# log messages that should fail CI def log_warning( fromdocnode, message: str ): + """ + Log warning. Should set exit code to non-zero. + """ logger = logging.getLogger(__name__) logger.warning(f"[phpdomain.warning] {message}", location=fromdocnode) -# log assertions that should fail CI +# def log_assert( fromdocnode, value: bool ): + """ + Log assertion. Should set exit code to non-zero. + """ if not value: caller = inspect.getframeinfo(inspect.stack()[1][0]) logger = logging.getLogger(__name__) @@ -87,7 +90,8 @@ def log_assert( def _pseudo_parse_arglist(signode, arglist): # type: (addnodes.desc_signature, unicode) -> None - """"Parse" a list of arguments separated by commas. + """ + "Parse" a list of arguments separated by commas. Arguments can have "optional" annotations given by enclosing them in brackets. Currently, this will split at any comma, even if it's inside a string literal (e.g. default argument value). @@ -640,7 +644,9 @@ def generate(self, docnames=None): class PhpDomain(Domain): - """PHP language domain.""" + """ + PHP language domain. + """ name = 'php' label = 'PHP' object_types = { From 152208ce12581df77b109d006124ee2185825408 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Tue, 1 Aug 2023 18:45:14 +0200 Subject: [PATCH 09/23] refactor "modname" to "namespace" --- sphinxcontrib/phpdomain.py | 166 ++++++++++++++++++------------------- 1 file changed, 83 insertions(+), 83 deletions(-) diff --git a/sphinxcontrib/phpdomain.py b/sphinxcontrib/phpdomain.py index c03a7d3c..71c2446a 100644 --- a/sphinxcontrib/phpdomain.py +++ b/sphinxcontrib/phpdomain.py @@ -202,7 +202,7 @@ def handle_signature(self, sig, signode): name_prefix = "" # determine module and class name (if applicable), as well as full name - modname = self.options.get( + namespace = self.options.get( 'namespace', self.env.temp_data.get('php:namespace')) separator = separators[self.objtype] @@ -212,7 +212,7 @@ def handle_signature(self, sig, signode): classname = self.env.temp_data.get('php:class') if self.objtype == 'global' or self.objtype == 'function': - modname = None + namespace = None classname = None fullname = name else: @@ -229,7 +229,7 @@ def handle_signature(self, sig, signode): classname = '' fullname = name - signode['namespace'] = modname + signode['namespace'] = namespace signode['class'] = self.class_name = classname signode['fullname'] = fullname @@ -245,12 +245,12 @@ def handle_signature(self, sig, signode): signode += addnodes.desc_annotation(sig_prefix, sig_prefix) if name_prefix: - if modname and not self.env.temp_data['php:in_class']: - name_prefix = modname + NS + name_prefix + if namespace and not self.env.temp_data['php:in_class']: + name_prefix = namespace + NS + name_prefix signode += addnodes.desc_addname(name_prefix, name_prefix) - elif modname and not self.env.temp_data.get('php:in_class', False) and self.env.config.add_module_names: - nodetext = modname + NS + elif namespace and not self.env.temp_data.get('php:in_class', False) and self.env.config.add_module_names: + nodetext = namespace + NS signode += addnodes.desc_addname(nodetext, nodetext) signode += addnodes.desc_name(name, name) @@ -304,7 +304,7 @@ def _toc_entry_name(self, sig_node: addnodes.desc_signature) -> str: return '\\'.join(parents + [name + parens]) return '' - def get_index_text(self, modname, name): + def get_index_text(self, namespace, name): """ Return the text for the index entry of the object. """ @@ -316,18 +316,18 @@ def _is_class_member(self): def add_target_and_index(self, name_cls, sig, signode): if self.objtype == 'global': - modname = '' + namespace = None else: - modname = self.options.get( + namespace = self.options.get( 'namespace', self.env.temp_data.get('php:namespace')) separator = separators[self.objtype] if self._is_class_member(): if signode['class']: - prefix = modname and modname + NS or '' + prefix = namespace and namespace + NS or '' else: - prefix = modname and modname + NS or '' + prefix = namespace and namespace + NS or '' else: - prefix = modname and modname + NS or '' + prefix = namespace and namespace + NS or '' fullname = prefix + name_cls[0] # note target @@ -346,7 +346,7 @@ def add_target_and_index(self, name_cls, sig, signode): objects[fullname] = (self.env.docname, self.objtype) if 'noindexentry' not in self.options: - indextext = self.get_index_text(modname, name_cls) + indextext = self.get_index_text(namespace, name_cls) if indextext: self.indexnode['entries'].append(('single', indextext, fullname, fullname, None)) @@ -357,7 +357,7 @@ class PhpGloballevel(PhpObject): Description of an object on global level (global variables). """ - def get_index_text(self, modname, name_cls): + def get_index_text(self, namespace, name_cls): if self.objtype == 'global': return _('%s (global variable)') % name_cls[0] else: @@ -381,17 +381,17 @@ def get_signature_prefix(self, sig): if self.class_name and self.class_name != '': return self.class_name + '::' - def get_index_text(self, modname, name_cls): + def get_index_text(self, namespace, name_cls): if self.objtype == 'function': - if not modname: + if not namespace: return _('%s() (global function)') % name_cls[0] - return _('%s() (function in %s)') % (name_cls[0], modname) + return _('%s() (function in %s)') % (name_cls[0], namespace) elif self.objtype == 'const' and self.class_name != '': return _('%s (class constant)') % (name_cls[0]) elif self.objtype == 'const': - if not modname: + if not namespace: return _('%s (global constant)') % (name_cls[0]) - return _('%s (constant in %s)') % (name_cls[0], modname) + return _('%s (constant in %s)') % (name_cls[0], namespace) else: return '' @@ -405,23 +405,23 @@ class PhpClasslike(PhpObject): def get_signature_prefix(self, sig): return self.objtype + ' ' - def get_index_text(self, modname, name_cls): + def get_index_text(self, namespace, name_cls): if self.objtype == 'class': - if not modname: + if not namespace: return _('%s (class)') % name_cls[0] - return _('%s (class in %s)') % (name_cls[0], modname) + return _('%s (class in %s)') % (name_cls[0], namespace) elif self.objtype == 'interface': - if not modname: + if not namespace: return _('%s (interface)') % name_cls[0] - return _('%s (interface in %s)') % (name_cls[0], modname) + return _('%s (interface in %s)') % (name_cls[0], namespace) elif self.objtype == 'trait': - if not modname: + if not namespace: return _('%s (trait)') % name_cls[0] - return _('%s (trait in %s)') % (name_cls[0], modname) + return _('%s (trait in %s)') % (name_cls[0], namespace) elif self.objtype == 'enum': - if not modname: + if not namespace: return _('%s (enum)') % name_cls[0] - return _('%s (enum in %s)') % (name_cls[0], modname) + return _('%s (enum in %s)') % (name_cls[0], namespace) elif self.objtype == 'exception': return name_cls[0] else: @@ -453,7 +453,7 @@ def get_signature_prefix(self, sig): def needs_arglist(self): return self.objtype == 'method' - def get_index_text(self, modname, name_cls): + def get_index_text(self, namespace, name_cls): name, cls = name_cls if self.objtype.endswith('method') or self.objtype == 'attr' or self.objtype == 'case': @@ -464,24 +464,24 @@ def get_index_text(self, modname, name_cls): clsname = None if self.objtype.endswith('method'): - if modname and clsname is None: - return _('%s() (in namespace %s)') % (name, modname) - elif modname and self.env.config.add_module_names: - return _('%s() (%s\\%s method)') % (propname, modname, clsname) + if namespace and clsname is None: + return _('%s() (in namespace %s)') % (name, namespace) + elif namespace and self.env.config.add_module_names: + return _('%s() (%s\\%s method)') % (propname, namespace, clsname) else: return _('%s() (%s method)') % (propname, clsname) elif self.objtype == 'attr': - if modname and clsname is None: - return _('%s (in namespace %s)') % (name, modname) - elif modname and self.env.config.add_module_names: - return _('%s (%s\\%s property)') % (propname, modname, clsname) + if namespace and clsname is None: + return _('%s (in namespace %s)') % (name, namespace) + elif namespace and self.env.config.add_module_names: + return _('%s (%s\\%s property)') % (propname, namespace, clsname) else: return _('%s (%s property)') % (propname, clsname) elif self.objtype == 'case': - if modname and clsname is None: + if namespace and clsname is None: return _('%s enum case') % (name) - elif modname and self.env.config.add_module_names: - return _('%s (%s\\%s enum case)') % (propname, modname, clsname) + elif namespace and self.env.config.add_module_names: + return _('%s (%s\\%s enum case)') % (propname, namespace, clsname) else: return _('%s (%s enum case)') % (propname, clsname) else: @@ -504,15 +504,15 @@ class PhpNamespace(Directive): def run(self): env = self.state.document.settings.env - modname = self.arguments[0].strip() + namespace = self.arguments[0].strip() noindex = 'noindex' in self.options - env.temp_data['php:namespace'] = modname + env.temp_data['php:namespace'] = namespace env.temp_data['php:class'] = None - env.domaindata['php']['namespaces'][modname] = ( + env.domaindata['php']['namespaces'][namespace] = ( env.docname, self.options.get('synopsis', ''), 'deprecated' in self.options) - targetnode = nodes.target('', '', ids=['namespace-' + modname], + targetnode = nodes.target('', '', ids=['namespace-' + namespace], ismod=True) self.state.document.note_explicit_target(targetnode) ret = [targetnode] @@ -520,9 +520,9 @@ def run(self): # the synopsis isn't printed; in fact, it is only used in the # modindex currently if not noindex: - indextext = _('%s (namespace)') % modname + indextext = _('%s (namespace)') % namespace inode = addnodes.index(entries=[('single', indextext, - 'namespace-' + modname, modname, None)]) + 'namespace-' + namespace, namespace, None)]) ret.append(inode) return ret @@ -541,11 +541,11 @@ class PhpCurrentNamespace(Directive): def run(self): env = self.state.document.settings.env - modname = self.arguments[0].strip() - if modname == 'None': + namespace = self.arguments[0].strip() + if namespace == 'None': env.temp_data['php:namespace'] = None else: - env.temp_data['php:namespace'] = modname + env.temp_data['php:namespace'] = namespace return [] @@ -592,33 +592,33 @@ def generate(self, docnames=None): modules = sorted(self.domain.data['namespaces'].items(), key=lambda x: x[0].lower()) # sort out collapsable modules - prev_modname = '' + prev_namespace = '' num_toplevels = 0 - for modname, (docname, synopsis, deprecated) in modules: + for namespace, (docname, synopsis, deprecated) in modules: if docnames and docname not in docnames: continue for ignore in ignores: - if modname.startswith(ignore): - modname = modname[len(ignore):] + if namespace.startswith(ignore): + namespace = namespace[len(ignore):] stripped = ignore break else: stripped = '' # we stripped the whole module name? - if not modname: - modname, stripped = stripped, '' + if not namespace: + namespace, stripped = stripped, '' - entries = content.setdefault(modname[0].lower(), []) + entries = content.setdefault(namespace[0].lower(), []) - package = modname.split(NS)[0] - if package != modname: + package = namespace.split(NS)[0] + if package != namespace: # it's a submodule - if prev_modname == package: + if prev_namespace == package: # first submodule - make parent a group head entries[-1][1] = 1 - elif not prev_modname.startswith(package): + elif not prev_namespace.startswith(package): # submodule without parent in list, add dummy entry entries.append([stripped + package, 1, '', '', '', '', '']) subtype = 2 @@ -627,10 +627,10 @@ def generate(self, docnames=None): subtype = 0 qualifier = deprecated and _('Deprecated') or '' - entries.append([stripped + modname, subtype, docname, - 'namespace-' + stripped + modname, '', + entries.append([stripped + namespace, subtype, docname, + 'namespace-' + stripped + namespace, '', qualifier, synopsis]) - prev_modname = modname + prev_namespace = namespace # apply heuristics when to collapse modindex at page load: # only collapse if number of toplevel modules is larger than @@ -752,10 +752,10 @@ def resolve_xref(self, env, fromdocname, builder, contnode, title) else: - modname = node.get('php:namespace') + namespace = node.get('php:namespace') clsname = node.get('php:class') searchorder = node.hasattr('refspecific') and 1 or 0 - name, obj = self.find_obj(env, node, modname, clsname, + name, obj = self.find_obj(env, node, namespace, clsname, target, typ, searchorder) if not obj: return None @@ -763,7 +763,7 @@ def resolve_xref(self, env, fromdocname, builder, return make_refnode(builder, fromdocname, obj[0], name, contnode, name) - def find_obj(self, env, fromdocnode, modname, classname, name, type, searchorder=0): + def find_obj(self, env, fromdocnode, namespace, classname, name, type, searchorder=0): """ Find a PHP object for "name", using the given namespace and classname. """ @@ -776,7 +776,7 @@ def find_obj(self, env, fromdocnode, modname, classname, name, type, searchorder if name.startswith(NS): absname = name[1:] else: - absname = (modname + NS if modname else "") \ + absname = (namespace + NS if namespace else "") \ + (classname + NS if classname and '::' not in name else "") \ + name @@ -784,13 +784,13 @@ def find_obj(self, env, fromdocnode, modname, classname, name, type, searchorder if searchorder == 1: if absname in objects: newname = absname - elif modname and classname and \ - modname + NS + classname + '::' + name in objects: - newname = modname + NS + classname + '::' + name - elif modname and modname + NS + name in objects: - newname = modname + NS + name - elif modname and modname + NS + name in objects: - newname = modname + NS + name + elif namespace and classname and \ + namespace + NS + classname + '::' + name in objects: + newname = namespace + NS + classname + '::' + name + elif namespace and namespace + NS + name in objects: + newname = namespace + NS + name + elif namespace and namespace + NS + name in objects: + newname = namespace + NS + name elif classname and classname + '::' + name in objects: newname = classname + '.' + name elif classname and classname + '::$' + name in objects: @@ -806,14 +806,14 @@ def find_obj(self, env, fromdocnode, modname, classname, name, type, searchorder newname = classname + '::' + name elif classname and classname + '::$' + name in objects: newname = classname + '::$' + name - elif modname and modname + NS + name in objects: - newname = modname + NS + name - elif modname and classname and \ - modname + NS + classname + '::' + name in objects: - newname = modname + NS + classname + '::' + name - elif modname and classname and \ - modname + NS + classname + '::$' + name in objects: - newname = modname + NS + classname + '::$' + name + elif namespace and namespace + NS + name in objects: + newname = namespace + NS + name + elif namespace and classname and \ + namespace + NS + classname + '::' + name in objects: + newname = namespace + NS + classname + '::' + name + elif namespace and classname and \ + namespace + NS + classname + '::$' + name in objects: + newname = namespace + NS + classname + '::$' + name # special case: object methods elif type in ('func', 'meth') and '::' not in name and \ 'object::' + name in objects: From 6aa0d336585103f824a674eb416676e2672771b4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Tue, 1 Aug 2023 14:32:36 +0200 Subject: [PATCH 10/23] refactor remaining module mentions --- sphinxcontrib/phpdomain.py | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/sphinxcontrib/phpdomain.py b/sphinxcontrib/phpdomain.py index 71c2446a..9cd4b068 100644 --- a/sphinxcontrib/phpdomain.py +++ b/sphinxcontrib/phpdomain.py @@ -201,7 +201,7 @@ def handle_signature(self, sig, signode): if not name_prefix: name_prefix = "" - # determine module and class name (if applicable), as well as full name + # determine namespace and class name (if applicable), as well as full name namespace = self.options.get( 'namespace', self.env.temp_data.get('php:namespace')) separator = separators[self.objtype] @@ -490,7 +490,7 @@ def get_index_text(self, namespace, name_cls): class PhpNamespace(Directive): """ - Directive to start a new PHP namespace, which are similar to modules. + Directive to start a new PHP namespace, which is similar to module. """ has_content = False required_arguments = 1 @@ -576,7 +576,7 @@ def process_link(self, env, refnode, has_explicit_title, title, target): class PhpNamespaceIndex(Index): """ - Index subclass to provide the Php module index. + Index subclass to provide the PHP namespace index. """ name = 'modindex' @@ -588,13 +588,13 @@ def generate(self, docnames=None): # list of prefixes to ignore ignores = self.domain.env.config['modindex_common_prefix'] ignores = sorted(ignores, key=len, reverse=True) - # list of all modules, sorted by module name - modules = sorted(self.domain.data['namespaces'].items(), + # list of all namespaces, sorted by name + namespaces = sorted(self.domain.data['namespaces'].items(), key=lambda x: x[0].lower()) - # sort out collapsable modules + # sort out collapsable namespaces prev_namespace = '' num_toplevels = 0 - for namespace, (docname, synopsis, deprecated) in modules: + for namespace, (docname, synopsis, deprecated) in namespaces: if docnames and docname not in docnames: continue @@ -606,7 +606,7 @@ def generate(self, docnames=None): else: stripped = '' - # we stripped the whole module name? + # we stripped the whole namespace name? if not namespace: namespace, stripped = stripped, '' @@ -614,12 +614,12 @@ def generate(self, docnames=None): package = namespace.split(NS)[0] if package != namespace: - # it's a submodule + # it's a subnamespace if prev_namespace == package: - # first submodule - make parent a group head + # first subnamespace - make parent a group head entries[-1][1] = 1 elif not prev_namespace.startswith(package): - # submodule without parent in list, add dummy entry + # subnamespace without parent in list, add dummy entry entries.append([stripped + package, 1, '', '', '', '', '']) subtype = 2 else: @@ -633,9 +633,9 @@ def generate(self, docnames=None): prev_namespace = namespace # apply heuristics when to collapse modindex at page load: - # only collapse if number of toplevel modules is larger than - # number of submodules - collapse = len(modules) - num_toplevels < num_toplevels + # only collapse if number of toplevel namespaces is larger than + # number of subnamespaces + collapse = len(namespaces) - num_toplevels < num_toplevels # sort by first letter content = sorted(content.items()) From 35499bca32cc0db614dbd18ed4b698a1f5a322a0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Tue, 1 Aug 2023 18:46:14 +0200 Subject: [PATCH 11/23] remove dead code --- sphinxcontrib/phpdomain.py | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/sphinxcontrib/phpdomain.py b/sphinxcontrib/phpdomain.py index 9cd4b068..9fe72025 100644 --- a/sphinxcontrib/phpdomain.py +++ b/sphinxcontrib/phpdomain.py @@ -784,15 +784,10 @@ def find_obj(self, env, fromdocnode, namespace, classname, name, type, searchord if searchorder == 1: if absname in objects: newname = absname - elif namespace and classname and \ - namespace + NS + classname + '::' + name in objects: - newname = namespace + NS + classname + '::' + name - elif namespace and namespace + NS + name in objects: - newname = namespace + NS + name elif namespace and namespace + NS + name in objects: newname = namespace + NS + name elif classname and classname + '::' + name in objects: - newname = classname + '.' + name + newname = classname + '::' + name elif classname and classname + '::$' + name in objects: newname = classname + '::$' + name elif name in objects: @@ -808,16 +803,6 @@ def find_obj(self, env, fromdocnode, namespace, classname, name, type, searchord newname = classname + '::$' + name elif namespace and namespace + NS + name in objects: newname = namespace + NS + name - elif namespace and classname and \ - namespace + NS + classname + '::' + name in objects: - newname = namespace + NS + classname + '::' + name - elif namespace and classname and \ - namespace + NS + classname + '::$' + name in objects: - newname = namespace + NS + classname + '::$' + name - # special case: object methods - elif type in ('func', 'meth') and '::' not in name and \ - 'object::' + name in objects: - newname = 'object::' + name if newname is None: log_info(fromdocnode, f"Target not found '{absname}'") return None, None From 1cd7705828d9e0ac1268e56fd3eb40e4de6fcc8c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Tue, 1 Aug 2023 18:47:17 +0200 Subject: [PATCH 12/23] reserved names are never resolved using NS --- sphinxcontrib/phpdomain.py | 5 ++++- test/log.txt | 34 +++++++--------------------------- test/rst_doc.html | 2 +- test/rst_doc.md | 2 +- test/rst_doc2.html | 16 ++++++++-------- test/rst_doc2.md | 16 ++++++++-------- 6 files changed, 29 insertions(+), 46 deletions(-) diff --git a/sphinxcontrib/phpdomain.py b/sphinxcontrib/phpdomain.py index 9fe72025..c6d701fe 100644 --- a/sphinxcontrib/phpdomain.py +++ b/sphinxcontrib/phpdomain.py @@ -803,9 +803,12 @@ def find_obj(self, env, fromdocnode, namespace, classname, name, type, searchord newname = classname + '::$' + name elif namespace and namespace + NS + name in objects: newname = namespace + NS + name + if newname is None: - log_info(fromdocnode, f"Target not found '{absname}'") + if name not in ['array', 'bool', 'callable', 'false', 'float', 'int', 'iterable', 'mixed', 'never', 'null', 'numeric', 'object', 'parent', 'resource', 'self', 'static', 'string', 'true', 'void']: + log_info(fromdocnode, f"Target {absname} not found") return None, None + return newname, objects[newname] def get_objects(self): diff --git a/test/log.txt b/test/log.txt index 30f2478d..61449fac 100644 --- a/test/log.txt +++ b/test/log.txt @@ -1,27 +1,7 @@ -test/method.md:11: [phpdomain.info] Target not found 'Foo\array' -test/ns.md:48: [phpdomain.info] Target not found 'A2::simplify' -test/ns.md:53: [phpdomain.info] Target not found 'Bar2\A::simplify' -test/rst_doc.md:24: [phpdomain.info] Target not found 'array' -test/rst_doc.md:27: [phpdomain.info] Target not found 'boolean' -test/rst_doc.md:40: [phpdomain.info] Target not found 'DateTime\int' -test/rst_doc.md:40: [phpdomain.info] Target not found 'DateTime\int' -test/rst_doc.md:40: [phpdomain.info] Target not found 'DateTime\int' -test/rst_doc.md:48: [phpdomain.info] Target not found 'DateTime\int' -test/rst_doc.md:48: [phpdomain.info] Target not found 'DateTime\int' -test/rst_doc.md:48: [phpdomain.info] Target not found 'DateTime\int' -test/rst_doc.md:104: [phpdomain.info] Target not found 'DateTimeInterface\int' -test/rst_doc.md:104: [phpdomain.info] Target not found 'DateTimeInterface\int' -test/rst_doc.md:104: [phpdomain.info] Target not found 'DateTimeInterface\int' -test/rst_doc.md:112: [phpdomain.info] Target not found 'DateTimeInterface\int' -test/rst_doc.md:112: [phpdomain.info] Target not found 'DateTimeInterface\int' -test/rst_doc.md:112: [phpdomain.info] Target not found 'DateTimeInterface\int' -test/rst_doc.md:221: [phpdomain.info] Target not found 'LibraryName\string' -test/rst_doc.md:221: [phpdomain.info] Target not found 'LibraryName\string' -test/rst_doc.md:497: [phpdomain.info] Target not found 'OtherLibrary\ReturningClass\string' -test/rst_doc.md:502: [phpdomain.info] Target not found 'OtherLibrary\ReturningClass\int|string|OtherLibrary\ReturnedClass|LibraryName\SubPackage\SubpackageInterface|null' -test/rst_doc2.md:11: [phpdomain.info] Target not found 'Imagine\Draw\ImagineImageImageInterface::draw' -test/rst_doc2.md:17: [phpdomain.info] Target not found 'Imagine\Draw\DrawerInterface\ImagineImagePointInterface' -test/rst_doc2.md:17: [phpdomain.info] Target not found 'Imagine\Draw\DrawerInterface\ImagineImageBoxInterface' -test/rst_doc2.md:17: [phpdomain.info] Target not found 'Imagine\Draw\DrawerInterface\integer' -test/rst_doc2.md:17: [phpdomain.info] Target not found 'Imagine\Draw\DrawerInterface\integer' -test/rst_doc2.md:17: [phpdomain.info] Target not found 'Imagine\Draw\DrawerInterface\ImagineImageColor' +test/ns.md:48: [phpdomain.info] Target A2::simplify not found +test/ns.md:53: [phpdomain.info] Target Bar2\A::simplify not found +test/rst_doc.md:502: [phpdomain.info] Target OtherLibrary\ReturningClass\int|string|OtherLibrary\ReturnedClass|LibraryName\SubPackage\SubpackageInterface|null not found +test/rst_doc2.md:11: [phpdomain.info] Target Imagine\Image\ImageInterface::draw not found +test/rst_doc2.md:17: [phpdomain.info] Target Imagine\Image\PointInterface not found +test/rst_doc2.md:17: [phpdomain.info] Target Imagine\Image\BoxInterface not found +test/rst_doc2.md:17: [phpdomain.info] Target Imagine\Image\Color not found diff --git a/test/rst_doc.html b/test/rst_doc.html index 69bff6c3..2397a543 100644 --- a/test/rst_doc.html +++ b/test/rst_doc.html @@ -68,7 +68,7 @@

Globals - boolean + bool

diff --git a/test/rst_doc.md b/test/rst_doc.md index a5fd2c79..127bcdc5 100644 --- a/test/rst_doc.md +++ b/test/rst_doc.md @@ -24,7 +24,7 @@ Globals :param needle: The element to search for. :param array haystack: The array to search. :returns: Element exists in array. - :returntype: boolean + :returntype: bool Classes ======= diff --git a/test/rst_doc2.html b/test/rst_doc2.html index 81294b4d..cf7f78f9 100644 --- a/test/rst_doc2.html +++ b/test/rst_doc2.html @@ -19,7 +19,7 @@

Top Level NamespaceImagineImageImageInterface::draw.

+

Instance of this interface is returned by Imagine\Image\ImageInterface::draw.

Imagine\Draw\DrawerInterface::arc(PointInterface$center, BoxInterface$size, $start, $end, Color$color)
@@ -29,29 +29,29 @@

Top Level Namespace
  • -

    $center (ImagineImagePointInterface) – Center of the arc.

    +

    $center (Imagine\Image\PointInterface) – Center of the arc.

  • -

    $size (ImagineImageBoxInterface) – Size of the bounding box.

    +

    $size (Imagine\Image\BoxInterface) – Size of the bounding box.

  • -

    $start (integer) – Start angle.

    +

    $start (int) – Start angle.

  • -

    $end (integer) – End angle.

    +

    $end (int) – End angle.

  • -

    $color (ImagineImageColor) – Line color.

    +

    $color (Imagine\Image\Color) – Line color.

Throws:
-

ImagineExceptionRuntimeException

+

\Imagine\Exception\RuntimeException

Returns:
-

ImagineDrawDrawerInterface

+

DrawerInterface

diff --git a/test/rst_doc2.md b/test/rst_doc2.md index a7774bd8..79b7e168 100644 --- a/test/rst_doc2.md +++ b/test/rst_doc2.md @@ -8,21 +8,21 @@ namespace ``Imagine\Draw`` .. php:class:: DrawerInterface -Instance of this interface is returned by :php:meth:`Imagine\Image\ImageInterface::draw`. +Instance of this interface is returned by :php:meth:`\\Imagine\\Image\\ImageInterface::draw`. .. php:method:: arc(PointInterface $center, BoxInterface $size, $start, $end, Color $color) Draws an arc on a starting at a given x, y coordinates under a given start and end angles - :param Imagine\Image\PointInterface $center: Center of the arc. - :param Imagine\Image\BoxInterface $size: Size of the bounding box. - :param integer $start: Start angle. - :param integer $end: End angle. - :param Imagine\Image\Color $color: Line color. + :param \\Imagine\\Image\\PointInterface $center: Center of the arc. + :param \\Imagine\\Image\\BoxInterface $size: Size of the bounding box. + :param int $start: Start angle. + :param int $end: End angle. + :param \\Imagine\\Image\\Color $color: Line color. - :throws: Imagine\Exception\RuntimeException + :throws: \\Imagine\\Exception\\RuntimeException - :returns: Imagine\Draw\DrawerInterface + :returns: DrawerInterface Re-used namespace ================= From 54a4a8d207f3658b3b1e30244a2513914b2a4dbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Wed, 2 Aug 2023 13:13:23 +0200 Subject: [PATCH 13/23] "global" is for global variables only --- test/rst_doc.html | 2 +- test/rst_doc.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/test/rst_doc.html b/test/rst_doc.html index 2397a543..0d85a3ec 100644 --- a/test/rst_doc.html +++ b/test/rst_doc.html @@ -418,7 +418,7 @@

More globals after classes +
strpos($needle, $haystack)

Position of needle in haystack

diff --git a/test/rst_doc.md b/test/rst_doc.md index 127bcdc5..ba37c247 100644 --- a/test/rst_doc.md +++ b/test/rst_doc.md @@ -143,7 +143,7 @@ More globals after classes A global variable -.. php:global:: strpos($needle, $haystack) +.. php:function:: strpos($needle, $haystack) Position of needle in haystack From 1bee410ec5a92d0d5e30c54dc82a998415a58adb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Wed, 2 Aug 2023 13:55:00 +0200 Subject: [PATCH 14/23] stricter identifier matching in signature --- sphinxcontrib/phpdomain.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/sphinxcontrib/phpdomain.py b/sphinxcontrib/phpdomain.py index c6d701fe..eb80d9c3 100644 --- a/sphinxcontrib/phpdomain.py +++ b/sphinxcontrib/phpdomain.py @@ -56,13 +56,13 @@ def log_assert( php_sig_re = re.compile( r'''^ (public\ |protected\ |private\ )? # visibility (final\ |abstract\ |static\ )? # modifiers - ([\w\\]+\:\:)? # class name(s) - (\$?[\w\\]+) \s* # thing name + ((?:\\?(?!\d)\w+)\:\:)? # class name + (\$?(?:\\?(?!\d)\w+)+) \s* # thing name (?: \((.*)\) # optional: arguments - (?:\s* -> \s* (.*))? # return annotation + (?: \s* -> \s* (.*))? # return annotation )? - (?:\s* : \s* (.*))? # backed enum type / case value + (?: \s* : \s* (.*))? # backed enum type / case value $ # and nothing more ''', re.VERBOSE) From f07b9aa0f1851a25e8adf59b4305432f938d5699 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Wed, 2 Aug 2023 13:57:04 +0200 Subject: [PATCH 15/23] improve separators def --- sphinxcontrib/phpdomain.py | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) diff --git a/sphinxcontrib/phpdomain.py b/sphinxcontrib/phpdomain.py index eb80d9c3..a66fd350 100644 --- a/sphinxcontrib/phpdomain.py +++ b/sphinxcontrib/phpdomain.py @@ -70,18 +70,19 @@ def log_assert( NS = '\\' separators = { + 'global': None, + 'namespace': None, + 'function': None, + 'interface': None, + 'class': None, + 'trait': None, + 'enum': None, + 'exception': None, + 'method': '::', - 'function': NS, - 'class': NS, - 'namespace': NS, - 'global': '', 'const': '::', 'attr': '::$', - 'exception': '', 'staticmethod': '::', - 'interface': NS, - 'trait': NS, - 'enum': NS, 'case': '::', } @@ -399,7 +400,7 @@ def get_index_text(self, namespace, name_cls): class PhpClasslike(PhpObject): """ Description of a class-like object - (classes, exceptions, interfaces, traits, enums). + (classes, interfaces, traits, enums). """ def get_signature_prefix(self, sig): @@ -438,7 +439,7 @@ def before_content(self): class PhpClassmember(PhpObject): """ - Description of a class member (methods, attributes). + Description of a class member (methods, properties). """ def get_signature_prefix(self, sig): From c28418620dcc3595cb218a2f45551a88f0eb695b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Wed, 2 Aug 2023 13:57:38 +0200 Subject: [PATCH 16/23] log unable to parse signature --- sphinxcontrib/phpdomain.py | 1 + 1 file changed, 1 insertion(+) diff --git a/sphinxcontrib/phpdomain.py b/sphinxcontrib/phpdomain.py index a66fd350..1aaacdf6 100644 --- a/sphinxcontrib/phpdomain.py +++ b/sphinxcontrib/phpdomain.py @@ -195,6 +195,7 @@ def handle_signature(self, sig, signode): """ m = php_sig_re.match(sig) if m is None: + log_warning(signode, 'Invalid signature: ' + sig) raise ValueError visibility, modifiers, name_prefix, name, arglist, retann, enumtype = m.groups() From 5ac94101b87b1ed6327759654d90d9535681c9af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Wed, 2 Aug 2023 19:11:14 +0200 Subject: [PATCH 17/23] always resolve refs using namespace, never fallback to global --- sphinxcontrib/phpdomain.py | 91 ++++++++++++++----------------- test/log.txt | 2 +- test/rst_doc.html | 70 ++++++++++++++---------- test/rst_doc.md | 106 +++++++++++++++++++------------------ 4 files changed, 138 insertions(+), 131 deletions(-) diff --git a/sphinxcontrib/phpdomain.py b/sphinxcontrib/phpdomain.py index 1aaacdf6..12f8762e 100644 --- a/sphinxcontrib/phpdomain.py +++ b/sphinxcontrib/phpdomain.py @@ -213,7 +213,7 @@ def handle_signature(self, sig, signode): else: classname = self.env.temp_data.get('php:class') - if self.objtype == 'global' or self.objtype == 'function': + if self.objtype == 'global': namespace = None classname = None fullname = name @@ -222,7 +222,7 @@ def handle_signature(self, sig, signode): fullname = name_prefix + name # Currently in a class, but not creating another class, - elif classname and not self.objtype in ['class', 'exception', 'interface', 'trait', 'enum']: + elif classname and not self.objtype in ['class', 'exception', 'interface', 'trait', 'enum', 'function']: if not self.env.temp_data['php:in_class']: name_prefix = classname + separator @@ -322,7 +322,6 @@ def add_target_and_index(self, name_cls, sig, signode): else: namespace = self.options.get( 'namespace', self.env.temp_data.get('php:namespace')) - separator = separators[self.objtype] if self._is_class_member(): if signode['class']: prefix = namespace and namespace + NS or '' @@ -557,21 +556,26 @@ class PhpXRefRole(XRefRole): """ def process_link(self, env, refnode, has_explicit_title, title, target): if not has_explicit_title: - if title.startswith("::"): - title = title[2:] - target = target.lstrip('~') # only has a meaning for the title - # If the first char is '~' don't display the leading namespace & class. + if target.startswith('~'): # only has a meaning for the title + target = title[1:] if title.startswith('~'): - m = re.search(r"(?:.+[:]{2}|(?:.*?\\{1,2})+)?(.*)\Z", title) - if m: - title = m.group(1) + title = title[1:] + title = re.sub(r'^[\w\\]+::', '', title) if title.startswith(NS): title = title[1:] - refnode['php:namespace'] = env.temp_data.get('php:namespace') - refnode['php:class'] = env.temp_data.get('php:class') + reftype = refnode.attributes['reftype'] + if reftype == 'global': + namespace = None + classname = None + else: + namespace = env.temp_data.get('php:namespace') + classname = env.temp_data.get('php:class') + + refnode['php:namespace'] = namespace + refnode['php:class'] = classname return title, target @@ -756,20 +760,19 @@ def resolve_xref(self, env, fromdocname, builder, else: namespace = node.get('php:namespace') clsname = node.get('php:class') - searchorder = node.hasattr('refspecific') and 1 or 0 name, obj = self.find_obj(env, node, namespace, clsname, - target, typ, searchorder) + target, typ) if not obj: return None else: return make_refnode(builder, fromdocname, obj[0], name, contnode, name) - def find_obj(self, env, fromdocnode, namespace, classname, name, type, searchorder=0): + def find_obj(self, env, fromdocnode, namespace, classname, name, type): """ Find a PHP object for "name", using the given namespace and classname. """ - # skip parens + # strip parenthesis if name[-2:] == '()': name = name[:-2] @@ -778,40 +781,28 @@ def find_obj(self, env, fromdocnode, namespace, classname, name, type, searchord if name.startswith(NS): absname = name[1:] else: - absname = (namespace + NS if namespace else "") \ - + (classname + NS if classname and '::' not in name else "") \ - + name - - newname = None - if searchorder == 1: - if absname in objects: - newname = absname - elif namespace and namespace + NS + name in objects: - newname = namespace + NS + name - elif classname and classname + '::' + name in objects: - newname = classname + '::' + name - elif classname and classname + '::$' + name in objects: - newname = classname + '::$' + name - elif name in objects: - newname = name - else: - if absname in objects: - newname = absname - elif name in objects: - newname = name - elif classname and classname + '::' + name in objects: - newname = classname + '::' + name - elif classname and classname + '::$' + name in objects: - newname = classname + '::$' + name - elif namespace and namespace + NS + name in objects: - newname = namespace + NS + name - - if newname is None: - if name not in ['array', 'bool', 'callable', 'false', 'float', 'int', 'iterable', 'mixed', 'never', 'null', 'numeric', 'object', 'parent', 'resource', 'self', 'static', 'string', 'true', 'void']: - log_info(fromdocnode, f"Target {absname} not found") - return None, None - - return newname, objects[newname] + absname = (namespace + NS if namespace else "") + name + + if absname not in objects and name in objects: + # constants/functions can be namespaced, but allow fallback to global namespace the same way as PHP does + name_type = objects[name][1] + if (name_type == 'function' or name_type == 'const') and NS not in name and '::' not in name: + absname = name + else: + if namespace and name.startswith(namespace + NS): + log_info(fromdocnode, f"Target {absname} not found - did you mean {name[len(namespace + NS):]}?") + else: + log_info(fromdocnode, f"Target {absname} not found - did you mean {NS + name}?") + absname = name # fallback for BC, might be removed in the next major release + + if absname in objects: + return absname, objects[absname] + + # PHP reserved words are never resolved using NS and ignore them when not defined + if name not in ['array', 'bool', 'callable', 'false', 'float', 'int', 'iterable', 'mixed', 'never', 'null', 'numeric', 'object', 'parent', 'resource', 'self', 'static', 'string', 'true', 'void']: + log_info(fromdocnode, f"Target {absname} not found") + + return None, None def get_objects(self): for ns, info in self.data['namespaces'].items(): diff --git a/test/log.txt b/test/log.txt index 61449fac..2fc037f9 100644 --- a/test/log.txt +++ b/test/log.txt @@ -1,6 +1,6 @@ test/ns.md:48: [phpdomain.info] Target A2::simplify not found test/ns.md:53: [phpdomain.info] Target Bar2\A::simplify not found -test/rst_doc.md:502: [phpdomain.info] Target OtherLibrary\ReturningClass\int|string|OtherLibrary\ReturnedClass|LibraryName\SubPackage\SubpackageInterface|null not found +test/rst_doc.md:506: [phpdomain.info] Target OtherLibrary\int|string|ReturnedClass|\LibraryName\SubPackage\SubpackageInterface|null not found test/rst_doc2.md:11: [phpdomain.info] Target Imagine\Image\ImageInterface::draw not found test/rst_doc2.md:17: [phpdomain.info] Target Imagine\Image\PointInterface not found test/rst_doc2.md:17: [phpdomain.info] Target Imagine\Image\BoxInterface not found diff --git a/test/rst_doc.html b/test/rst_doc.html index 0d85a3ec..24e4e5c5 100644 --- a/test/rst_doc.html +++ b/test/rst_doc.html @@ -618,7 +618,7 @@

Test Case - Prefix less links

Namespaced elements

-
namespaced_function($one[, $two])
+
LibraryName\namespaced_function($one[, $two])

A function in a namespace

@@ -1026,14 +1026,14 @@

Test Case - not including namespace - LibraryName\OtherClass + OtherClass

- LibraryName\ThirdClass + ThirdClass

@@ -1135,17 +1135,10 @@

Test Case - not including namespace - - LibraryName\TemplateTrait - - -

- LibraryName\TemplateTrait::render() + TemplateTrait::render()

@@ -1234,6 +1227,13 @@

Test Case - global access + + in_array() + + +

@@ -1352,6 +1352,11 @@

Test Case - Test subpackage links + LibraryName\SubPackage + +

@@ -1380,6 +1385,13 @@

Test Case - Test subpackage links + + NestedNamespaceException + + +

@@ -1428,7 +1440,7 @@

Return Types - OtherLibrary\ReturnedClass + ReturnedClass @@ -1510,7 +1522,7 @@

Return Types
Returns:
-

The value of a specific global constant.

+

The value of a specific global constant. # TODO link is not working without “\”

Return type:
@@ -1605,7 +1617,7 @@

Return Types - int|string|OtherLibrary\ReturnedClass|LibraryName\SubPackage\SubpackageInterface|null + int|string|ReturnedClass|\LibraryName\SubPackage\SubpackageInterface|null

@@ -1958,7 +1970,7 @@

Advanced Enumerations

-

An alias for Example\Advanced\Suit::Hearts.

+

An alias for Suit::Hearts.

@@ -1978,7 +1990,7 @@

Advanced Enumerations
-

An alias for Example\Advanced\Suit::Diamonds.

+

An alias for Suit::Diamonds.

@@ -1998,7 +2010,7 @@

Advanced Enumerations
-

An alias for Example\Advanced\Suit::Clubs.

+

An alias for Suit::Clubs.

@@ -2018,7 +2030,7 @@

Advanced Enumerations
-

An alias for Example\Advanced\Suit::Spades.

+

An alias for Suit::Spades.

@@ -2107,77 +2119,77 @@

Links to Advanced Enumeration Example - Example\Advanced\Suit + Suit

- Example\Advanced\Suit::Hearts + Suit::Hearts

- Example\Advanced\Suit::Diamonds + Suit::Diamonds

- Example\Advanced\Suit::Clubs + Suit::Clubs

- Example\Advanced\Suit::Spades + Suit::Spades

- Example\Advanced\Suit::color + Suit::color

- Example\Advanced\Suit::values + Suit::values

- Example\Advanced\Suit::Roses + Suit::Roses

- Example\Advanced\Suit::Bells + Suit::Bells

- Example\Advanced\Suit::Acorns + Suit::Acorns

- Example\Advanced\Suit::Shields + Suit::Shields

diff --git a/test/rst_doc.md b/test/rst_doc.md index ba37c247..1c3e9660 100644 --- a/test/rst_doc.md +++ b/test/rst_doc.md @@ -337,9 +337,9 @@ Within a namespace context you don't need to include the namespace in links. :php:const:`LibraryClass::TEST_CONST` -:php:class:`LibraryName\\OtherClass` +:php:class:`OtherClass` -:php:class:`LibraryName\\ThirdClass` +:php:class:`ThirdClass` :php:class:`NamespaceClass` @@ -369,22 +369,20 @@ Within a namespace context you don't need to include the namespace in links. :php:trait:`TemplateTrait` -:php:trait:`LibraryName\\TemplateTrait` - -:php:func:`LibraryName\\TemplateTrait::render()` +:php:func:`TemplateTrait::render()` Test Case - Links with prefix trimming -------------------------------------- All of the following links should not be prefixed with a namespace. -:php:interface:`~LibraryName\\LibraryInterface` +:php:interface:`~LibraryInterface` -:php:class:`~LibraryName\\LibraryClass` +:php:class:`~LibraryClass` -:php:trait:`~LibraryName\\TemplateTrait` +:php:trait:`~TemplateTrait` -:php:exc:`~LibraryName\\NamespaceException` +:php:exc:`~NamespaceException` All of the following links should not be prefixed with a classname. @@ -398,25 +396,27 @@ All of the following links should not be prefixed with a classname. Test Case - global access ------------------------- -:php:class:`DateTime` +:php:class:`\\DateTime` -:php:func:`DateTime::setTime()` +:php:func:`\\DateTime::setTime()` :php:global:`$global_var` :php:const:`SOME_CONSTANT` -:php:attr:`LibraryName\\LibraryClass::$property` +:php:func:`in_array()` -:php:attr:`~LibraryName\\LibraryClass::$property` Should not be prefixed with classname. +:php:attr:`\\LibraryName\\LibraryClass::$property` -:php:const:`LibraryName\\LibraryClass::TEST_CONST` +:php:attr:`~\\LibraryName\\LibraryClass::$property` Should not be prefixed with classname. -:php:const:`LibraryName\\NS_CONST` +:php:const:`\\LibraryName\\LibraryClass::TEST_CONST` -:php:interface:`DateTimeInterface` +:php:const:`\\LibraryName\\NS_CONST` -:php:func:`DateTimeInterface::setTime()` +:php:interface:`\\DateTimeInterface` + +:php:func:`\\DateTimeInterface::setTime()` Any Cross Ref ============= @@ -447,15 +447,19 @@ Test Case - Test subpackage links :php:ns:`LibraryName\\SubPackage` +:php:ns:`\\LibraryName\\SubPackage` + :php:class:`SubpackageClass` -:php:class:`LibraryName\\SubPackage\\SubpackageClass` +:php:class:`\\LibraryName\\SubPackage\\SubpackageClass` :php:interface:`SubpackageInterface` -:php:class:`LibraryName\\SubPackage\\SubpackageInterface` +:php:class:`\\LibraryName\\SubPackage\\SubpackageInterface` + +:php:exc:`NestedNamespaceException` -:php:exc:`LibraryName\\SubPackage\\NestedNamespaceException` +:php:exc:`\\LibraryName\\SubPackage\\NestedNamespaceException` Return Types ============ @@ -469,27 +473,27 @@ Return Types .. php:method:: returnClassFromSameNamespace() :returns: An object instance of a class from the same namespace. - :returntype: OtherLibrary\\ReturnedClass + :returntype: ReturnedClass .. php:method:: returnClassFromOtherNamespace() :returns: An object instance of a class from another namespace. - :returntype: LibraryName\\SubPackage\\SubpackageInterface + :returntype: \\LibraryName\\SubPackage\\SubpackageInterface .. php:method:: returnClassConstant() :returns: The value of a specific class constant. - :returntype: LibraryName\\NamespaceClass::NAMESPACE_CONST + :returntype: \\LibraryName\\NamespaceClass::NAMESPACE_CONST .. php:method:: returnGlobalConstant() - :returns: The value of a specific global constant. + :returns: The value of a specific global constant. # TODO link is not working without "\\" :returntype: SOME_CONSTANT .. php:method:: returnExceptionInstance() :returns: An instance of an exception. - :returntype: InvalidArgumentException + :returntype: \\InvalidArgumentException .. php:method:: returnScalarType() @@ -499,7 +503,7 @@ Return Types .. php:method:: returnUnionType() :returns: Any of a whole bunch of things specified with a PHP 8 union type. - :returntype: int|string|OtherLibrary\\ReturnedClass|LibraryName\\SubPackage\\SubpackageInterface|null + :returntype: int|string|ReturnedClass|\\LibraryName\\SubPackage\\SubpackageInterface|null .. php:class:: ReturnedClass @@ -563,19 +567,19 @@ Advanced Enumerations .. php:const:: Roses() : Hearts - An alias for :php:case:`Example\\Advanced\\Suit::Hearts`. + An alias for :php:case:`Suit::Hearts`. .. php:const:: Bells : Diamonds - An alias for :php:case:`Example\\Advanced\\Suit::Diamonds`. + An alias for :php:case:`Suit::Diamonds`. .. php:const:: Acorns : Clubs - An alias for :php:case:`Example\\Advanced\\Suit::Clubs`. + An alias for :php:case:`Suit::Clubs`. .. php:const:: Shields : Spades - An alias for :php:case:`Example\\Advanced\\Suit::Spades`. + An alias for :php:case:`Suit::Spades`. Enumeration Links ----------------- @@ -583,51 +587,51 @@ Enumeration Links Links to Basic Enumeration Example ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -:php:enum:`Example\\Basic\\Suit` +:php:enum:`\\Example\\Basic\\Suit` -:php:case:`Example\\Basic\\Suit::Hearts` +:php:case:`\\Example\\Basic\\Suit::Hearts` -:php:case:`Example\\Basic\\Suit::Diamonds` +:php:case:`\\Example\\Basic\\Suit::Diamonds` -:php:case:`Example\\Basic\\Suit::Clubs` +:php:case:`\\Example\\Basic\\Suit::Clubs` -:php:case:`Example\\Basic\\Suit::Spades` +:php:case:`\\Example\\Basic\\Suit::Spades` Links to Backed Enumeration Example ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -:php:enum:`Example\\Backed\\Suit` +:php:enum:`\\Example\\Backed\\Suit` -:php:case:`Example\\Backed\\Suit::Hearts` +:php:case:`\\Example\\Backed\\Suit::Hearts` -:php:case:`Example\\Backed\\Suit::Diamonds` +:php:case:`\\Example\\Backed\\Suit::Diamonds` -:php:case:`Example\\Backed\\Suit::Clubs` +:php:case:`\\Example\\Backed\\Suit::Clubs` -:php:case:`Example\\Backed\\Suit::Spades` +:php:case:`\\Example\\Backed\\Suit::Spades` Links to Advanced Enumeration Example ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -:php:enum:`Example\\Advanced\\Suit` +:php:enum:`Suit` -:php:case:`Example\\Advanced\\Suit::Hearts` +:php:case:`Suit::Hearts` -:php:case:`Example\\Advanced\\Suit::Diamonds` +:php:case:`Suit::Diamonds` -:php:case:`Example\\Advanced\\Suit::Clubs` +:php:case:`Suit::Clubs` -:php:case:`Example\\Advanced\\Suit::Spades` +:php:case:`Suit::Spades` -:php:meth:`Example\\Advanced\\Suit::color` +:php:meth:`Suit::color` -:php:meth:`Example\\Advanced\\Suit::values` +:php:meth:`Suit::values` -:php:const:`Example\\Advanced\\Suit::Roses` +:php:const:`Suit::Roses` -:php:const:`Example\\Advanced\\Suit::Bells` +:php:const:`Suit::Bells` -:php:const:`Example\\Advanced\\Suit::Acorns` +:php:const:`Suit::Acorns` -:php:const:`Example\\Advanced\\Suit::Shields` +:php:const:`Suit::Shields` ``` From 0d234b0469ab3b2abcabdc67c5b0d240ff243701 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Wed, 2 Aug 2023 19:31:27 +0200 Subject: [PATCH 18/23] Enforce Coding Style using Black --- .github/workflows/ci.yml | 8 +- doc/conf.py | 116 ++++--- setup.py | 47 ++- sphinxcontrib/__init__.py | 2 +- sphinxcontrib/phpdomain.py | 669 +++++++++++++++++++++---------------- test/conf.py | 16 +- 6 files changed, 477 insertions(+), 381 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 9bc1e437..7f023d9f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,7 +45,13 @@ jobs: (cd _build/html && find . -name '*.html' -exec sh -c 'xmllint {} --xpath '"'"'//div[@role="main"]'"'"' | xmllint --format - > ../../{}' \;) sed -i -r 's~.*/(test/.* \[phpdomain)~\1~;t;d' log.txt - - name: Diff Unit Tests Output + - name: Apply Coding Style + if: matrix.python == '3.11' + run: | + pip install black + python -m black . + + - name: Diff Unit Tests Output and Coding Style run: | cd test rm -r _build diff --git a/doc/conf.py b/doc/conf.py index 7c30d66f..95da0d1e 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -3,188 +3,193 @@ # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. -sys.path.append(os.path.abspath('..')) +sys.path.append(os.path.abspath("..")) # -- General configuration ----------------------------------------------------- # If your documentation needs a minimal Sphinx version, state it here. -needs_sphinx = '1.0' +needs_sphinx = "1.0" # Add any Sphinx extension module names here, as strings. They can be extensions # coming with Sphinx (named 'sphinx.ext.*') or your custom ones. -extensions = ['sphinxcontrib.phpdomain'] +extensions = ["sphinxcontrib.phpdomain"] # Add any paths that contain templates here, relative to this directory. -templates_path = ['_templates'] +templates_path = ["_templates"] # The suffix of source filenames. -source_suffix = '.rst' +source_suffix = ".rst" # The encoding of source files. -#source_encoding = 'utf-8-sig' +# source_encoding = 'utf-8-sig' # The master toctree document. -master_doc = 'index' +master_doc = "index" # General information about the project. -project = u'sphinxcontrib-phpdomain' -copyright = u'2011, Mark Story' +project = "sphinxcontrib-phpdomain" +copyright = "2011, Mark Story" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. -version = '0.1' +version = "0.1" # The full version, including alpha/beta/rc tags. -release = '0.1' +release = "0.1" # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. -#language = None +# language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: -#today = '' +# today = '' # Else, today_fmt is used as the format for a strftime call. -#today_fmt = '%B %d, %Y' +# today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. -exclude_patterns = ['_build'] +exclude_patterns = ["_build"] # The reST default role (used for this markup: `text`) to use for all documents. -#default_role = None +# default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. -#add_function_parentheses = True +# add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). -#add_module_names = True +# add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. -#show_authors = False +# show_authors = False # The name of the Pygments (syntax highlighting) style to use. -pygments_style = 'sphinx' +pygments_style = "sphinx" # A list of ignored prefixes for module index sorting. -#modindex_common_prefix = [] +# modindex_common_prefix = [] # -- Options for HTML output --------------------------------------------------- # The theme to use for HTML and HTML Help pages. Major themes that come with # Sphinx are currently 'default' and 'sphinxdoc'. -html_theme = 'sphinxdoc' +html_theme = "sphinxdoc" # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. -#html_theme_options = {} +# html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. -#html_theme_path = [] +# html_theme_path = [] # The name for this set of Sphinx documents. If None, it defaults to # " v documentation". -#html_title = None +# html_title = None # A shorter title for the navigation bar. Default is the same as html_title. -#html_short_title = None +# html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. -#html_logo = None +# html_logo = None # The name of an image file (within the static path) to use as favicon of the # docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. -#html_favicon = None +# html_favicon = None # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". -#html_static_path = ['_static'] +# html_static_path = ['_static'] # If not '', a 'Last updated on:' timestamp is inserted at every page bottom, # using the given strftime format. -#html_last_updated_fmt = '%b %d, %Y' +# html_last_updated_fmt = '%b %d, %Y' # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. -#html_use_smartypants = True +# html_use_smartypants = True # Custom sidebar templates, maps document names to template names. -#html_sidebars = {} +# html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. -#html_additional_pages = {} +# html_additional_pages = {} # If false, no module index is generated. -#html_domain_indices = True +# html_domain_indices = True # If false, no index is generated. -#html_use_index = True +# html_use_index = True # If true, the index is split into individual pages for each letter. -#html_split_index = False +# html_split_index = False # If true, links to the reST sources are added to the pages. -#html_show_sourcelink = True +# html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. -#html_show_sphinx = True +# html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. -#html_show_copyright = True +# html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. -#html_use_opensearch = '' +# html_use_opensearch = '' # If nonempty, this is the file name suffix for HTML files (e.g. ".xhtml"). -#html_file_suffix = '' +# html_file_suffix = '' # Output file base name for HTML help builder. -htmlhelp_basename = 'sphinxcontrib-phpdomain' +htmlhelp_basename = "sphinxcontrib-phpdomain" # -- Options for LaTeX output -------------------------------------------------- # The paper size ('letter' or 'a4'). -#latex_paper_size = 'letter' +# latex_paper_size = 'letter' # The font size ('10pt', '11pt' or '12pt'). -#latex_font_size = '10pt' +# latex_font_size = '10pt' # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, author, documentclass [howto/manual]). latex_documents = [ - ('index', 'sphinxcontrib-phpdomain.tex', u'sphinxcontrib-phpdomain Documentation', - u'Mark Story', 'manual'), + ( + "index", + "sphinxcontrib-phpdomain.tex", + "sphinxcontrib-phpdomain Documentation", + "Mark Story", + "manual", + ), ] # The name of an image file (relative to this directory) to place at the top of # the title page. -#latex_logo = None +# latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. -#latex_use_parts = False +# latex_use_parts = False # Additional stuff for the LaTeX preamble. -#latex_preamble = '' +# latex_preamble = '' # Documents to append as an appendix to all manuals. -#latex_appendices = [] +# latex_appendices = [] # If false, no module index is generated. -#latex_domain_indices = True +# latex_domain_indices = True # -- Options for manual page output -------------------------------------------- @@ -192,6 +197,11 @@ # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ - ('index', 'sphinxcontrib-phpdomain', u'sphinxcontrib-phpdomain Documentation', - [u'Mark Story'], 1) + ( + "index", + "sphinxcontrib-phpdomain", + "sphinxcontrib-phpdomain Documentation", + ["Mark Story"], + 1, + ) ] diff --git a/setup.py b/setup.py index 5fa8643c..3a6a72ee 100644 --- a/setup.py +++ b/setup.py @@ -1,11 +1,10 @@ import os from setuptools import setup, find_packages -with open(os.path.join(os.path.dirname(__file__), - 'requirements.txt'), 'r') as f: +with open(os.path.join(os.path.dirname(__file__), "requirements.txt"), "r") as f: requirements = f.read() -long_desc = ''' +long_desc = """ This package provides a Sphinx extension for documenting PHP projects. PHP Domain supports following objects: @@ -26,35 +25,35 @@ * Properties * Enums -''' +""" setup( - name='sphinxcontrib-phpdomain', - version='0.11.1', - url='https://github.com/markstory/sphinxcontrib-phpdomain', - download_url='http://pypi.python.org/pypi/sphinxcontrib-phpdomain', - license='BSD', - author='Mark Story', - author_email='mark@mark-story.com', - description='Sphinx extension to enable documenting PHP code', + name="sphinxcontrib-phpdomain", + version="0.11.1", + url="https://github.com/markstory/sphinxcontrib-phpdomain", + download_url="http://pypi.python.org/pypi/sphinxcontrib-phpdomain", + license="BSD", + author="Mark Story", + author_email="mark@mark-story.com", + description="Sphinx extension to enable documenting PHP code", long_description=long_desc, project_urls={ - 'Documentation': 'https://markstory.github.io/sphinxcontrib-phpdomain/', + "Documentation": "https://markstory.github.io/sphinxcontrib-phpdomain/", }, classifiers=[ - 'Environment :: Console', - 'Environment :: Web Environment', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: BSD License', - 'Operating System :: OS Independent', - 'Programming Language :: Python', - 'Topic :: Documentation', - 'Topic :: Utilities', + "Environment :: Console", + "Environment :: Web Environment", + "Intended Audience :: Developers", + "License :: OSI Approved :: BSD License", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Topic :: Documentation", + "Topic :: Utilities", ], - platforms='any', + platforms="any", dependency_links=[], - namespace_packages=['sphinxcontrib'], - packages=find_packages(exclude=['test*']), + namespace_packages=["sphinxcontrib"], + packages=find_packages(exclude=["test*"]), include_package_data=True, install_requires=requirements, zip_safe=False, diff --git a/sphinxcontrib/__init__.py b/sphinxcontrib/__init__.py index ae0f862b..5d0d71dc 100644 --- a/sphinxcontrib/__init__.py +++ b/sphinxcontrib/__init__.py @@ -9,4 +9,4 @@ :license: BSD, see LICENSE for details. """ -__import__('pkg_resources').declare_namespace(__name__) +__import__("pkg_resources").declare_namespace(__name__) diff --git a/sphinxcontrib/phpdomain.py b/sphinxcontrib/phpdomain.py index 12f8762e..11ccb7ec 100644 --- a/sphinxcontrib/phpdomain.py +++ b/sphinxcontrib/phpdomain.py @@ -20,31 +20,25 @@ from sphinx.util.docfields import Field, GroupedField, TypedField from sphinx import __version__ as sphinx_version -def log_info( - fromdocnode, - message: str -): + +def log_info(fromdocnode, message: str): """ Log informative message. Should have no affect on exit code. """ logger = logging.getLogger(__name__) logger.info(f"[phpdomain.info] {message}", location=fromdocnode) -def log_warning( - fromdocnode, - message: str -): + +def log_warning(fromdocnode, message: str): """ Log warning. Should set exit code to non-zero. """ logger = logging.getLogger(__name__) logger.warning(f"[phpdomain.warning] {message}", location=fromdocnode) -# -def log_assert( - fromdocnode, - value: bool -): + +# +def log_assert(fromdocnode, value: bool): """ Log assertion. Should set exit code to non-zero. """ @@ -53,8 +47,9 @@ def log_assert( logger = logging.getLogger(__name__) logger.warning(f"[phpdomain.assert] line {caller.lineno}", location=fromdocnode) + php_sig_re = re.compile( - r'''^ (public\ |protected\ |private\ )? # visibility + r"""^ (public\ |protected\ |private\ )? # visibility (final\ |abstract\ |static\ )? # modifiers ((?:\\?(?!\d)\w+)\:\:)? # class name (\$?(?:\\?(?!\d)\w+)+) \s* # thing name @@ -64,26 +59,27 @@ def log_assert( )? (?: \s* : \s* (.*))? # backed enum type / case value $ # and nothing more - ''', re.VERBOSE) + """, + re.VERBOSE, +) -NS = '\\' +NS = "\\" separators = { - 'global': None, - 'namespace': None, - 'function': None, - 'interface': None, - 'class': None, - 'trait': None, - 'enum': None, - 'exception': None, - - 'method': '::', - 'const': '::', - 'attr': '::$', - 'staticmethod': '::', - 'case': '::', + "global": None, + "namespace": None, + "function": None, + "interface": None, + "class": None, + "trait": None, + "enum": None, + "exception": None, + "method": "::", + "const": "::", + "attr": "::$", + "staticmethod": "::", + "case": "::", } php_separator = re.compile(r"(\w+)?(?:[:]{2})?") @@ -102,20 +98,20 @@ def _pseudo_parse_arglist(signode, arglist): paramlist = addnodes.desc_parameterlist() stack = [paramlist] try: - for argument in arglist.split(','): + for argument in arglist.split(","): argument = argument.strip() ends_open = ends_close = 0 - while argument.startswith('['): + while argument.startswith("["): stack.append(addnodes.desc_optional()) stack[-2] += stack[-1] argument = argument[1:].strip() - while argument.startswith(']'): + while argument.startswith("]"): stack.pop() argument = argument[1:].strip() - while argument.endswith(']') and not argument.endswith('[]'): + while argument.endswith("]") and not argument.endswith("[]"): ends_close += 1 argument = argument[:-1].strip() - while argument.endswith('['): + while argument.endswith("["): ends_open += 1 argument = argument[:-1].strip() if argument: @@ -141,41 +137,64 @@ def _pseudo_parse_arglist(signode, arglist): def php_rsplit(fullname): items = [item for item in php_separator.findall(fullname)] - return ''.join(items[:-2]), ''.join(items[1:-1]) + return "".join(items[:-2]), "".join(items[1:-1]) class PhpObject(ObjectDescription): """ Description of a general PHP object. """ + option_spec = { - 'noindex': directives.flag, - 'noindexentry': directives.flag, - 'nocontentsentry': directives.flag, - 'module': directives.unchanged, + "noindex": directives.flag, + "noindexentry": directives.flag, + "nocontentsentry": directives.flag, + "module": directives.unchanged, } doc_field_types = [ - TypedField('parameter', label=_('Parameters'), - names=('param', 'parameter', 'arg', 'argument'), - typerolename='obj', typenames=('paramtype', 'type')), - TypedField('variable', label=_('Variables'), rolename='obj', - names=('var', 'ivar', 'cvar'), - typerolename='obj', typenames=('vartype',)), - GroupedField('exceptions', label=_('Throws'), rolename='exc', - names=('throws', 'throw', 'exception', 'except'), - can_collapse=True), - Field('returnvalue', label=_('Returns'), has_arg=False, - names=('returns', 'return')), - Field('returntype', label=_('Return type'), has_arg=False, - names=('rtype', 'returntype'), bodyrolename='obj'), + TypedField( + "parameter", + label=_("Parameters"), + names=("param", "parameter", "arg", "argument"), + typerolename="obj", + typenames=("paramtype", "type"), + ), + TypedField( + "variable", + label=_("Variables"), + rolename="obj", + names=("var", "ivar", "cvar"), + typerolename="obj", + typenames=("vartype",), + ), + GroupedField( + "exceptions", + label=_("Throws"), + rolename="exc", + names=("throws", "throw", "exception", "except"), + can_collapse=True, + ), + Field( + "returnvalue", + label=_("Returns"), + has_arg=False, + names=("returns", "return"), + ), + Field( + "returntype", + label=_("Return type"), + has_arg=False, + names=("rtype", "returntype"), + bodyrolename="obj", + ), ] def get_signature_prefix(self, sig): """ May return a prefix to put before the object name in the signature. """ - return '' + return "" def needs_arglist(self): """ @@ -195,7 +214,7 @@ def handle_signature(self, sig, signode): """ m = php_sig_re.match(sig) if m is None: - log_warning(signode, 'Invalid signature: ' + sig) + log_warning(signode, "Invalid signature: " + sig) raise ValueError visibility, modifiers, name_prefix, name, arglist, retann, enumtype = m.groups() @@ -205,15 +224,16 @@ def handle_signature(self, sig, signode): # determine namespace and class name (if applicable), as well as full name namespace = self.options.get( - 'namespace', self.env.temp_data.get('php:namespace')) + "namespace", self.env.temp_data.get("php:namespace") + ) separator = separators[self.objtype] - if '::' in name_prefix: - classname = name_prefix.rstrip('::') + if "::" in name_prefix: + classname = name_prefix.rstrip("::") else: - classname = self.env.temp_data.get('php:class') + classname = self.env.temp_data.get("php:class") - if self.objtype == 'global': + if self.objtype == "global": namespace = None classname = None fullname = name @@ -222,36 +242,47 @@ def handle_signature(self, sig, signode): fullname = name_prefix + name # Currently in a class, but not creating another class, - elif classname and not self.objtype in ['class', 'exception', 'interface', 'trait', 'enum', 'function']: - if not self.env.temp_data['php:in_class']: + elif classname and not self.objtype in [ + "class", + "exception", + "interface", + "trait", + "enum", + "function", + ]: + if not self.env.temp_data["php:in_class"]: name_prefix = classname + separator fullname = classname + separator + name else: - classname = '' + classname = "" fullname = name - signode['namespace'] = namespace - signode['class'] = self.class_name = classname - signode['fullname'] = fullname + signode["namespace"] = namespace + signode["class"] = self.class_name = classname + signode["fullname"] = fullname if visibility: signode += addnodes.desc_annotation(visibility, visibility) sig_prefix = self.get_signature_prefix(sig) - if modifiers and not (sig_prefix and 'static' in sig_prefix): - signode += addnodes.desc_annotation(modifiers, modifiers) + if modifiers and not (sig_prefix and "static" in sig_prefix): + signode += addnodes.desc_annotation(modifiers, modifiers) if sig_prefix: signode += addnodes.desc_annotation(sig_prefix, sig_prefix) if name_prefix: - if namespace and not self.env.temp_data['php:in_class']: + if namespace and not self.env.temp_data["php:in_class"]: name_prefix = namespace + NS + name_prefix signode += addnodes.desc_addname(name_prefix, name_prefix) - elif namespace and not self.env.temp_data.get('php:in_class', False) and self.env.config.add_module_names: + elif ( + namespace + and not self.env.temp_data.get("php:in_class", False) + and self.env.config.add_module_names + ): nodetext = namespace + NS signode += addnodes.desc_addname(nodetext, nodetext) @@ -275,82 +306,87 @@ def handle_signature(self, sig, signode): return fullname, name_prefix def _object_hierarchy_parts(self, sig_node: addnodes.desc_signature): - if 'fullname' not in sig_node: + if "fullname" not in sig_node: return () - namespace = sig_node.get('namespace') - fullname = sig_node['fullname'] + namespace = sig_node.get("namespace") + fullname = sig_node["fullname"] if isinstance(namespace, str): - return (namespace, *fullname.split('::')) + return (namespace, *fullname.split("::")) else: - return tuple(fullname.split('::')) + return tuple(fullname.split("::")) def _toc_entry_name(self, sig_node: addnodes.desc_signature) -> str: - if not sig_node.get('_toc_parts'): - return '' + if not sig_node.get("_toc_parts"): + return "" config = self.env.app.config - objtype = sig_node.parent.get('objtype') - if config.add_function_parentheses and objtype in {'function', 'method'}: - parens = '()' + objtype = sig_node.parent.get("objtype") + if config.add_function_parentheses and objtype in {"function", "method"}: + parens = "()" else: - parens = '' - *parents, name = sig_node['_toc_parts'] - if config.toc_object_entries_show_parents == 'domain': - return sig_node.get('fullname', name) + parens - if config.toc_object_entries_show_parents == 'hide': + parens = "" + *parents, name = sig_node["_toc_parts"] + if config.toc_object_entries_show_parents == "domain": + return sig_node.get("fullname", name) + parens + if config.toc_object_entries_show_parents == "hide": return name + parens - if config.toc_object_entries_show_parents == 'all': - if objtype in {'method', 'const', 'attr', 'staticmethod', 'case'} and len(parents) > 0: - name = parents.pop() + '::' + name - return '\\'.join(parents + [name + parens]) - return '' + if config.toc_object_entries_show_parents == "all": + if ( + objtype in {"method", "const", "attr", "staticmethod", "case"} + and len(parents) > 0 + ): + name = parents.pop() + "::" + name + return "\\".join(parents + [name + parens]) + return "" def get_index_text(self, namespace, name): """ Return the text for the index entry of the object. """ - raise NotImplementedError('must be implemented in subclasses') + raise NotImplementedError("must be implemented in subclasses") def _is_class_member(self): - return (self.objtype.startswith('method') or - self.objtype.startswith('attr')) + return self.objtype.startswith("method") or self.objtype.startswith("attr") def add_target_and_index(self, name_cls, sig, signode): - if self.objtype == 'global': + if self.objtype == "global": namespace = None else: namespace = self.options.get( - 'namespace', self.env.temp_data.get('php:namespace')) + "namespace", self.env.temp_data.get("php:namespace") + ) if self._is_class_member(): - if signode['class']: - prefix = namespace and namespace + NS or '' + if signode["class"]: + prefix = namespace and namespace + NS or "" else: - prefix = namespace and namespace + NS or '' + prefix = namespace and namespace + NS or "" else: - prefix = namespace and namespace + NS or '' + prefix = namespace and namespace + NS or "" fullname = prefix + name_cls[0] # note target if fullname not in self.state.document.ids: - signode['names'].append(fullname) - signode['ids'].append(fullname) - signode['first'] = (not self.names) + signode["names"].append(fullname) + signode["ids"].append(fullname) + signode["first"] = not self.names self.state.document.note_explicit_target(signode) - objects = self.env.domaindata['php']['objects'] + objects = self.env.domaindata["php"]["objects"] if fullname in objects: self.state_machine.reporter.warning( - 'duplicate object description of %s, ' % fullname + - 'other instance in ' + - self.env.doc2path(objects[fullname][0]), - line=self.lineno) + "duplicate object description of %s, " % fullname + + "other instance in " + + self.env.doc2path(objects[fullname][0]), + line=self.lineno, + ) objects[fullname] = (self.env.docname, self.objtype) - if 'noindexentry' not in self.options: + if "noindexentry" not in self.options: indextext = self.get_index_text(namespace, name_cls) if indextext: - self.indexnode['entries'].append(('single', indextext, - fullname, fullname, None)) + self.indexnode["entries"].append( + ("single", indextext, fullname, fullname, None) + ) class PhpGloballevel(PhpObject): @@ -359,10 +395,10 @@ class PhpGloballevel(PhpObject): """ def get_index_text(self, namespace, name_cls): - if self.objtype == 'global': - return _('%s (global variable)') % name_cls[0] + if self.objtype == "global": + return _("%s (global variable)") % name_cls[0] else: - return '' + return "" class PhpNamespacelevel(PhpObject): @@ -371,30 +407,30 @@ class PhpNamespacelevel(PhpObject): """ def needs_arglist(self): - return self.objtype == 'function' + return self.objtype == "function" def get_signature_prefix(self, sig): """ Adds class prefix for constants created inside classes """ - if self.objtype == 'const': - return _('constant ') - if self.class_name and self.class_name != '': - return self.class_name + '::' + if self.objtype == "const": + return _("constant ") + if self.class_name and self.class_name != "": + return self.class_name + "::" def get_index_text(self, namespace, name_cls): - if self.objtype == 'function': + if self.objtype == "function": if not namespace: - return _('%s() (global function)') % name_cls[0] - return _('%s() (function in %s)') % (name_cls[0], namespace) - elif self.objtype == 'const' and self.class_name != '': - return _('%s (class constant)') % (name_cls[0]) - elif self.objtype == 'const': + return _("%s() (global function)") % name_cls[0] + return _("%s() (function in %s)") % (name_cls[0], namespace) + elif self.objtype == "const" and self.class_name != "": + return _("%s (class constant)") % (name_cls[0]) + elif self.objtype == "const": if not namespace: - return _('%s (global constant)') % (name_cls[0]) - return _('%s (constant in %s)') % (name_cls[0], namespace) + return _("%s (global constant)") % (name_cls[0]) + return _("%s (constant in %s)") % (name_cls[0], namespace) else: - return '' + return "" class PhpClasslike(PhpObject): @@ -404,37 +440,37 @@ class PhpClasslike(PhpObject): """ def get_signature_prefix(self, sig): - return self.objtype + ' ' + return self.objtype + " " def get_index_text(self, namespace, name_cls): - if self.objtype == 'class': + if self.objtype == "class": if not namespace: - return _('%s (class)') % name_cls[0] - return _('%s (class in %s)') % (name_cls[0], namespace) - elif self.objtype == 'interface': + return _("%s (class)") % name_cls[0] + return _("%s (class in %s)") % (name_cls[0], namespace) + elif self.objtype == "interface": if not namespace: - return _('%s (interface)') % name_cls[0] - return _('%s (interface in %s)') % (name_cls[0], namespace) - elif self.objtype == 'trait': + return _("%s (interface)") % name_cls[0] + return _("%s (interface in %s)") % (name_cls[0], namespace) + elif self.objtype == "trait": if not namespace: - return _('%s (trait)') % name_cls[0] - return _('%s (trait in %s)') % (name_cls[0], namespace) - elif self.objtype == 'enum': + return _("%s (trait)") % name_cls[0] + return _("%s (trait in %s)") % (name_cls[0], namespace) + elif self.objtype == "enum": if not namespace: - return _('%s (enum)') % name_cls[0] - return _('%s (enum in %s)') % (name_cls[0], namespace) - elif self.objtype == 'exception': + return _("%s (enum)") % name_cls[0] + return _("%s (enum in %s)") % (name_cls[0], namespace) + elif self.objtype == "exception": return name_cls[0] else: - return '' + return "" def after_content(self): - self.env.temp_data['php:in_class'] = False + self.env.temp_data["php:in_class"] = False def before_content(self): - self.env.temp_data['php:in_class'] = True + self.env.temp_data["php:in_class"] = True if self.names: - self.env.temp_data['php:class'] = self.names[0][0] + self.env.temp_data["php:class"] = self.names[0][0] class PhpClassmember(PhpObject): @@ -443,87 +479,96 @@ class PhpClassmember(PhpObject): """ def get_signature_prefix(self, sig): - if self.objtype == 'attr': - return _('property ') - if self.objtype == 'staticmethod': - return _('static ') - if self.objtype == 'case': - return _('case ') - return '' + if self.objtype == "attr": + return _("property ") + if self.objtype == "staticmethod": + return _("static ") + if self.objtype == "case": + return _("case ") + return "" def needs_arglist(self): - return self.objtype == 'method' + return self.objtype == "method" def get_index_text(self, namespace, name_cls): name, cls = name_cls - if self.objtype.endswith('method') or self.objtype == 'attr' or self.objtype == 'case': + if ( + self.objtype.endswith("method") + or self.objtype == "attr" + or self.objtype == "case" + ): try: clsname, propname = php_rsplit(name) except ValueError: propname = name clsname = None - if self.objtype.endswith('method'): + if self.objtype.endswith("method"): if namespace and clsname is None: - return _('%s() (in namespace %s)') % (name, namespace) + return _("%s() (in namespace %s)") % (name, namespace) elif namespace and self.env.config.add_module_names: - return _('%s() (%s\\%s method)') % (propname, namespace, clsname) + return _("%s() (%s\\%s method)") % (propname, namespace, clsname) else: - return _('%s() (%s method)') % (propname, clsname) - elif self.objtype == 'attr': + return _("%s() (%s method)") % (propname, clsname) + elif self.objtype == "attr": if namespace and clsname is None: - return _('%s (in namespace %s)') % (name, namespace) + return _("%s (in namespace %s)") % (name, namespace) elif namespace and self.env.config.add_module_names: - return _('%s (%s\\%s property)') % (propname, namespace, clsname) + return _("%s (%s\\%s property)") % (propname, namespace, clsname) else: - return _('%s (%s property)') % (propname, clsname) - elif self.objtype == 'case': + return _("%s (%s property)") % (propname, clsname) + elif self.objtype == "case": if namespace and clsname is None: - return _('%s enum case') % (name) + return _("%s enum case") % (name) elif namespace and self.env.config.add_module_names: - return _('%s (%s\\%s enum case)') % (propname, namespace, clsname) + return _("%s (%s\\%s enum case)") % (propname, namespace, clsname) else: - return _('%s (%s enum case)') % (propname, clsname) + return _("%s (%s enum case)") % (propname, clsname) else: - return '' + return "" class PhpNamespace(Directive): """ Directive to start a new PHP namespace, which is similar to module. """ + has_content = False required_arguments = 1 optional_arguments = 0 final_argument_whitespace = False option_spec = { - 'synopsis': lambda x: x, - 'noindex': directives.flag, - 'deprecated': directives.flag, + "synopsis": lambda x: x, + "noindex": directives.flag, + "deprecated": directives.flag, } def run(self): env = self.state.document.settings.env namespace = self.arguments[0].strip() - noindex = 'noindex' in self.options - env.temp_data['php:namespace'] = namespace - env.temp_data['php:class'] = None - env.domaindata['php']['namespaces'][namespace] = ( - env.docname, self.options.get('synopsis', ''), - 'deprecated' in self.options) - - targetnode = nodes.target('', '', ids=['namespace-' + namespace], - ismod=True) + noindex = "noindex" in self.options + env.temp_data["php:namespace"] = namespace + env.temp_data["php:class"] = None + env.domaindata["php"]["namespaces"][namespace] = ( + env.docname, + self.options.get("synopsis", ""), + "deprecated" in self.options, + ) + + targetnode = nodes.target("", "", ids=["namespace-" + namespace], ismod=True) self.state.document.note_explicit_target(targetnode) ret = [targetnode] # the synopsis isn't printed; in fact, it is only used in the # modindex currently if not noindex: - indextext = _('%s (namespace)') % namespace - inode = addnodes.index(entries=[('single', indextext, - 'namespace-' + namespace, namespace, None)]) + indextext = _("%s (namespace)") % namespace + inode = addnodes.index( + entries=[ + ("single", indextext, "namespace-" + namespace, namespace, None) + ] + ) ret.append(inode) return ret @@ -543,10 +588,10 @@ class PhpCurrentNamespace(Directive): def run(self): env = self.state.document.settings.env namespace = self.arguments[0].strip() - if namespace == 'None': - env.temp_data['php:namespace'] = None + if namespace == "None": + env.temp_data["php:namespace"] = None else: - env.temp_data['php:namespace'] = namespace + env.temp_data["php:namespace"] = namespace return [] @@ -554,28 +599,29 @@ class PhpXRefRole(XRefRole): """ Provides cross reference links for PHP objects """ + def process_link(self, env, refnode, has_explicit_title, title, target): if not has_explicit_title: # If the first char is '~' don't display the leading namespace & class. - if target.startswith('~'): # only has a meaning for the title + if target.startswith("~"): # only has a meaning for the title target = title[1:] - if title.startswith('~'): + if title.startswith("~"): title = title[1:] - title = re.sub(r'^[\w\\]+::', '', title) + title = re.sub(r"^[\w\\]+::", "", title) if title.startswith(NS): title = title[1:] - reftype = refnode.attributes['reftype'] - if reftype == 'global': + reftype = refnode.attributes["reftype"] + if reftype == "global": namespace = None classname = None else: - namespace = env.temp_data.get('php:namespace') - classname = env.temp_data.get('php:class') + namespace = env.temp_data.get("php:namespace") + classname = env.temp_data.get("php:class") - refnode['php:namespace'] = namespace - refnode['php:class'] = classname + refnode["php:namespace"] = namespace + refnode["php:class"] = classname return title, target @@ -585,20 +631,21 @@ class PhpNamespaceIndex(Index): Index subclass to provide the PHP namespace index. """ - name = 'modindex' - localname = _('PHP Namespace Index') - shortname = _('namespaces') + name = "modindex" + localname = _("PHP Namespace Index") + shortname = _("namespaces") def generate(self, docnames=None): content = {} # list of prefixes to ignore - ignores = self.domain.env.config['modindex_common_prefix'] + ignores = self.domain.env.config["modindex_common_prefix"] ignores = sorted(ignores, key=len, reverse=True) # list of all namespaces, sorted by name - namespaces = sorted(self.domain.data['namespaces'].items(), - key=lambda x: x[0].lower()) + namespaces = sorted( + self.domain.data["namespaces"].items(), key=lambda x: x[0].lower() + ) # sort out collapsable namespaces - prev_namespace = '' + prev_namespace = "" num_toplevels = 0 for namespace, (docname, synopsis, deprecated) in namespaces: if docnames and docname not in docnames: @@ -606,15 +653,15 @@ def generate(self, docnames=None): for ignore in ignores: if namespace.startswith(ignore): - namespace = namespace[len(ignore):] + namespace = namespace[len(ignore) :] stripped = ignore break else: - stripped = '' + stripped = "" # we stripped the whole namespace name? if not namespace: - namespace, stripped = stripped, '' + namespace, stripped = stripped, "" entries = content.setdefault(namespace[0].lower(), []) @@ -626,16 +673,24 @@ def generate(self, docnames=None): entries[-1][1] = 1 elif not prev_namespace.startswith(package): # subnamespace without parent in list, add dummy entry - entries.append([stripped + package, 1, '', '', '', '', '']) + entries.append([stripped + package, 1, "", "", "", "", ""]) subtype = 2 else: num_toplevels += 1 subtype = 0 - qualifier = deprecated and _('Deprecated') or '' - entries.append([stripped + namespace, subtype, docname, - 'namespace-' + stripped + namespace, '', - qualifier, synopsis]) + qualifier = deprecated and _("Deprecated") or "" + entries.append( + [ + stripped + namespace, + subtype, + docname, + "namespace-" + stripped + namespace, + "", + qualifier, + synopsis, + ] + ) prev_namespace = namespace # apply heuristics when to collapse modindex at page load: @@ -653,130 +708,126 @@ class PhpDomain(Domain): """ PHP language domain. """ - name = 'php' - label = 'PHP' + + name = "php" + label = "PHP" object_types = { - 'function': ObjType(_('function'), 'func', 'obj'), - 'global': ObjType(_('global variable'), 'global', 'obj'), - 'const': ObjType(_('const'), 'const', 'obj'), - 'method': ObjType(_('method'), 'meth', 'obj'), - 'class': ObjType(_('class'), 'class', 'obj'), - 'attr': ObjType(_('attribute'), 'attr', 'obj'), - 'exception': ObjType(_('exception'), 'exc', 'obj'), - 'namespace': ObjType(_('namespace'), 'ns', 'obj'), - 'interface': ObjType(_('interface'), 'interface', 'obj'), - 'trait': ObjType(_('trait'), 'trait', 'obj'), - 'enum': ObjType(_('enum'), 'enum', 'obj'), - 'case': ObjType(_('case'), 'case', 'obj'), + "function": ObjType(_("function"), "func", "obj"), + "global": ObjType(_("global variable"), "global", "obj"), + "const": ObjType(_("const"), "const", "obj"), + "method": ObjType(_("method"), "meth", "obj"), + "class": ObjType(_("class"), "class", "obj"), + "attr": ObjType(_("attribute"), "attr", "obj"), + "exception": ObjType(_("exception"), "exc", "obj"), + "namespace": ObjType(_("namespace"), "ns", "obj"), + "interface": ObjType(_("interface"), "interface", "obj"), + "trait": ObjType(_("trait"), "trait", "obj"), + "enum": ObjType(_("enum"), "enum", "obj"), + "case": ObjType(_("case"), "case", "obj"), } directives = { - 'function': PhpNamespacelevel, - 'global': PhpGloballevel, - 'const': PhpNamespacelevel, - 'class': PhpClasslike, - 'method': PhpClassmember, - 'staticmethod': PhpClassmember, - 'attr': PhpClassmember, - 'case': PhpClassmember, - 'exception': PhpClasslike, - 'interface': PhpClasslike, - 'trait': PhpClasslike, - 'enum': PhpClasslike, - 'namespace': PhpNamespace, - 'currentmodule': PhpCurrentNamespace, - 'currentnamespace': PhpCurrentNamespace, + "function": PhpNamespacelevel, + "global": PhpGloballevel, + "const": PhpNamespacelevel, + "class": PhpClasslike, + "method": PhpClassmember, + "staticmethod": PhpClassmember, + "attr": PhpClassmember, + "case": PhpClassmember, + "exception": PhpClasslike, + "interface": PhpClasslike, + "trait": PhpClasslike, + "enum": PhpClasslike, + "namespace": PhpNamespace, + "currentmodule": PhpCurrentNamespace, + "currentnamespace": PhpCurrentNamespace, } roles = { - 'func': PhpXRefRole(fix_parens=False), - 'global': PhpXRefRole(), - 'class': PhpXRefRole(), - 'exc': PhpXRefRole(), - 'meth': PhpXRefRole(fix_parens=False), - 'attr': PhpXRefRole(), - 'const': PhpXRefRole(), - 'ns': PhpXRefRole(), - 'obj': PhpXRefRole(), - 'interface': PhpXRefRole(), - 'trait': PhpXRefRole(), - 'enum': PhpXRefRole(), - 'case': PhpXRefRole(), + "func": PhpXRefRole(fix_parens=False), + "global": PhpXRefRole(), + "class": PhpXRefRole(), + "exc": PhpXRefRole(), + "meth": PhpXRefRole(fix_parens=False), + "attr": PhpXRefRole(), + "const": PhpXRefRole(), + "ns": PhpXRefRole(), + "obj": PhpXRefRole(), + "interface": PhpXRefRole(), + "trait": PhpXRefRole(), + "enum": PhpXRefRole(), + "case": PhpXRefRole(), } initial_data = { - 'objects': {}, # fullname -> docname, objtype - 'namespaces': {}, # namespace -> docname, synopsis + "objects": {}, # fullname -> docname, objtype + "namespaces": {}, # namespace -> docname, synopsis } indices = [ PhpNamespaceIndex, ] def clear_doc(self, docname): - for fullname, (fn, _l) in list(self.data['objects'].items()): + for fullname, (fn, _l) in list(self.data["objects"].items()): if fn == docname: - del self.data['objects'][fullname] - for ns, (fn, _x, _x) in list(self.data['namespaces'].items()): + del self.data["objects"][fullname] + for ns, (fn, _x, _x) in list(self.data["namespaces"].items()): if fn == docname: - del self.data['namespaces'][ns] + del self.data["namespaces"][ns] def merge_domaindata(self, docnames, otherdata): - for fullname, (fn, objtype) in otherdata['objects'].items(): + for fullname, (fn, objtype) in otherdata["objects"].items(): if fn in docnames: - self.data['objects'][fullname] = (fn, objtype) - for namespace, data in otherdata['namespaces'].items(): + self.data["objects"][fullname] = (fn, objtype) + for namespace, data in otherdata["namespaces"].items(): if data[0] in docnames: - self.data['namespaces'][namespace] = data + self.data["namespaces"][namespace] = data - def resolve_any_xref(self, env, fromdocname, builder, - target, node, contnode): + def resolve_any_xref(self, env, fromdocname, builder, target, node, contnode): for typ in self.roles: - resolve = self.resolve_xref(env, fromdocname, builder, - typ, target, node, contnode) + resolve = self.resolve_xref( + env, fromdocname, builder, typ, target, node, contnode + ) if resolve: - return [('php:%s' % typ, resolve)] + return [("php:%s" % typ, resolve)] return [] - def resolve_xref(self, env, fromdocname, builder, - typ, target, node, contnode): - if (typ == 'ns' or - typ == 'obj' and target in self.data['namespaces']): - docname, synopsis, deprecated = self.data['namespaces'].get( - target, - ('', '', '') + def resolve_xref(self, env, fromdocname, builder, typ, target, node, contnode): + if typ == "ns" or typ == "obj" and target in self.data["namespaces"]: + docname, synopsis, deprecated = self.data["namespaces"].get( + target, ("", "", "") ) if not docname: return None else: - title = '%s%s' % (synopsis, - (deprecated and ' (deprecated)' or '')) + title = "%s%s" % (synopsis, (deprecated and " (deprecated)" or "")) return make_refnode( builder, fromdocname, docname, - 'namespace-' + target, + "namespace-" + target, contnode, - title) + title, + ) else: - namespace = node.get('php:namespace') - clsname = node.get('php:class') - name, obj = self.find_obj(env, node, namespace, clsname, - target, typ) + namespace = node.get("php:namespace") + clsname = node.get("php:class") + name, obj = self.find_obj(env, node, namespace, clsname, target, typ) if not obj: return None else: - return make_refnode(builder, fromdocname, obj[0], name, - contnode, name) + return make_refnode(builder, fromdocname, obj[0], name, contnode, name) def find_obj(self, env, fromdocnode, namespace, classname, name, type): """ Find a PHP object for "name", using the given namespace and classname. """ # strip parenthesis - if name[-2:] == '()': + if name[-2:] == "()": name = name[:-2] - objects = self.data['objects'] + objects = self.data["objects"] if name.startswith(NS): absname = name[1:] @@ -786,32 +837,62 @@ def find_obj(self, env, fromdocnode, namespace, classname, name, type): if absname not in objects and name in objects: # constants/functions can be namespaced, but allow fallback to global namespace the same way as PHP does name_type = objects[name][1] - if (name_type == 'function' or name_type == 'const') and NS not in name and '::' not in name: + if ( + (name_type == "function" or name_type == "const") + and NS not in name + and "::" not in name + ): absname = name else: if namespace and name.startswith(namespace + NS): - log_info(fromdocnode, f"Target {absname} not found - did you mean {name[len(namespace + NS):]}?") + log_info( + fromdocnode, + f"Target {absname} not found - did you mean {name[len(namespace + NS):]}?", + ) else: - log_info(fromdocnode, f"Target {absname} not found - did you mean {NS + name}?") - absname = name # fallback for BC, might be removed in the next major release + log_info( + fromdocnode, + f"Target {absname} not found - did you mean {NS + name}?", + ) + absname = name # fallback for BC, might be removed in the next major release if absname in objects: return absname, objects[absname] # PHP reserved words are never resolved using NS and ignore them when not defined - if name not in ['array', 'bool', 'callable', 'false', 'float', 'int', 'iterable', 'mixed', 'never', 'null', 'numeric', 'object', 'parent', 'resource', 'self', 'static', 'string', 'true', 'void']: + if name not in [ + "array", + "bool", + "callable", + "false", + "float", + "int", + "iterable", + "mixed", + "never", + "null", + "numeric", + "object", + "parent", + "resource", + "self", + "static", + "string", + "true", + "void", + ]: log_info(fromdocnode, f"Target {absname} not found") return None, None def get_objects(self): - for ns, info in self.data['namespaces'].items(): - yield (ns, ns, 'namespace', info[0], 'namespace-' + ns, 0) - for refname, (docname, type) in self.data['objects'].items(): + for ns, info in self.data["namespaces"].items(): + yield (ns, ns, "namespace", info[0], "namespace-" + ns, 0) + for refname, (docname, type) in self.data["objects"].items(): yield (refname, refname, type, docname, refname, 1) def setup(app): app.add_domain(PhpDomain) - return {'version': sphinx_version, 'parallel_read_safe': True} + return {"version": sphinx_version, "parallel_read_safe": True} diff --git a/test/conf.py b/test/conf.py index 2487b293..2fe1b267 100644 --- a/test/conf.py +++ b/test/conf.py @@ -1,17 +1,17 @@ import sys, os -sys.path.append(os.path.abspath('..')) +sys.path.append(os.path.abspath("..")) extensions = [ - 'sphinxcontrib.phpdomain', - 'myst_parser', + "sphinxcontrib.phpdomain", + "myst_parser", ] -myst_enable_extensions = ['colon_fence'] +myst_enable_extensions = ["colon_fence"] -source_suffix = '.rst' -master_doc = 'index' +source_suffix = ".rst" +master_doc = "index" -exclude_patterns = ['_build'] +exclude_patterns = ["_build"] -html_theme = 'default' +html_theme = "default" From d011cc54f6426483269b437f52c7eb5c61d8bff4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Thu, 3 Aug 2023 10:00:48 +0200 Subject: [PATCH 19/23] "numeric" type is not reserved keyword --- sphinxcontrib/phpdomain.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/sphinxcontrib/phpdomain.py b/sphinxcontrib/phpdomain.py index 11ccb7ec..692235fd 100644 --- a/sphinxcontrib/phpdomain.py +++ b/sphinxcontrib/phpdomain.py @@ -859,7 +859,7 @@ def find_obj(self, env, fromdocnode, namespace, classname, name, type): if absname in objects: return absname, objects[absname] - # PHP reserved words are never resolved using NS and ignore them when not defined + # PHP reserved keywords are never resolved using NS and ignore them when not defined if name not in [ "array", "bool", @@ -871,7 +871,6 @@ def find_obj(self, env, fromdocnode, namespace, classname, name, type): "mixed", "never", "null", - "numeric", "object", "parent", "resource", From db899a0743dd428e55469e054ce94852d27ad553 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Thu, 3 Aug 2023 12:21:04 +0200 Subject: [PATCH 20/23] add mvorisek as coauthor --- README.rst | 1 + doc/conf.py | 6 +++--- setup.py | 2 +- sphinxcontrib/phpdomain.py | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/README.rst b/README.rst index c197aa99..9407f786 100644 --- a/README.rst +++ b/README.rst @@ -2,6 +2,7 @@ PHP Domain for Sphinx ##################### :author: Mark Story +:author: Michael Voříšek About ===== diff --git a/doc/conf.py b/doc/conf.py index 95da0d1e..21e646aa 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -28,7 +28,7 @@ # General information about the project. project = "sphinxcontrib-phpdomain" -copyright = "2011, Mark Story" +copyright = "2011 - 2023, Mark Story and Michael Voříšek" # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the @@ -169,7 +169,7 @@ "index", "sphinxcontrib-phpdomain.tex", "sphinxcontrib-phpdomain Documentation", - "Mark Story", + "Mark Story and Michael Voříšek", "manual", ), ] @@ -201,7 +201,7 @@ "index", "sphinxcontrib-phpdomain", "sphinxcontrib-phpdomain Documentation", - ["Mark Story"], + ["Mark Story and Michael Voříšek"], 1, ) ] diff --git a/setup.py b/setup.py index 3a6a72ee..5c193f9f 100644 --- a/setup.py +++ b/setup.py @@ -33,7 +33,7 @@ url="https://github.com/markstory/sphinxcontrib-phpdomain", download_url="http://pypi.python.org/pypi/sphinxcontrib-phpdomain", license="BSD", - author="Mark Story", + author="Mark Story and Michael Voříšek", author_email="mark@mark-story.com", description="Sphinx extension to enable documenting PHP code", long_description=long_desc, diff --git a/sphinxcontrib/phpdomain.py b/sphinxcontrib/phpdomain.py index 692235fd..3b6caa54 100644 --- a/sphinxcontrib/phpdomain.py +++ b/sphinxcontrib/phpdomain.py @@ -1,7 +1,7 @@ """ Sphinx PHP domain. - :copyright: Copyright 2023 by Mark Story + :copyright: Copyright 2011 - 2023 by Mark Story and Michael Voříšek :license: BSD, see LICENSE for details. """ import re From 4282e3764b40fce840553e4c16e8f67de9342f01 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Thu, 3 Aug 2023 16:00:10 +0200 Subject: [PATCH 21/23] test warnings and non-zero exit code --- .github/workflows/ci.yml | 17 +++++++++-- sphinxcontrib/phpdomain.py | 47 ++++++++++++++--------------- test/conf.py | 4 +-- test/log.html | 61 ++++++++++++++++++++++++++++++++++++++ test/log.md | 25 ++++++++++++++++ test/log.txt | 20 ++++++++----- 6 files changed, 139 insertions(+), 35 deletions(-) create mode 100644 test/log.html create mode 100644 test/log.md diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 7f023d9f..5a55bbbd 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -40,10 +40,14 @@ jobs: run: | cd test find . -name '*.html' -exec rm {} \; - make html SPHINXOPTS='-W' | tee log.txt + + sed -i 's~, "log\.md"~~' conf.py + make html SPHINXOPTS='' 2>&1 | tee log.txt + git restore conf.py + (cd _build/html && rm genindex.html index.html search.html php-modindex.html) (cd _build/html && find . -name '*.html' -exec sh -c 'xmllint {} --xpath '"'"'//div[@role="main"]'"'"' | xmllint --format - > ../../{}' \;) - sed -i -r 's~.*/(test/.* \[phpdomain)~\1~;t;d' log.txt + sed -i -r 's~.*/(test/)~\1~;t;d' log.txt - name: Apply Coding Style if: matrix.python == '3.11' @@ -69,6 +73,15 @@ jobs: commit_user_email: bot@example.com commit_author: Bot + - name: Build Unit Tests with '-W' option + run: | + cd test + make html SPHINXOPTS='-W' + + sed -i 's~, "log\.md"~~' conf.py + ! make html SPHINXOPTS='-W' || (echo 'Unexpected zero exit code'; false) + git restore conf.py + - name: Build Unit Tests with toc show_parents=hide run: | cd test diff --git a/sphinxcontrib/phpdomain.py b/sphinxcontrib/phpdomain.py index 3b6caa54..3adcb537 100644 --- a/sphinxcontrib/phpdomain.py +++ b/sphinxcontrib/phpdomain.py @@ -23,10 +23,10 @@ def log_info(fromdocnode, message: str): """ - Log informative message. Should have no affect on exit code. + Log informative message. Should have no effect on exit code. """ logger = logging.getLogger(__name__) - logger.info(f"[phpdomain.info] {message}", location=fromdocnode) + logger.info(f"[phpdomain] {message}", location=fromdocnode) def log_warning(fromdocnode, message: str): @@ -34,32 +34,32 @@ def log_warning(fromdocnode, message: str): Log warning. Should set exit code to non-zero. """ logger = logging.getLogger(__name__) - logger.warning(f"[phpdomain.warning] {message}", location=fromdocnode) + logger.warning(f"[phpdomain] {message}", location=fromdocnode, type="phpdomain") -# -def log_assert(fromdocnode, value: bool): +def throw_if_false(fromdocnode, value, message: str): """ - Log assertion. Should set exit code to non-zero. + Log warning if the value is not true and throw ValueError. Should set exit code to non-zero. """ if not value: - caller = inspect.getframeinfo(inspect.stack()[1][0]) - logger = logging.getLogger(__name__) - logger.warning(f"[phpdomain.assert] line {caller.lineno}", location=fromdocnode) + log_warning(fromdocnode, message) + raise ValueError php_sig_re = re.compile( - r"""^ (public\ |protected\ |private\ )? # visibility - (final\ |abstract\ |static\ )? # modifiers - ((?:\\?(?!\d)\w+)\:\:)? # class name - (\$?(?:\\?(?!\d)\w+)+) \s* # thing name - (?: - \((.*)\) # optional: arguments - (?: \s* -> \s* (.*))? # return annotation - )? - (?: \s* : \s* (.*))? # backed enum type / case value - $ # and nothing more - """, + r""" + ^ + (public\ |protected\ |private\ )? # visibility + (final\ |abstract\ |static\ )? # modifiers + ((?:\\?(?!\d)\w+)\:\:)? # class name + (\$?(?:\\?(?!\d)\w+)+) \s* # thing name + (?: + \((.*)\) # optional: arguments + (?: \s* -> \s* (.*))? # return annotation + )? + (?: \s* : \s* (.*))? # backed enum type / case value + $ # and nothing more + """, re.VERBOSE, ) @@ -214,8 +214,7 @@ def handle_signature(self, sig, signode): """ m = php_sig_re.match(sig) if m is None: - log_warning(signode, "Invalid signature: " + sig) - raise ValueError + throw_if_false(signode, False, "Invalid signature") visibility, modifiers, name_prefix, name, arglist, retann, enumtype = m.groups() @@ -847,12 +846,12 @@ def find_obj(self, env, fromdocnode, namespace, classname, name, type): if namespace and name.startswith(namespace + NS): log_info( fromdocnode, - f"Target {absname} not found - did you mean {name[len(namespace + NS):]}?", + f"Target {absname} not found - did you mean to write {name[len(namespace + NS):]}?", ) else: log_info( fromdocnode, - f"Target {absname} not found - did you mean {NS + name}?", + f"Target {absname} not found - did you mean to write {NS + name}?", ) absname = name # fallback for BC, might be removed in the next major release diff --git a/test/conf.py b/test/conf.py index 2fe1b267..2be6ab33 100644 --- a/test/conf.py +++ b/test/conf.py @@ -9,9 +9,9 @@ myst_enable_extensions = ["colon_fence"] -source_suffix = ".rst" +source_suffix = ".md" master_doc = "index" -exclude_patterns = ["_build"] +exclude_patterns = ["_build", "log.md"] html_theme = "default" diff --git a/test/log.html b/test/log.html new file mode 100644 index 00000000..b6a2cb37 --- /dev/null +++ b/test/log.html @@ -0,0 +1,61 @@ + +
+
+

Invalid domain type

+
+
+

Invalid signature

+
+
+ + x(); + +
+
+
+
+
+

Unresolved references

+
    +
  • +

    + + Foo\Aa + +

    +
  • +
  • +

    + + Foo\A::simplifyy + +

    +
  • +
+ + + + +
+
+
diff --git a/test/log.md b/test/log.md new file mode 100644 index 00000000..fd119717 --- /dev/null +++ b/test/log.md @@ -0,0 +1,25 @@ +# Invalid domain type + +:::{php:namespacee} Foo +::: + +# Invalid signature + +:::{php:method} x(); +::: + +# Unresolved references + +- {php:class}`Foo\Aa` + +- {php:meth}`Foo\A::simplifyy` + +:::{php:namespace} Foo +::: + +- {php:meth}`Foo\A::simplify` + +:::{php:namespace} Fooo +::: + +- {php:meth}`Foo\A::simplify` diff --git a/test/log.txt b/test/log.txt index 2fc037f9..12ca82b9 100644 --- a/test/log.txt +++ b/test/log.txt @@ -1,7 +1,13 @@ -test/ns.md:48: [phpdomain.info] Target A2::simplify not found -test/ns.md:53: [phpdomain.info] Target Bar2\A::simplify not found -test/rst_doc.md:506: [phpdomain.info] Target OtherLibrary\int|string|ReturnedClass|\LibraryName\SubPackage\SubpackageInterface|null not found -test/rst_doc2.md:11: [phpdomain.info] Target Imagine\Image\ImageInterface::draw not found -test/rst_doc2.md:17: [phpdomain.info] Target Imagine\Image\PointInterface not found -test/rst_doc2.md:17: [phpdomain.info] Target Imagine\Image\BoxInterface not found -test/rst_doc2.md:17: [phpdomain.info] Target Imagine\Image\Color not found +test/log.md:3: WARNING: Unknown directive type: 'php:namespacee' [myst.directive_unknown] +test/log.md:8: WARNING: [phpdomain] Invalid signature +test/log.md:13: [phpdomain] Target Foo\Aa not found +test/log.md:15: [phpdomain] Target Foo\A::simplifyy not found +test/log.md:20: [phpdomain] Target Foo\Foo\A::simplify not found - did you mean to write A::simplify? +test/log.md:25: [phpdomain] Target Fooo\Foo\A::simplify not found - did you mean to write \Foo\A::simplify? +test/ns.md:48: [phpdomain] Target A2::simplify not found +test/ns.md:53: [phpdomain] Target Bar2\A::simplify not found +test/rst_doc.md:506: [phpdomain] Target OtherLibrary\int|string|ReturnedClass|\LibraryName\SubPackage\SubpackageInterface|null not found +test/rst_doc2.md:11: [phpdomain] Target Imagine\Image\ImageInterface::draw not found +test/rst_doc2.md:17: [phpdomain] Target Imagine\Image\PointInterface not found +test/rst_doc2.md:17: [phpdomain] Target Imagine\Image\BoxInterface not found +test/rst_doc2.md:17: [phpdomain] Target Imagine\Image\Color not found From 927431eb8779490da9e436cdbdd6e04da71fa78a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Thu, 3 Aug 2023 18:05:05 +0200 Subject: [PATCH 22/23] fix ref type mismatch --- test/rst_doc.html | 28 ++++++++++++++-------------- test/rst_doc.md | 28 ++++++++++++++-------------- 2 files changed, 28 insertions(+), 28 deletions(-) diff --git a/test/rst_doc.html b/test/rst_doc.html index 24e4e5c5..e6dfa1af 100644 --- a/test/rst_doc.html +++ b/test/rst_doc.html @@ -470,42 +470,42 @@

Test Case - Global symbols with no namespaces - + DateTime::setTime()

- + DateTime::getLastErrors()

- + DateTime::setDate()

- + DateTime::ATOM

- + DateTime::$testattr

- + OtherClass::update @@ -547,35 +547,35 @@

Test Case - Global symbols with no namespaces - + DateTimeInterface::setTime()

- + setDate()

- + DateTimeInterface::ATOM

- + DateTimeInterface::$testattr

- + OtherInterface @@ -589,7 +589,7 @@

Test Case - Global symbols with no namespaces - + LogTrait::log() @@ -600,7 +600,7 @@

Test Case - Prefix less links - + setDate() @@ -1137,7 +1137,7 @@

Test Case - not including namespace - + TemplateTrait::render() diff --git a/test/rst_doc.md b/test/rst_doc.md index 1c3e9660..d29c7c8d 100644 --- a/test/rst_doc.md +++ b/test/rst_doc.md @@ -163,17 +163,17 @@ Test Case - Global symbols with no namespaces :php:class:`DateTime` -:php:func:`DateTime::setTime()` +:php:meth:`DateTime::setTime()` -:php:func:`DateTime::getLastErrors()` +:php:meth:`DateTime::getLastErrors()` -:php:func:`DateTime::setDate()` +:php:meth:`DateTime::setDate()` -:php:func:`DateTime::ATOM` +:php:const:`DateTime::ATOM` -:php:func:`DateTime::$testattr` +:php:attr:`DateTime::$testattr` -:php:func:`OtherClass::update` +:php:meth:`OtherClass::update` :php:attr:`OtherClass::$nonIndentedAttribute` @@ -185,26 +185,26 @@ Test Case - Global symbols with no namespaces :php:interface:`DateTimeInterface` -:php:func:`DateTimeInterface::setTime()` +:php:meth:`DateTimeInterface::setTime()` -:php:func:`~DateTimeInterface::setDate()` +:php:meth:`~DateTimeInterface::setDate()` -:php:func:`DateTimeInterface::ATOM` +:php:const:`DateTimeInterface::ATOM` -:php:func:`DateTimeInterface::$testattr` +:php:attr:`DateTimeInterface::$testattr` -:php:func:`OtherInterface` +:php:interface:`OtherInterface` :php:trait:`LogTrait` -:php:func:`LogTrait::log()` +:php:meth:`LogTrait::log()` Test Case - Prefix less links ----------------------------- The following links should not be prefixed with a classname. -:php:func:`~DateTime::setDate()` +:php:meth:`~DateTime::setDate()` :php:attr:`~DateTime::$testattr` @@ -369,7 +369,7 @@ Within a namespace context you don't need to include the namespace in links. :php:trait:`TemplateTrait` -:php:func:`TemplateTrait::render()` +:php:meth:`TemplateTrait::render()` Test Case - Links with prefix trimming -------------------------------------- From 5a21b2a4cfc6aac2b2263c0fe34b4208d1185c34 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Vo=C5=99=C3=AD=C5=A1ek?= Date: Fri, 4 Aug 2023 16:04:42 +0200 Subject: [PATCH 23/23] fix .gitignore --- .gitignore | 1 - 1 file changed, 1 deletion(-) diff --git a/.gitignore b/.gitignore index f9909d52..c28a058b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,6 @@ dist/ build/ doc/_build -test/must-build/_build test/_build *.pyc *.egg-info