Skip to content

Commit 35f95df

Browse files
authored
Merge pull request #1 from ChenPi11/copilot/add-footnote-hash-support
Add support for custom href prefix in footnotes
2 parents fe6c169 + 9a7260c commit 35f95df

File tree

5 files changed

+131
-2
lines changed

5 files changed

+131
-2
lines changed

README.md

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,34 @@ package system, module will add itself globally as `window.markdownitFootnote`.
9090

9191
### Customize
9292

93+
#### Custom href prefix
94+
95+
You can customize the `href` attribute in footnote links by providing a `footnoteHrefPrefix` in the `env` parameter when calling `render()`. This is useful when you want footnotes to link to a different page or use a custom URL structure:
96+
97+
```js
98+
const md = require('markdown-it')().use(require('markdown-it-footnote'));
99+
100+
// Custom href prefix for footnote links
101+
md.render('Here is a footnote[^1]\n\n[^1]: Footnote text', {
102+
footnoteHrefPrefix: '/docs/page#'
103+
});
104+
105+
// Output includes: <a href="/docs/page#fn1" ...>
106+
```
107+
108+
You can also combine this with `docId` to customize both the IDs and href prefix:
109+
110+
```js
111+
md.render('Here is a footnote[^1]\n\n[^1]: Footnote text', {
112+
docId: 'my-doc',
113+
footnoteHrefPrefix: '/docs/page#'
114+
});
115+
116+
// Output includes: <a href="/docs/page#fn-my-doc-1" id="fnref-my-doc-1">
117+
```
118+
119+
#### Renderer customization
120+
93121
If you want to customize the output, you'll need to replace the template
94122
functions. To see which templates exist and their default implementations,
95123
look in [`index.js`](index.js). The API of these template functions is out of

index.mjs

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ function render_footnote_anchor_name (tokens, idx, options, env/*, slf */) {
1414
return prefix + n
1515
}
1616

17+
function render_footnote_href_prefix (tokens, idx, options, env/*, slf */) {
18+
let prefix = '#'
19+
20+
if (typeof env.footnoteHrefPrefix === 'string') prefix = env.footnoteHrefPrefix
21+
22+
return prefix
23+
}
24+
1725
function render_footnote_caption (tokens, idx/*, options, env, slf */) {
1826
let n = Number(tokens[idx].meta.id + 1).toString()
1927

@@ -25,11 +33,12 @@ function render_footnote_caption (tokens, idx/*, options, env, slf */) {
2533
function render_footnote_ref (tokens, idx, options, env, slf) {
2634
const id = slf.rules.footnote_anchor_name(tokens, idx, options, env, slf)
2735
const caption = slf.rules.footnote_caption(tokens, idx, options, env, slf)
36+
const hrefPrefix = slf.rules.footnote_href_prefix(tokens, idx, options, env, slf)
2837
let refid = id
2938

3039
if (tokens[idx].meta.subId > 0) refid += `:${tokens[idx].meta.subId}`
3140

32-
return `<sup class="footnote-ref"><a href="#fn${id}" id="fnref${refid}">${caption}</a></sup>`
41+
return `<sup class="footnote-ref"><a href="${hrefPrefix}fn${id}" id="fnref${refid}">${caption}</a></sup>`
3342
}
3443

3544
function render_footnote_block_open (tokens, idx, options) {
@@ -56,11 +65,12 @@ function render_footnote_close () {
5665

5766
function render_footnote_anchor (tokens, idx, options, env, slf) {
5867
let id = slf.rules.footnote_anchor_name(tokens, idx, options, env, slf)
68+
const hrefPrefix = slf.rules.footnote_href_prefix(tokens, idx, options, env, slf)
5969

6070
if (tokens[idx].meta.subId > 0) id += `:${tokens[idx].meta.subId}`
6171

6272
/* ↩ with escape code to prevent display as Apple Emoji on iOS */
63-
return ` <a href="#fnref${id}" class="footnote-backref">\u21a9\uFE0E</a>`
73+
return ` <a href="${hrefPrefix}fnref${id}" class="footnote-backref">\u21a9\uFE0E</a>`
6474
}
6575

6676
export default function footnote_plugin (md) {
@@ -77,6 +87,7 @@ export default function footnote_plugin (md) {
7787
// helpers (only used in other rules, no tokens are attached to those)
7888
md.renderer.rules.footnote_caption = render_footnote_caption
7989
md.renderer.rules.footnote_anchor_name = render_footnote_anchor_name
90+
md.renderer.rules.footnote_href_prefix = render_footnote_href_prefix
8091

8192
// Process footnote block definition
8293
function footnote_def (state, startLine, endLine, silent) {
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
Both docId and custom href prefix:
2+
.
3+
Here is a footnote reference,[^1] and another.[^longnote]
4+
5+
[^1]: Here is the footnote.
6+
7+
[^longnote]: Here's one with multiple blocks.
8+
.
9+
<p>Here is a footnote reference,<sup class="footnote-ref"><a href="/docs#fn-my-doc-1" id="fnref-my-doc-1">[1]</a></sup> and another.<sup class="footnote-ref"><a href="/docs#fn-my-doc-2" id="fnref-my-doc-2">[2]</a></sup></p>
10+
<hr class="footnotes-sep">
11+
<section class="footnotes">
12+
<ol class="footnotes-list">
13+
<li id="fn-my-doc-1" class="footnote-item"><p>Here is the footnote. <a href="/docs#fnref-my-doc-1" class="footnote-backref">↩</a></p>
14+
</li>
15+
<li id="fn-my-doc-2" class="footnote-item"><p>Here's one with multiple blocks. <a href="/docs#fnref-my-doc-2" class="footnote-backref">↩</a></p>
16+
</li>
17+
</ol>
18+
</section>
19+
.
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
Custom href prefix:
2+
.
3+
Here is a footnote reference,[^1] and another.[^longnote]
4+
5+
[^1]: Here is the footnote.
6+
7+
[^longnote]: Here's one with multiple blocks.
8+
.
9+
<p>Here is a footnote reference,<sup class="footnote-ref"><a href="/custom/path#fn1" id="fnref1">[1]</a></sup> and another.<sup class="footnote-ref"><a href="/custom/path#fn2" id="fnref2">[2]</a></sup></p>
10+
<hr class="footnotes-sep">
11+
<section class="footnotes">
12+
<ol class="footnotes-list">
13+
<li id="fn1" class="footnote-item"><p>Here is the footnote. <a href="/custom/path#fnref1" class="footnote-backref">↩</a></p>
14+
</li>
15+
<li id="fn2" class="footnote-item"><p>Here's one with multiple blocks. <a href="/custom/path#fnref2" class="footnote-backref">↩</a></p>
16+
</li>
17+
</ol>
18+
</section>
19+
.
20+
21+
22+
Inline footnote with custom href:
23+
.
24+
Here is an inline note.^[Inlines notes are easier to write.]
25+
.
26+
<p>Here is an inline note.<sup class="footnote-ref"><a href="/custom/path#fn1" id="fnref1">[1]</a></sup></p>
27+
<hr class="footnotes-sep">
28+
<section class="footnotes">
29+
<ol class="footnotes-list">
30+
<li id="fn1" class="footnote-item"><p>Inlines notes are easier to write. <a href="/custom/path#fnref1" class="footnote-backref">↩</a></p>
31+
</li>
32+
</ol>
33+
</section>
34+
.
35+
36+
37+
Multiple references with custom href:
38+
.
39+
[^1][^2][^3]
40+
41+
[^1]: foo
42+
[^2]: bar
43+
[^3]: baz
44+
.
45+
<p><sup class="footnote-ref"><a href="/custom/path#fn1" id="fnref1">[1]</a></sup><sup class="footnote-ref"><a href="/custom/path#fn2" id="fnref2">[2]</a></sup><sup class="footnote-ref"><a href="/custom/path#fn3" id="fnref3">[3]</a></sup></p>
46+
<hr class="footnotes-sep">
47+
<section class="footnotes">
48+
<ol class="footnotes-list">
49+
<li id="fn1" class="footnote-item"><p>foo <a href="/custom/path#fnref1" class="footnote-backref">↩</a></p>
50+
</li>
51+
<li id="fn2" class="footnote-item"><p>bar <a href="/custom/path#fnref2" class="footnote-backref">↩</a></p>
52+
</li>
53+
<li id="fn3" class="footnote-item"><p>baz <a href="/custom/path#fnref3" class="footnote-backref">↩</a></p>
54+
</li>
55+
</ol>
56+
</section>
57+
.

test/test.mjs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,17 @@ describe('custom docId in env', function () {
4242
// Now check that using `env.documentId` works to prefix IDs
4343
generate(fileURLToPath(new URL('fixtures/footnote-prefixed.txt', import.meta.url)), md, { docId: 'test-doc-id' })
4444
})
45+
46+
describe('custom href prefix in env', function () {
47+
const md = markdownit().use(footnote)
48+
49+
// Check that using `env.footnoteHrefPrefix` works to customize href
50+
generate(fileURLToPath(new URL('fixtures/footnote-custom-href.txt', import.meta.url)), md, { footnoteHrefPrefix: '/custom/path#' })
51+
})
52+
53+
describe('combined docId and custom href prefix in env', function () {
54+
const md = markdownit().use(footnote)
55+
56+
// Check that both env.docId and env.footnoteHrefPrefix work together
57+
generate(fileURLToPath(new URL('fixtures/footnote-combined.txt', import.meta.url)), md, { docId: 'my-doc', footnoteHrefPrefix: '/docs#' })
58+
})

0 commit comments

Comments
 (0)