Skip to content

Footnotes implementation - #78

Open
ralsina wants to merge 6 commits into
icyleaf:masterfrom
ralsina:footnotes
Open

Footnotes implementation#78
ralsina wants to merge 6 commits into
icyleaf:masterfrom
ralsina:footnotes

Conversation

@ralsina

@ralsina ralsina commented Mar 7, 2025

Copy link
Copy Markdown
Contributor

This is, AFAIK a full implementation of footnotes as per the specs we have on the repo.

@trafico-bot trafico-bot Bot added the 🚧 WIP Still work-in-progress, please don't review and don't merge label Mar 7, 2025
@icyleaf icyleaf mentioned this pull request Mar 9, 2025
7 tasks
@ralsina
ralsina force-pushed the footnotes branch 2 times, most recently from d9a392d to ac17b63 Compare March 13, 2025 23:21
@ralsina ralsina changed the title WIP: Footnotes implementation Basic Footnotes implementation Mar 13, 2025
@trafico-bot trafico-bot Bot added 🔍 Ready for Review Pull Request is not reviewed yet and removed 🚧 WIP Still work-in-progress, please don't review and don't merge labels Mar 13, 2025
@ralsina
ralsina marked this pull request as ready for review March 14, 2025 17:39
@nobodywasishere

Copy link
Copy Markdown
Collaborator

If this is in a stable place, I'm fine to merge this now and sort out the issues in followup PRs.

@ralsina

ralsina commented May 6, 2025

Copy link
Copy Markdown
Contributor Author

It implements some basic cases and doesn't seem to break anything at least :-)

@ralsina

ralsina commented Feb 15, 2026

Copy link
Copy Markdown
Contributor Author

I am working now on finishing this.

@ralsina ralsina changed the title Basic Footnotes implementation Footnotes implementation Feb 15, 2026
@ralsina

ralsina commented Feb 16, 2026

Copy link
Copy Markdown
Contributor Author

Weird, tests were passing before, fixing.

@ralsina

ralsina commented Feb 16, 2026

Copy link
Copy Markdown
Contributor Author

Sigh, I asked the LLM to fix ameba warnings and it choose wrong on every one of them. Fixed now :-)

@nobodywasishere

nobodywasishere commented Feb 23, 2026

Copy link
Copy Markdown
Collaborator

Wonder if the .claude and .vscode folders should be in the .gitignore - how are other projects handling these?

Edit: Same with CLAUDE.md and the like

Comment thread src/markd/parsers/block.cr
@ralsina

ralsina commented Feb 23, 2026

Copy link
Copy Markdown
Contributor Author

Wonder if the .claude and .vscode folders should be in the .gitignore - how are other projects handling these?

Edit: Same with CLAUDE.md and the like

Deleted and ignored.

@ralsina ralsina closed this Feb 23, 2026
@ralsina ralsina reopened this Feb 23, 2026
@ralsina

ralsina commented Feb 23, 2026

Copy link
Copy Markdown
Contributor Author

I can't spend any more time working on this PR.

@nobodywasishere

Copy link
Copy Markdown
Collaborator

I can carry it from here if you want.

@ralsina

ralsina commented Feb 23, 2026

Copy link
Copy Markdown
Contributor Author

I can carry it from here if you want.

That would be great, thanks.

@ralsina

ralsina commented Jul 29, 2026

Copy link
Copy Markdown
Contributor Author

I may have some time to work on markpdf these days if the project is still alive. Is there any problem with this PR?

@ralsina ralsina closed this Aug 12, 2026
@ralsina

ralsina commented Aug 12, 2026

Copy link
Copy Markdown
Contributor Author

Guess it's dead. If someone wants to pick up the footnotes implementation the code is there.

@ralsina

ralsina commented Aug 13, 2026

Copy link
Copy Markdown
Contributor Author

Sorry about the tantrum. Costs me nothing to keep this PR open for whenever someone wants it.

@ralsina ralsina reopened this Aug 13, 2026

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements GitHub Flavored Markdown (GFM) footnotes end-to-end in Markd: parsing inline footnote references and block footnote definitions, post-processing to normalize numbering / resolve nested references, and HTML rendering including backrefs.

Changes:

  • Added a new block rule and node types to recognize and store footnote definitions and inline footnote references.
  • Added a post-parse footnote processing pass to resolve references (including nesting), normalize numbering, and move definitions to the end.
  • Implemented HTML output for footnote refs/definitions and enabled fixture-based coverage for the new behavior.

Reviewed changes

Copilot reviewed 10 out of 11 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/markd/rules/footnote_definition.cr New block rule to match and collect footnote definition blocks.
src/markd/rule.cr Adds footnote definition start regex and treats [ as “maybe special”.
src/markd/renderers/html_renderer.cr Renders footnote refs/definitions and backrefs; supports valueless (boolean) attributes.
src/markd/renderer.cr Adds abstract renderer hooks for footnote nodes (but currently has a duplicate abstract method).
src/markd/parsers/inline.cr Parses [^label] as a new inline Footnote node in GFM mode.
src/markd/parsers/block.cr Adds passes to parse definition contents as blocks and to resolve/number/move footnotes.
src/markd/node.cr Introduces Footnote / FootnoteDefinition node types and container classification changes.
spec/fixtures/gfm-regression.txt Enables existing regression fixtures that exercise footnotes (including nesting).
spec/fixtures/gfm-extensions.txt Enables GFM extension fixtures for footnotes and escaping behavior.
.vscode/launch.json Removes editor launch configuration from the repo.
.gitignore Ignores spec binary and editor/agent files; broadens VS Code ignore to entire folder.
Suppressed comments (1)

src/markd/parsers/block.cr:156

  • footnote_definitions is deleted from inside footnote_definitions.each, which can raise due to Hash mutation during iteration. Iterate over keys snapshot instead.
          footnote_definitions.delete footnote_title

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread src/markd/renderer.cr Outdated
Comment thread src/markd/rule.cr Outdated

ADMONITION_START = /^> \[!((?:NOTE|TIP|IMPORTANT|CAUTION|WARNING)+)](\s*.*)?$/

FOOTNOTE_DEFINITION_START = /\[\^[^\]]+\]:(\s|$)/
Comment thread src/markd/parsers/block.cr Outdated
Comment on lines +99 to +106
footnotes.each do |footnote_title, nodes|
if !footnote_definitions.has_key?(footnote_title)
nodes.each do |fn_node|
fn_node.type = Node::Type::Text
fn_node.text = "[^#{footnote_title}]"
end
footnotes.delete footnote_title
else
Comment thread src/markd/parsers/block.cr Outdated
n, entering = event
if entering && n.type.text?
replaced = false
n.text = n.text.gsub(/\[\^([\w\-]+)\]/) do |m|
Comment on lines +287 to +291
# Is it a footnote?
if is_footnote
# If the 1st char after the closing bracket is a ":" then it's NOT
# a footnote, it's a footnote definition.
if char_at?(@pos) == ':'
@icyleaf

icyleaf commented Aug 13, 2026

Copy link
Copy Markdown
Owner

I'll do a first round of review using Copilot first. @nobodywasishere hasn't been active in this project for a while—I wonder if she's been busy since joining the Crystal core team.

@nobodywasishere

Copy link
Copy Markdown
Collaborator

I am still here. Over the past year though have been dealing with a lot of personal stuff, and wrestling with how programming is changing in the advent of AI. Hard to find the joy in doing things if there's a machine that'll do it much faster than you.

@ralsina

ralsina commented Aug 13, 2026

Copy link
Copy Markdown
Contributor Author

I am still here. Over the past year though have been dealing with a lot of personal stuff, and wrestling with how programming is changing in the advent of AI. Hard to find the joy in doing things if there's a machine that'll do it much faster than you.

Understand the feeling

Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@ralsina

ralsina commented Aug 13, 2026

Copy link
Copy Markdown
Contributor Author

Tried to accept the fix suggestions but after the 1st one now they are stale :-D

I will get a minion to look at them

- renderer.cr: remove duplicate thematic_break abstract method declaration
- rule.cr: anchor FOOTNOTE_DEFINITION_START to line start so mid-line
  [^x]: does not incorrectly start a block-level definition
- block.cr: iterate over a snapshot of hash keys instead of mutating
  footnotes/footnote_definitions during each-iteration (fixes potential
  Hash modified during iteration errors)
- block.cr: broaden nested-footnote label regex from [\w-]+ to [^\]]+
  so nested references with arbitrary label characters are detected
- inline.cr: skip inline-link (...) parsing for footnote openers so
  [^a](url) renders as a footnote ref with (url) preserved as text
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🔍 Ready for Review Pull Request is not reviewed yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants