vsch / flexmark-java

CommonMark/Markdown Java parser with source level AST. CommonMark 0.28, emulation of: pegdown, kramdown, markdown.pl, MultiMarkdown. With HTML to MD, MD to PDF, MD to DOCX conversion modules.
BSD 2-Clause "Simplified" License
2.21k stars 260 forks source link

footnotes are not sorted by first appearence #592

Open mtrevisan opened 9 months ago

mtrevisan commented 9 months ago

Processing a document I found that FootnoteRepository.addFootnoteReference:46 is called every time a reference is found. But since footnoteBlock.setFirstReferenceOffset(footnote.getStartOffset()); is called and not addFirstReferenceOffset, the offset is rewritten every time. I think the following code will work, but I ask you if I'm not wrong with my analysis.

if (! footnoteBlock.isReferenced()){
   footnoteBlock.setFirstReferenceOffset(footnote.getStartOffset());
}
else{
   footnoteBlock.addFirstReferenceOffset(footnote.getStartOffset());
}

and correct the method into (because "first" implies "<")

public void addFirstReferenceOffset (int firstReferenceOffset){
    if (firstReferenceOffset < this.firstReferenceOffset){
        this.firstReferenceOffset = firstReferenceOffset;
    }
}

The code I'm using is as simple as

private static Parser PARSER;
private static HtmlRenderer RENDERER;
static{
    MutableDataSet options = new MutableDataSet()
        .set(Parser.REFERENCES_KEEP, KeepType.LAST)
        .set(Parser.EXTENSIONS, List.of(
            TablesExtension.create(),
            TypographicExtension.create(),
            SubscriptExtension.create(),
            FootnoteExtension.create()))

        .set(TablesExtension.COLUMN_SPANS, false)
        .set(TablesExtension.APPEND_MISSING_COLUMNS, true)
        .set(TablesExtension.DISCARD_EXTRA_COLUMNS, true)
        .set(TablesExtension.HEADER_SEPARATOR_COLUMN_MATCH, true)

        .set(FootnoteExtension.FOOTNOTE_PLACEMENT, ElementPlacement.DOCUMENT_TOP)
//      .set(FootnoteExtension.FOOTNOTE_SORT, ElementPlacementSort.AS_IS)
        .set(FootnoteExtension.FOOTNOTE_SORT, ElementPlacementSort.SORT)
    ;

    PARSER = Parser.builder(options)
        .build();
    RENDERER = HtmlRenderer.builder(options)
        .build();
}

and

Node document = PARSER.parse(content);
String html = RENDERER.render(document);

Please provide as much information about where the bug is located or what you were using:

  1. Options used to configure the parser, renderer, formatter, etc. Please provide concise code when you can.

I've tried every combination of the following options

options.set(FootnoteExtension.FOOTNOTE_PLACEMENT, ElementPlacement.DOCUMENT_TOP);
options.set(FootnoteExtension.FOOTNOTE_SORT, ElementPlacementSort.SORT);

But without any success (and then I found the problem I described in the beginning).

Am I wrong in my analysis?

To reproduce the problem:

public static void main(String[] args){
    String input = """
        asd[^1]
        qwe[^2]
        123[^1]
        [^1]: one
        [^2]: two
        """;
    System.out.println(RENDERER.render(PARSER.parse(input)));
}

output:

<p>asd<sup id="fnref-2"><a class="footnote-ref" href="#fn-2">2</a></sup>
qwe<sup id="fnref-1"><a class="footnote-ref" href="#fn-1">1</a></sup>
123<sup id="fnref-2-1"><a class="footnote-ref" href="#fn-2">2</a></sup></p>
<div class="footnotes">
   <hr />
   <ol>
      <li id="fn-1">
         <p>two</p>
         <a href="#fnref-1" class="footnote-backref">&#8617;</a>
      </li>
      <li id="fn-2">
         <p>one</p>
         <a href="#fnref-2" class="footnote-backref">&#8617;</a>
         <a href="#fnref-2-1" class="footnote-backref">&#8617;</a>
      </li>
   </ol>
</div>