diff --git a/snooty/postprocess.py b/snooty/postprocess.py index 29b55e89..6b27e80d 100644 --- a/snooty/postprocess.py +++ b/snooty/postprocess.py @@ -1640,6 +1640,28 @@ def enter_node(self, fileid_stack: FileIdStack, node: n.Node) -> None: self.pending_targets = [] +class FootnoteHandler(Handler): + """ + Handles normalizing footnotes and their references to make sure footnotes spread across include files are not repeated + across a single page. + """ + + def __init__(self, context: Context) -> None: + super().__init__(context) + # Footnote reference ids from tinydocutils starts at 1 + self.id_counter = 1 + + def enter_page(self, fileid_stack: FileIdStack, page: Page) -> None: + self.id_counter = 1 + + def enter_node(self, fileid_stack: FileIdStack, node: n.Node) -> None: + if not isinstance(node, n.FootnoteReference): + return + + node.id = f"id{self.id_counter}" + self.id_counter += 1 + + class RefsHandler(Handler): def __init__(self, context: Context) -> None: super().__init__(context) @@ -2139,6 +2161,7 @@ class Postprocessor: HeadingHandler, TocTitleHandler, AddTitlesToLabelTargetsHandler, + FootnoteHandler, ProgramOptionHandler, TabsSelectorHandler, ContentsHandler, diff --git a/snooty/test_postprocess.py b/snooty/test_postprocess.py index 3fc4faa8..e7e7117d 100644 --- a/snooty/test_postprocess.py +++ b/snooty/test_postprocess.py @@ -4349,3 +4349,183 @@ def test_multi_page_tutorials() -> None: ], }, } + + +def test_footnote_id_numbers() -> None: + with make_test( + { + Path( + "source/includes/testing-footnotes-inside-includes.rst" + ): """ +This is content within an include for a footnote related to [#footnote-inside-includes]_ . +""", + Path( + "source/index.txt" + ): """ +================= +Testing Footnotes +================= + +Two within same content block +----------------------------- + +This is a paragraph with a footnote to [#footnote-same-block]_ . Here is another sentence +with another reference to the [#footnote-same-block]_ footnote. + +.. [#footnote-same-block] + + Footnote within same content block. + +Inside of includes +------------------ + +.. include:: /includes/testing-footnotes-inside-includes.rst + +.. include:: /includes/testing-footnotes-inside-includes.rst + +.. include:: /includes/testing-footnotes-inside-includes.rst + +.. include:: /includes/testing-footnotes-inside-includes.rst + +.. [#footnote-inside-includes] + + Footnotes inside of the same includes files, but different instances. + +Numbered footnotes +------------------ + +This is a paragraph with a footnote to [1]_ . Here is another sentence +with another reference to the [1]_ footnote. + +This is a paragraph with a footnote to [2]_ . Here is another sentence +with another reference to the [3]_ footnote. + +This is a paragraph with a footnote to [3]_ . Here is another sentence +with another reference to the [2]_ footnote [1]_. + +.. [1] Footnote 1? + +.. [2] Footnote 2? + +.. [3] + + Footnote 3? +""", + } + ) as result: + test_fileid = FileId("index.txt") + page = result.pages[test_fileid] + diagnostics = result.diagnostics[test_fileid] + assert len(diagnostics) == 0 + check_ast_testing_string( + page.ast, + """ + +
+ Testing Footnotes +
+ Two within same content block + + This is a paragraph with a footnote to + + . Here is another sentence +with another reference to the + + footnote. + + + + Footnote within same content block. + + +
+
+ Inside of includes + + /includes/testing-footnotes-inside-includes.rst + + + This is content within an include for a footnote related to + + . + + + + + /includes/testing-footnotes-inside-includes.rst + + + This is content within an include for a footnote related to + + . + + + + + /includes/testing-footnotes-inside-includes.rst + + + This is content within an include for a footnote related to + + . + + + + + /includes/testing-footnotes-inside-includes.rst + + + This is content within an include for a footnote related to + + . + + + + + + Footnotes inside of the same includes files, but different instances. + + +
+
+ Numbered footnotes + + This is a paragraph with a footnote to + 1 + . Here is another sentence +with another reference to the + 1 + footnote. + + + This is a paragraph with a footnote to + 2 + . Here is another sentence +with another reference to the + 3 + footnote. + + + This is a paragraph with a footnote to + + 3 + + . Here is another sentence +with another reference to the + + 2 + + footnote + + 1 + + . + + Footnote 1? + Footnote 2? + Footnote 3? +
+
+
+""", + )