html: Fix html writer footnotes (in headlines)
writer.footnotes must be a pointer as we copy the writer in nodesAsString() and can thus end up modifying the footnotes.list slice without it being reflected in the original writer (i.e. when the backing array of the slice changes).
This commit is contained in:
parent
97fe8b7850
commit
f67a251e27
4 changed files with 69 additions and 2 deletions
|
@ -19,7 +19,7 @@ type HTMLWriter struct {
|
||||||
HighlightCodeBlock func(source, lang string) string
|
HighlightCodeBlock func(source, lang string) string
|
||||||
htmlEscape bool
|
htmlEscape bool
|
||||||
log *log.Logger
|
log *log.Logger
|
||||||
footnotes footnotes
|
footnotes *footnotes
|
||||||
}
|
}
|
||||||
|
|
||||||
type footnotes struct {
|
type footnotes struct {
|
||||||
|
@ -61,7 +61,7 @@ func NewHTMLWriter() *HTMLWriter {
|
||||||
HighlightCodeBlock: func(source, lang string) string {
|
HighlightCodeBlock: func(source, lang string) string {
|
||||||
return fmt.Sprintf("<div class=\"highlight\">\n<pre>\n%s\n</pre>\n</div>", html.EscapeString(source))
|
return fmt.Sprintf("<div class=\"highlight\">\n<pre>\n%s\n</pre>\n</div>", html.EscapeString(source))
|
||||||
},
|
},
|
||||||
footnotes: footnotes{
|
footnotes: &footnotes{
|
||||||
mapping: map[string]int{},
|
mapping: map[string]int{},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
47
org/testdata/footnotes_in_headline.html
vendored
Normal file
47
org/testdata/footnotes_in_headline.html
vendored
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
<nav>
|
||||||
|
<ul>
|
||||||
|
<li><a href="#headline-1">Title <sup class="footnote-reference">1</sup></a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
<h1 id="headline-1">
|
||||||
|
Title <sup class="footnote-reference"><a id="footnote-reference-1" href="#footnote-1">1</a></sup>
|
||||||
|
</h1>
|
||||||
|
<div class="footnotes">
|
||||||
|
<hr class="footnotes-separatator">
|
||||||
|
<div class="footnote-definitions">
|
||||||
|
<div class="footnote-definition">
|
||||||
|
<sup id="footnote-1"><a href="#footnote-reference-1">1</a></sup>
|
||||||
|
<div class="footnote-body">
|
||||||
|
<p>
|
||||||
|
this test file just exists to reproduce a bug with footnotes in headlines - that only happens in very specific circumstances.
|
||||||
|
The TLDR is:
|
||||||
|
</p>
|
||||||
|
<ul>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
HTMLWriter.footnotes should be a pointer field. I didn't notice my error as go translated my pointer-method calls on
|
||||||
|
non-pointer values rather than complaining - i.e. <code class="verbatim">footnotes.add()</code> transparently gets translated to <code class="verbatim">(&footnotes).add()</code> (<a href="https://golang.org/ref/spec#Calls">docs</a>).
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
Headlines have to be htmlified twice - once for the outline and once for the headline itself. To do so we have to copy the writer
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
Copying the writer copies footnotes - which contains a map and a slice. Changes to the map will always be reflected in the original map.
|
||||||
|
Changes to the slice will only be reflected if the slice doesn't grow.
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
<li>
|
||||||
|
<p>
|
||||||
|
We can thus end up with a footnote being in the mapping but not the slice - and get an index out of range error.
|
||||||
|
</p>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
10
org/testdata/footnotes_in_headline.org
vendored
Normal file
10
org/testdata/footnotes_in_headline.org
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
* Title [fn:1]
|
||||||
|
|
||||||
|
[fn:1] this test file just exists to reproduce a bug with footnotes in headlines - that only happens in very specific circumstances.
|
||||||
|
The TLDR is:
|
||||||
|
- HTMLWriter.footnotes should be a pointer field. I didn't notice my error as go translated my pointer-method calls on
|
||||||
|
non-pointer values rather than complaining - i.e. =footnotes.add()= transparently gets translated to =(&footnotes).add()= ([[https://golang.org/ref/spec#Calls][docs]]).
|
||||||
|
- Headlines have to be htmlified twice - once for the outline and once for the headline itself. To do so we have to copy the writer
|
||||||
|
- Copying the writer copies footnotes - which contains a map and a slice. Changes to the map will always be reflected in the original map.
|
||||||
|
Changes to the slice will only be reflected if the slice doesn't grow.
|
||||||
|
- We can thus end up with a footnote being in the mapping but not the slice - and get an index out of range error.
|
10
org/testdata/footnotes_in_headline.pretty_org
vendored
Normal file
10
org/testdata/footnotes_in_headline.pretty_org
vendored
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
* Title [fn:1]
|
||||||
|
|
||||||
|
[fn:1] this test file just exists to reproduce a bug with footnotes in headlines - that only happens in very specific circumstances.
|
||||||
|
The TLDR is:
|
||||||
|
- HTMLWriter.footnotes should be a pointer field. I didn't notice my error as go translated my pointer-method calls on
|
||||||
|
non-pointer values rather than complaining - i.e. =footnotes.add()= transparently gets translated to =(&footnotes).add()= ([[https://golang.org/ref/spec#Calls][docs]]).
|
||||||
|
- Headlines have to be htmlified twice - once for the outline and once for the headline itself. To do so we have to copy the writer
|
||||||
|
- Copying the writer copies footnotes - which contains a map and a slice. Changes to the map will always be reflected in the original map.
|
||||||
|
Changes to the slice will only be reflected if the slice doesn't grow.
|
||||||
|
- We can thus end up with a footnote being in the mapping but not the slice - and get an index out of range error.
|
Loading…
Add table
Add a link
Reference in a new issue