docs: Complete documentation site, add autodoc extensions, and MystLexer#7
Merged
Conversation
…g reference, and API why: The extensions/ hub had 3 dead-end cards (sphinx-fonts, sphinx-gptheme, sphinx-argparse-neo linked to nothing), no configuration reference for merge_sphinx_config(), and no API docs. what: - Rename extensions/ to packages/ (gp-sphinx and sphinx-gptheme aren't extensions; packages/ matches the UV workspace vocabulary) - Add package pages for gp-sphinx, sphinx-fonts, sphinx-gptheme, and sphinx-argparse-neo with badges, install commands, usage examples, and source links - Add configuration.md: full parameter table for merge_sphinx_config(), auto-computed values, hardcoded defaults, shared defaults by category (extensions, theme, fonts, MyST, autodoc, copybutton, napoleon) - Add api.md: autodoc for merge_sphinx_config, make_linkcode_resolve, deep_merge - Update landing page: add Packages and Configuration cards - Add redirect entries for old extensions/ URLs
…e_neo.exemplar why: argparse_exemplar is not importable as a top-level module — downstream users copying the quickstart example get ModuleNotFoundError. The correct Sphinx extension name is sphinx_argparse_neo.exemplar. what: - Fix docs/quickstart.md extra_extensions example - Fix config.py docstring example
… warning why: autodoc interprets **overrides as malformed bold RST markup when rendering api.md, producing parse warnings under -W. what: - Escape as \**overrides in the parameter list and prose - Use raw docstring (r""") to support the backslash escapes
…ge demo why: The page was a badge gallery with no install, config, directive, or role documentation. All 4 config values, 4 directives, 1 role, and 11 py:fixture options were undocumented. what: - Add maturity badge, install commands, usage example - Document all 4 registered config values with defaults - Document all directives (py:fixture, autofixture, autofixtures, autofixture-index) and the :fixture: role - Document all py:fixture directive options - Add source link - Badge demo section preserved below reference content
…ons, lexers, and roles why: The page documented 0/4 base config values, 0/9 exemplar config values, 0/7 directive options, 0/4 lexers, and 0/5 CLI roles. what: - Document 4 base config values with defaults - Document key argparse directive options - Add exemplar sub-extension section with correct extension name (sphinx_argparse_neo.exemplar, not argparse_exemplar) - Document 9 exemplar config values, 4 Pygments lexers, 5 CLI roles - Clarify lexers and roles are registered by the exemplar, not the base
why: The page listed bundled files but documented 0/7 theme.conf options, no copyable html_theme_options example, and no template/stylesheet detail. what: - Document all 7 theme.conf options with descriptions - Add copyable html_theme_options example - Document templates (brand, projects), stylesheets (custom, argparse), and JavaScript (spa-nav.js) - Document theme inheritance and entry point
…xt variables why: Config table documented names but omitted default values from gp-sphinx and the template integration contract that downstream theme authors need. what: - Add gp-sphinx default values for all 4 config options - Document the FontConfig dict shape - Document 4 template context variables injected during html-page-context (font_faces, font_preload_hrefs, font_fallbacks, font_css_variables)
…_options why: The page summarized defaults by behavior but never named the actual constants or their values — downstream authors couldn't verify coverage against the source. what: - Add DEFAULT_AUTODOC_OPTIONS dict (5 keys) - Add DEFAULT_SOURCE_SUFFIX, DEFAULT_HTML_STATIC_PATH, DEFAULT_TEMPLATES_PATH - Add copybutton constant names and values - Add napoleon and suppress_warnings constant names
why: README.md was 0 bytes — PyPI and editor-side DX showed no package description. what: - Add description, install command, usage example, and docs link
why: The argparse demo used "gp-demo" with "build"/"serve" subcommands, which didn't look like obvious examples. Epilog text also rendered as unformatted paragraphs due to a nested_parse limitation. what: - Rewrite demo_cli.py with generic names (myapp, mysubcommand, myothersubcommand) and remove epilog that triggered rendering bug - Fix RUF012 in docutils_demo.py: add ClassVar to option_spec - Fix E501 in sphinx_config_demo.py, sphinx_config_single_demo.py: shorten doctest stub construction
…nx, and package-reference directive why: Enable auto-generated reference tables for directives, roles, and config values across all package docs pages, replacing static Markdown tables with live introspection. what: - Add sphinx-autodoc-docutils package with autodirective, autorole, autodirective-index, autorole-index directives - Add sphinx-autodoc-sphinx package with autoconfigvalue, autoconfigvalues, autoconfigvalue-index directives - Add package_reference.py docs extension (auto-generates registered surface tables from setup() introspection) - Rewrite all package docs pages to use live autodoc directives - Add argparse_neo_demo.py with subcommand and epilog examples - Register new extensions in conf.py with env-gated intersphinx - Add workspace packages to pyproject.toml, ruff, and mypy config - Add tests for autodoc-docutils, autodoc-sphinx, and package-reference - Add CSS for package demo cards and font specimens - Add redirects for new package doc pages
why: Base class Directive expects dict[str, Callable] | None, not dict[str, object]. what: - Change option_spec annotation from dict[str, object] to dict[str, t.Any] in AutoconfigvalueDirective and AutoconfigvaluesDirective
…with attribute access why: Sphinx 8.x changed config values from 3-tuples to _Opt instances. The t.cast + tuple unpacking emits 4 RemovedInSphinx90Warning per call and will crash in Sphinx 9. what: - Use isinstance(opt, tuple) guard for backward compat with old Sphinx - Access .default, .rebuild, .valid_types attributes on _Opt instances - Remove unused import typing (cleaned by ruff)
…) functions why: ExtensionMetadata is a public TypedDict from sphinx.util.typing (Sphinx 7.3+) that catches typos in metadata keys at type-check time. what: - Add TYPE_CHECKING import of ExtensionMetadata in both packages - Change setup() return annotation from dict[str, t.Any] to ExtensionMetadata
…rom sphinx.util.typing why: OptionSpec (dict[str, Callable[[str], Any]]) is Sphinx's public type alias for directive option specs, available since Sphinx 4.0. what: - Add TYPE_CHECKING import of OptionSpec in both packages - Annotate all option_spec class attributes with ClassVar[OptionSpec] - Add import typing as t to docutils _directives.py (was missing)
…nSpec | None why: The parameter receives Directive.option_spec values which are dict[str, Callable[[str], Any]] | None per types-docutils stubs. what: - Change _option_rows(option_spec: object) to OptionSpec | None - isinstance guard on line 116 still narrows correctly for mypy
…ations why: Both packages ship py.typed with strict mypy. Reviewers and contributors need to know why object is used instead of Any or a narrower type in 14+ locations. what: - Add brief inline comments on every intentional object annotation explaining why it is the correct type (e.g., wraps inspect.getdoc, config defaults are heterogeneous, roles have monkey-patched attrs) - Comments follow pattern: # object: <reason>
… config extractor why: _config_values_from_calls() required 3+ positional args, silently dropping extensions that use keyword args (e.g., sphinx-autodoc-pytest- fixtures passes default=, rebuild=, types= as kwargs). what: - Lower guard from len(args) < 3 to len(args) < 1 - Read default/rebuild/types/description from kwargs with positional fallback - Add kwargs-style doctest to verify extraction
…face extractor why: Same bug as sphinx-autodoc-sphinx — len(args) < 2 guard dropped kwargs-style calls where default/rebuild are keyword arguments. what: - Lower guard from len(args) < 2 to len(args) < 1 - Read default from kwargs with positional fallback
why: The quickstart stopped at the conf.py snippet with no build command, directory creation, or verification step. A new user could not go from zero to rendered docs. what: - Add mkdir, index.md creation, sphinx-build command, and browser step - Reference the Usage section for conf.py pattern
why: The install command used the old single-package form that doesn't resolve workspace members. what: - Change uv sync --all-extras --dev to uv sync --all-packages --all-extras --group dev
…nd :exclude: options why: The only filtering and sorting mechanisms for bulk fixture documentation were invisible to users. what: - Add autofixtures option table (:order:, :exclude:) - Add autofixture-index option table (:exclude:)
why: The autofunction block showed the signature but not how to wire the resolver into merge_sphinx_config() via **overrides. what: - Add conf.py example showing linkcode_resolve= usage - Note that sphinx.ext.linkcode is auto-appended to extensions
why: sphinx-fonts registers no directives or roles — only config values and template context hooks. The empty heading was confusing. what: - Remove the "Directives and Roles" heading and its empty autodirective-index/autorole-index blocks
why: Pages demoed index + single-object directives but skipped bulk variants (autodirectives, autoroles, autoconfigvalues) — the forms most users reach for first. what: - Add autodirectives and autoroles bulk demos to sphinx-autodoc-docutils - Add autoconfigvalues bulk demo to sphinx-autodoc-sphinx - Pass :no-index: through to rst:directive:option:: to avoid duplicate description warnings in bulk rendering
why: Sphinx 8.2.3+ accepts description= as structured metadata for config values. Enriches self-documentation and future autodoc rendering. what: - Add description= to all 21 add_config_value() calls across sphinx-fonts (4), argparse-neo base (4), argparse-neo exemplar (9), and sphinx-autodoc-pytest-fixtures (4) - Fix sphinx-fonts test mocks to accept **kwargs
why: Repository default branch is main, not master; links 404 otherwise. what: - Update 7 docs/packages/*.md source links - Update hardcoded fallback URL in docs/_ext/package_reference.py
…dule docstring
why: New contributors had no entry point for understanding how the
auto-generation pipeline works or how to extend it.
what:
- Describe the three-layer pipeline (discovery, surface extraction, rendering)
- Explain how to add a new package (no code changes needed)
- Explain how to extend the surface extractor for new app.add_* calls
…dmonition
why: Missed in the batch master→main fix; the blob URL pointed to the
wrong branch and would 404 for users following the link.
what:
- docs/packages/gp-sphinx.md: blob/master → blob/main for conf.py link
…r fence
why: Multiple eval-rst blocks inside a single quadruple-backtick fence
render as a single copyable unit, making individual commands hard
to copy independently.
what:
- Separate each code block into its own ````md ... ```` fence
- Affects both single-object examples (autodirective/autorole) and
bulk-directive examples (autodirective-index/autodirectives/
autorole-index/autoroles)
…k highlighting
why: MarkdownLexer's fenced-block language pattern [\w\-]+ rejects {eval-rst}
(the {} are not word chars), so blocks in .myst.md source files fall through
to plain Token.Text with no syntax spans when shown via literalinclude.
what:
- Add MystLexer(MarkdownLexer) with a priority rule for ```{eval-rst} blocks
- _handle_eval_rst() emits opening/closing fences as String.Backtick and
delegates the body to RstLexer(handlecodeblocks=True) for 3-level nesting:
MyST fence → RST directive → inner language (e.g. Python)
- Add tokenize_myst() convenience wrapper for tests and doctests
- Full NumPy docstrings with working doctests throughout
…t suite why: MystLexer must be registered with Sphinx so ````myst fences in docs select it via lexer_classes["myst"] in PygmentsBridge.get_lexer(); tests document and lock the token output for all key cases. what: - Import MystLexer in config.py and register "myst"/"myst-md" aliases via app.add_lexer() in the setup() hook - Add tests/ext/test_myst_lexer.py with NamedTuple+parametrize pattern: FenceFixture (opening/info-string/closing as String.Backtick), RegressionFixture (plain text and standard Python fences still work), NestedHighlightFixture (3-level RST→Python nesting, documents trailing- blank-line requirement from RstLexer._handle_sourcecode) - Standalone tests: empty block, multiple blocks, EOF without newline, tokenize_myst helper
why: ````md triggers MarkdownLexer (via Pygments fallback get_lexer_by_name)
whose fenced-block rule uses [\w\-]+; {eval-rst} contains {} so it doesn't
match, and the block emits <span></span> + raw text (class="highlight-md").
````myst hits lexer_classes["myst"] in PygmentsBridge.get_lexer() at
sphinx/highlighting.py:160 before the Pygments fallback, selecting MystLexer.
what:
- sphinx-autodoc-docutils.md: 6 occurrences ````md → ````myst
- sphinx-autodoc-sphinx.md: 2 occurrences ````md → ````myst
- sphinx-argparse-neo.md: 1 occurrence ````md → ````myst
…s for E501 why: Three description= strings added in b209492 exceeded the 88-char line limit enforced by ruff E501, blocking a clean ruff check run. what: - sphinx_fonts: "Font family dicts (family, package, version, weights, styles)." - sphinx_font_fallbacks: "Fallback @font-face declarations with metric overrides for CLS." - sphinx_font_preload: "Critical font variants to preload (family, weight, style)."
why: Bare next(generator) raised StopIteration and crashed the Sphinx build for any package-reference directive with an unrecognised name. what: - Add default=None to next() and return "" with a logged warning - Add import logging and module-level logger - Add doctest and test_package_reference_markdown_unknown_package_returns_empty
… ImportError why: All three import_module() call sites were unguarded; any missing dependency or syntax error in a workspace package crashed the full build. what: - extension_modules(): wrap top-level import and submodule loop in try/except - collect_extension_surface(): wrap import and return empty SurfaceDict on failure - Log a warning at each site so the skipped module is visible in the build output - Add test_extension_modules_skips_unimportable_module - Add test_collect_extension_surface_skips_unimportable_module
…ainst empty markup why: AutoDirectives.run() and AutoRoles.run() called _render_blocks(self, markup) without a guard, unlike AutoDirectiveIndex which already has if markup else []. Modules with no directive classes or role callables produced a markup="" call that is inconsistent with the Index variant behaviour. what: - Add `if markup else []` guard to AutoDirectives.run() (line 332) - Add `if markup else []` guard to AutoRoles.run() (line 392) - Add test_directive_classes_empty_for_module_with_no_directives - Add test_role_callables_empty_for_module_with_no_roles
why: CLAUDE.md requires all functions/methods to have working doctests; setup() only had a one-line docstring with no doctest. what: - Add FakeApp doctest matching the pattern in sphinx_autodoc_sphinx/__init__.py - Verify autodirective is registered and parallel_read_safe is True
…o library __init__ files why: CLAUDE.md requires NullHandler in every library __init__.py; neither new package had it, causing logging.lastResort to print WARNING+ to stderr in user projects that don't configure logging. what: - Add import logging and NullHandler to sphinx_autodoc_docutils/__init__.py - Add import logging and NullHandler to sphinx_autodoc_sphinx/__init__.py
…lel-build constraint why: Two code review findings flagged undocumented constraints: _render_blocks() is silently duplicated across both autodoc packages, and the docutils global monkey-patch in collect_extension_surface() is not safe for parallel builds. what: - Add NOTE comment above both _render_blocks() copies explaining the duplication and why extraction requires a new dep (extract to gp_sphinx._render if a third caller emerges) - Add comment in collect_extension_surface() explaining the try/finally pattern and noting the parallel-build limitation (sphinx -j N)
why: Badges after H1 had inverted visual gravity — more whitespace above than below — making them read as floating unattached metadata rather than a subtitle. sphinx-design defaults use Bootstrap-era bright solid fills that clash with the Furo muted aesthetic. what: - Tighten h1 bottom margin (0.75rem → 0.2rem) so the badge strip reads as attached subtitle via `article > section > h1` - Convert badge-only first-<p> into a flex metadata strip using CSS :has() - Reset .sd-badge to inline-flex, compact sizing, lower border-radius - Add --badge-alpha-* / --badge-beta-* CSS vars for Furo dark mode: body[data-theme="dark"] for explicit mode, @media (prefers-color-scheme: dark) { body:not([data-theme="light"]) } for auto mode (two separate blocks — cannot combine as selector list) - Alpha: sd-outline-warning → warm amber outline with color-mix tint - Beta: sd-outline-success → cool green outline with color-mix tint - Type badges (sd-bg-primary/success/info): muted gray, scoped to metadata strip paragraph only - .sd-card-footer .sd-badge: compact sizing for grid index cards
…ard footer why: maturity_badge() was injected directly into the grid-item-card title f-string, making it impossible to reposition via CSS. Moving it to the +++ footer section lets the .sd-card-footer CSS rule style it independently. what: - workspace_package_grid_markdown(): remove maturity_badge() from card title, add blank line + +++ + maturity_badge() before ::: close - Add "+++" doctest assertion to verify footer section presence - tests/test_package_reference.py: add MaturityBadgeFixture NamedTuple parametrize for maturity_badge() inputs; GridMarkdownFixture parametrize for structural checks; separate test asserting no badge appears in card title lines
…ages
why: Type (extension/theme/coordinator) is redundant on individual package
pages — the URL, breadcrumb, and section heading already communicate where
you are. The maturity signal (Alpha/Beta) is the one piece of metadata that
earns page-level real estate. Type badge survives in the grid index cards.
what:
- Remove {bdg-primary}`extension`, {bdg-success}`coordinator`, {bdg-info}`theme`
from line 3 of all 7 docs/packages/*.md files
- Keep maturity badge ({bdg-warning-line}`Alpha` / {bdg-success-line}`Beta`) only
…nt overrides why: Previous color-mix approach was doubly broken — sphinx-design sets color and border-color with !important on .sd-text-warning/.sd-outline-warning, silently discarding our overrides; and color-mix(20%, transparent) resolved to a near-invisible tint on Furo's off-white card footer background. what: - Replace color-mix backgrounds with opaque Radix amber-3/green-3 hex values - Add !important to color and border-color to match sphinx-design's own escalation - Switch to background-color (narrower override; no !important needed) - Use Radix step-12 text (11.62:1/10.55:1 AAA), step-11 border (WCAG 1.4.11 ✓) - Add --badge-alpha-bg / --badge-beta-bg variables for clean dark-mode override - Dark mode: Radix amber-3/green-3 dark tints with step-11 text (9.13:1/6.66:1)
…ygments code blocks why: Dict/frozenset defaults (e.g. pytest_fixture_builtin_links with 6 full URLs) rendered as huge single-line <code> elements inside confval entries, breaking layout. Direct node construction bypasses RST re-parsing and build cache issues. what: - Add _COMPLEX_REPR_THRESHOLD (60 chars) and _is_complex_default() helper - Add _make_default_block() returning nodes.literal_block with language=python - Add _iter_desc_content() to traverse desc_content nodes from parsed output - Omit :default: field in render_config_value_markup() for complex values - Inject literal_block directly into desc_content in AutoconfigvaluesDirective - Add parametrized test_is_complex_default, test_make_default_block, and test_render_config_value_markup_omits_default_for_complex
…rt name
why: Full dotted paths like sphinx_autodoc_docutils._directives.AutoDirectives
made the Callable column very wide; linking to the autodoc entry and showing
only the short name is more scannable and navigable.
what:
- Change object_path() to return {py:obj}`~module.Name` cross-reference markup
- Update hard-coded add_crossref_type callable to {py:meth}`~...` form
- Update object_path() doctest to reflect new return format
…erences
why: The Callable column showed full dotted paths as plain code. With py:obj
roles resolved to the py domain, they now render as clickable short names.
what:
- Add _register_extension_objects() to populate py domain from workspace
extension setup() calls on env-check-consistency (after all docs are read
but before the write phase resolves xrefs)
- Use env-check-consistency not env-before-read-docs: clear_doc() wipes
domain entries whose docname matches the page being re-read, making
pre-registration in env-before-read-docs invisible to the resolver
- Add parametrized test_register_extension_objects_populates_py_domain
covering directives and role classes for multiple packages
- Update object_path() docstring and doctest to reflect {py:obj} markup
why: _register_extension_objects() only ran setup() on the base module, missing roles/lexers/directives from submodules like sphinx_argparse_neo.exemplar. Also missed docutils roles registered via register_local_role() rather than app.add_role() (e.g. cli_option_role in sphinx_argparse_neo.exemplar). what: - Loop over extension_modules() (all submodules with setup()) not just base module - Patch docutils register_local_role/register_canonical_role during setup() to capture docutils-registered roles alongside app.add_role() calls - Add exemplar_role_from_submodule fixture to domain registration tests
…o fix B023 why: _capture was defined inside a for-loop body and closed over docutils_roles without binding it, creating a latent late-binding closure bug (ruff B023). Also fix RUF012 on _MockPyDomain/_MockEnv mock classes in the test. what: - Add _roles: list[tuple[str, object]] = docutils_roles default arg to _capture - Annotate mock class attributes with t.ClassVar in test_package_reference.py
…ences
why: Inline mentions of merge_sphinx_config(), setup(), etc. were unlinked
plain code; MyST {py:func} roles make them navigable cross-references.
what:
- Add autofunction entry for gp_sphinx.config.setup to api.md
- Link gp_sphinx.config.merge_sphinx_config in configuration.md, index.md,
packages/gp-sphinx.md, packages/sphinx-gptheme.md, and api.md prose
- Link gp_sphinx.config.setup in configuration.md (intro paragraph + table)
…phinx why: intersphinx_mapping was gated behind GP_SPHINX_ENABLE_INTERSPHINX=1 with no real benefit — Sphinx caches objects.inv locally after first fetch, and CI already has network access. Remove the gate and always map both targets. what: - Remove os import and env-var conditional - Always enable py (docs.python.org) and sphinx (sphinx-doc.org) mappings - Use /3/ suffix on Python URL for stable version
why: Now that intersphinx is always enabled we can cross-reference Sphinx's
own API docs from prose that mentions those symbols.
what:
- sphinx-autodoc-sphinx.md: link add_config_value via {py:meth}sphinx:~...
- configuration.md: link sphinx.ext.linkcode via {py:mod}sphinx:...
- packages/gp-sphinx.md: link sphinx.ext.linkcode
- api.md: link sphinx.ext.linkcode in make_linkcode_resolve prose
Member
Author
Code reviewFound 3 issues:
gp-sphinx/packages/gp-sphinx/src/gp_sphinx/config.py Lines 82 to 90 in 9eaec87
Lines 158 to 167 in 9eaec87
Lines 34 to 38 in 9eaec87 🤖 Generated with Claude Code - If this code review was useful, please react with 👍. Otherwise, react with 👎. |
why: CLAUDE.md mandates NumPy docstring style; deep_merge, make_linkcode_resolve, and merge_sphinx_config used Sphinx :param:/:type:/:rtype: format instead. what: - Convert deep_merge docstring to NumPy Parameters/Returns sections - Convert make_linkcode_resolve docstring to NumPy Parameters/Returns sections - Convert merge_sphinx_config docstring to NumPy Parameters/Returns sections
why: 21 doctests in sphinx_autodoc_sphinx/_directives.py were never collected because packages/sphinx-autodoc-sphinx/src was absent from testpaths. what: - Add packages/sphinx-autodoc-sphinx/src to pytest testpaths in pyproject.toml
…" to "main" why: Repo default branch is main; "master" default caused broken "Edit on GitHub" and "View source" links on every docs page. what: - Change source_branch parameter default in merge_sphinx_config() to "main" - Change DEFAULT_THEME_OPTIONS["source_branch"] seed value to "main" - Update docs/conf.py source_branch kwarg to "main" - Update configuration.md table rows to reflect new defaults
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Large docs-and-feature branch covering three areas:
1. Documentation site build-out (
62b6550→0e96e0b)docs/_ext/package_reference.pyextension — introspects livesetup()calls and renders directives, roles, and config-value tables automaticallydocs/configuration.md— full reference for allmerge_sphinx_config()defaultsdocs/api.md— API reference withmake_linkcode_resolvewiring exampledocs/quickstart.md— "Your first build" tutorial sectiondocs/redirects.txt) for moved pagessphinx-autodoc-docutilsandsphinx-autodoc-sphinx2. New packages:
sphinx-autodoc-docutilsandsphinx-autodoc-sphinxsphinx-autodoc-docutils— documents docutils directives and roles from Python callables asrst:directive/rst:rolereference blockssphinx-autodoc-sphinx— documents Sphinx config values registered byapp.add_config_value()as liveconfvalentries and summary index tablespyproject.toml, typed stubs (py.typed), NumPy docstrings, mypy-strict typing3.
MystLexerfor{eval-rst}fenced block highlighting (968e4c3→37a3be0)Root cause:
MarkdownLexer's fenced-block language pattern[\w\-]+rejects{eval-rst}(the{}don't match), so blocks fall through to plainToken.Textwith no syntax spans.packages/gp-sphinx/src/gp_sphinx/myst_lexer.py—MystLexer(MarkdownLexer)with a priority rule for```{eval-rst}blocks; delegates body toRstLexer(handlecodeblocks=True)for 3-level nesting (MyST → RST → inner language)stubs/pygments/lexers/markup.pyi— local stub forMarkdownLexer/RstLexer(not intypes-Pygments)config.pyregisters"myst"/"myst-md"aliases viaapp.add_lexer()````mdto````myst—mdfell through to Pygments'MarkdownLexerwhilemysthits Sphinx'slexer_classes["myst"](verified via live HTML inspection showinghighlight-mystclass and proper<span class="sb">elements)Fixes
refactor(config): Adddescription=to alladd_config_value()callsfix(sphinx-fonts): Shorten description strings for E501fix(docs): GitHub source linkstree/master→tree/mainfix(sphinx-autodoc-sphinx):option_spectype annotation and_Opttuple unpackingTest plan
uv run pytest— 611 passed, 3 skippeduv run mypy— no issuesuv run ruff check .— no issues{eval-rst}blocks on/packages/sphinx-autodoc-sphinx/and/packages/sphinx-autodoc-docutils/render withhighlight-mystclass and RST syntax spans