Skip to content

🐛 FIX: Reserve inline footnote IDs before parsing content - #162

Merged
chrisjsewell merged 6 commits into
executablebooks:masterfrom
civilwarinfrance:fix/inline-footnote-id-collisions
Nov 9, 2026
Merged

chrisjsewell merged 6 commits into
executablebooks:masterfrom
civilwarinfrance:fix/inline-footnote-id-collisions

Conversation

@civilwarinfrance

Copy link
Copy Markdown
Contributor

Inline notes allocate their ID before parsing their content but only insert the note afterward. If that content references another note, both notes receive the same ID and the outer note overwrites the inner one. A later reference can then raise KeyError('count').

Reproducer:

from markdown_it import MarkdownIt
from mdit_py_plugins.footnote import footnote_plugin

md = MarkdownIt().use(footnote_plugin)
md.render("^[outer [^named]] again[^named]\n\n[^named]: named body")

Reserve the outer note's entry before recursive inline parsing, retaining the same token list while it is populated. Each note then keeps its own ID and content. Fourteen regression cases cover named references, repeated references, nested inline notes, pre-existing notes and both move_to_end modes; rendered anchors must be unique and all internal targets must exist. Unchanged master fails 10 cases, with 4 controls passing. The docs and changelog describe the corrected behavior.

Validation through tox on macOS ARM64:

  • CPython 3.11.15 / markdown-it-py 4.2.0: 525 tests passed, 93% package coverage.
  • Strict mypy: all 40 source files pass.
  • Full pinned pre-commit hooks pass, including Ruff lint/format and Mypy.
  • Strict documentation build passes (tox -e docs-update).
  • git diff --check passes. Other platforms and hosted CI remain unverified.

AI disclosure: OpenAI Codex assisted with code inspection, reproduction, implementation, tests and this description. This is a reproduced code-inspection finding; no production incident, personal human review or upstream acceptance is claimed.

@chrisjsewell
chrisjsewell merged commit 450f46a into executablebooks:master Nov 9, 2026
11 checks passed
chrisjsewell added a commit that referenced this pull request Nov 10, 2026
Brings in #148, #138, #153, #157, #149, #152, #154, #158, #159, #161, #162.

Conflicts:
- attrs/index.py: #153 merges inline classes from the opening token
  (attr_token), #150 passes state.md to _add_attrs; both kept.
- tests/test_attrs.py, tests/test_texmath.py: both sides' tests kept.

Adds test_class_accumulation_is_filtered: classes merged across inline
groups (onto the opening token) and across stacked block attributes still
pass through the on*, URL and allowed filters.
@chrisjsewell

Copy link
Copy Markdown
Member

Merged in 450f46a, thanks @civilwarinfrance, and for the thorough test! Before merging I pushed a merge of master, removed the docs/index.md paragraph (this is a bug fix rather than new behaviour to document) and the CHANGELOG entry (the release PR writes it), and added a ^[outer ^[inner]] HTML fixture to tests/fixtures/footnote.md.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants